software development
Tell Me About Yourself--Engineering Leader Edition
The following tweet starts an excellent thread of questions that I’m taking as a starting point for this post looking back over the past 5 years with my current company: twitter.com/lilykonin…
When was the last time you promoted someone on your team? How did it happen? My organization works in a way that promotion decisions are actually approved (or rejected) at a much higher level than mine. But I’ve advocated successfully for promotion for two of my direct reports, both during the pandemic.
The first was a recent college graduate who spent the 18 months of his professional career on my team. While I wasn’t his manager for the entirety of that time, I encouraged him to work on communication across various channels (Slack, email, documentation, pull request comments, etc). I did what I could to put opportunities in front of him to grow and showcase his skills. What he did on his own (in addition to pursuing a master’s degree in computer science on the side) was earn AWS certifications. He passed 4(!) in a single calendar year. So when it came time to year-end reviews, there were a lot of accomplishments to point to as well as positive feedback from people outside our team from their experiences of working with him. He was the first direct report I had who earned the highest possible year-end rating: exceptional, and the first promotion (to senior engineer). He’s still with the company today, and received another promotion (to principal engineer) in the same cycle I received a promotion to senior manager.
The second promotion was for someone who had been with the company longer than I had. From what I was told she had been submitted for promotion once or twice before but had not been selected for promotion. She was (and is) one of those engineers who leads much more by example than by talking. Having observed over the years that the review process tends to overindex on software engineers that present well, I became the person in meetings who consistently pushed people to consider written communication as well as presentations in judging the quality of an engineer’s communication. I also recommended she take the technical writing courses offered by Google. These steps, plus highlighting her numerous critical contributions to the team’s success during another year-end review cycle appear to have been enough to get her promoted to principal engineer.
Why did the last person in this role leave? It’s been long enough that I don’t actually recall why the previous leader of the team moved on. I presume they found an opportunity with another company.
How do you nurture psychological safety in your team? Regular one-on-ones (I follow a weekly cadence for these) has been important to nurturing psychological safety. Because I joined the team to lead it after work-from-home began, Zoom meetings were really the only avenue available to build the rapport necessary for my team to trust me. I also started a technical book club with the team, with the intention of giving my team exposure to software design and implementation principles outside the scope of our current work, along with providing opportunities for each member of the team to lead discussions and explore ideas. It seems to have had the additional benefit of building everyone’s comfort level with, and trust in, each other along with all the other things I’d intended it for (including ideas originating from book club showing up as production enhancements to our software).
When was the last time you supported a direct report’s growth, even if it meant leaving your team or company? In my previous department, I had staffing responsibilities for two teams for awhile: one composed entirely of contractors in addition to my own team. In helping a scrum master friend of mine diagnose the causes of the contractor team struggling to be productive, I concluded that the main issue wasn’t technical expertise but the lack of a leader to help remove impediments and connect them with others in the organization who could help their tasks move forward. I proposed this as a leadership opportunity for one of my direct reports and got buy-in from higher-level management. He was so successful in the stretch opportunity I created, he got promoted after leaving my team. Not long after that, he left our organization to join Amazon as an engineering team lead in Seattle. He’s currently a principal software engineering manager with Microsoft in Atlanta.
Can I speak to some women on the team to hear more about their experience? Two of the engineers on my current team are women. If all goes well, another one of them will be promoted to principal engineer by virtue of her performance over the past 18 months. While it will likely mean losing her to another team, her getting promoted and gaining new opportunities that my team’s scope doesn’t provide is more important to me. I see it as another opportunity to build up another engineer in her place.
Nulls Break Polymorphism, Revisited
Steve Smith wrote this post regarding the problem with null about two years ago. It’s definitely worth reading in full (as is pretty much anything Steve Smith writes). The post provided code for the implementation of an extension method and a change in the main code that would address null without throwing an exception. It also mentioned the null object design pattern but didn’t provide a code example.
I took the code from the original post and revised it to use the null object design pattern. Unlike the original example, my version of the Employee class overrides ToString() instead of using an extension method. There are almost certainly any number of other tweaks which could be made to the code which I may make later.
Smith’s post links additional material that’s also worth checking out:
Entity Framework Code First to a New Database (Revised Again)
As part of hunting for a new employer (an unfortunate necessity due to layoffs), I’ve been re-acquainting myself with the .NET stack after a couple of years building and managing teams of J2EE developers. MSDN has a handy article on Entity Framework Code First, but the last update was about a year ago and some of the information hasn’t aged so well.
The first 3 steps in the article went as planned (I’m using Visual Studio 2017 Community Edition). But once I got to step 4, neither of the suggested locations of the database worked per the instructions. A quick look in App.config revealed what I was missing:
Once I provided the following value for the server name:
(localhostdb)\mssqllocaldbdatabase I could connect to revealed themselves and I was able to inspect the schema. Steps 5-7 worked without modifications as well. My implementation of the sample diverged slightly from the original in that I refactored the five classes out of Program.cs into separate files. This didn't change how the program operated at all--it just made for a simpler Program.cs file. The code is available on GitHub.
Best Practices for Software Testing
I originally wrote the following as an internal corporate blog post to guide a pair of business analysts responsible for writing and unit testing business rules. The advice below applies pretty well to software testing in general.
80/20 Rule
80% of your test scenarios should cover failure cases, with the other 20% covering success cases. Too much of testing (unit testing or otherwise) seems to cover the happy path. A 4:1 ratio of failure case tests to success case tests will result in more durable software.Boundary/Range Testing
Given a range of valid values for an input, the following tests are strongly recommended:- Test of behavior at minimum value in range
- Test of behavior at maximum value in range
- Tests outside of valid value range
- Below minimum value
- Above maximum value
- Test of behavior within the range
Date/Time Testing
Above and beyond the boundary/range testing described above, the testing of dates creates a need to test how code handles different orderings of those values relative to each other. For example, if a method has a start and end date as inputs, you should test to make sure that the code responds with some sort of error if the start date is later than the end date. If a method has start and end times as inputs for the same day, the code should respond with an error if the start time is later than the end time. Testing of date or date/time-sensitive code must include an abstraction to represent current date and time as a value (or values) you choose, rather than the current system date and time. Otherwise, you'll have no way to test code that should only be executed years in the future.Boolean Testing
Given that a boolean value is either true or false, testing code that takes a boolean as an input seems quite simple. But if a method has multiple inputs that can be true or false, testing that the right behavior occurs for every possible combination of those values becomes less trivial. Combine that with the possibility of a null value, or multiple null values being provided (as described in the next section) and comprehensive testing of a method with boolean inputs becomes even harder.Null Testing
It is very important to test how a method behaves when it receives null values instead of valid data. The method under test should fail in graceful way instead of crashing or displaying cryptic error messages to the user.Arrange-Act-Assert
Arrange-Act-Assert is the organizing principle to follow when developing unit tests. Arrange refers to the work your test should do first in order to set up any necessary data, creation of supporting objects, etc. Act refers to executing the scenario you wish to test. Assert refers to verifying that the outcome you expect is the same as the actual outcome. A test should have just one assert. The rationale for this relates to the Single Responsibility Principle. That principles states that a class should have one, and only one, reason to change. As I apply that to testing, a unit test should test only one thing so that the reason for failure is clear if and when that happens as a result of subsequent code changes. This approach implies a large number of small, targeted tests, the majority of which should cover failure scenarios as indicated by the 80/20 Rule defined earlier.Test-First Development & Refactoring
This approach to development is best visually explained by this diagram. The key thing to understand is that a test that fails must be written before the code that makes the test pass. This approach ensures that test is good enough to catch any failures introduced by subsequent code changes. This approach applies not just to new development, but to refactoring as well. This means, if you plan to make a change that you know will result in broken tests, break the tests first. This way, when your changes are complete, the tests will be green again and you'll know your work is done. You can find an excellent blog post on the subject of test-driven development by Bob Martin here.Other Resources
I first learned about Arrange-Act-Assert for unit test organization from reading The Art of Unit Testing by Roy Osherove. He's on Twitter as @RoyOsherove. While it's not just about testing, Clean Code (by Bob Martin) is one of those books you should own and read regularly if you make your living writing software.Software Development Roles: Lead versus Manager
I’ve held the title of development lead and development manager at different points in my technology career. With the benefit of hindsight, one of the roles advertised and titled as the latter was actually the former. One key difference between the two roles boils down to how much of your time you spend writing code. If you spend half or more your time writing code, you’re a lead, even if your business cards have “manager” somewhere in the title. If you spend significantly less than half your time writing code, then the “manager” in your title is true to your role. When I compare my experience between the two organizations, the one that treats development lead and development manager as distinct roles with different responsibilities has been not only been a better work environment for me personally, but has been more successful at consistently delivering software that works as advertised.
A company can have any number of motivations for giving management responsibilities to lead developers. The organization may believe that a single person can be effective both in managing people and in delivering production code. They may have a corporate culture where only minimal amount of management is needed and developers are self-directed. Perhaps their implementation of a flat organizational structure means that developers take on multiple tasks beyond development (not uncommon in startup environments). If a reasonably-sized and established company gives lead and management responsibilities to an individual developer or developers however, it is also possible that there are budgetary motivations for that decision. The budgetary motivation doesn’t make a company bad (they’re in business to make money after all). It is a factor worth considering when deciding whether or not a company is good for you and your career goals.
Being a good lead developer is hard. In addition to consistently delivering high-quality code, you need to be a good example and mentor to less-senior developers. A good lead developer is a skilled troubleshooter (and guide to other team members in the resolution of technical problems). Depending on the organization, they may hold significant responsibility for application architecture. Being a good development manager is also hard. Beyond the reporting tasks that are part of every management role, they’re often responsible for removing any obstacles that are slowing or preventing the development team from doing work. They also structure work and assign it in a way that contributes to timely delivery of functionality. The best development managers play an active role in the professional growth of developers on their team, along with annual reviews. Placing the responsibility for these two challenging roles on a single person creates a role that is incredibly demanding and stressful. Unless you are superhuman, sooner or later your code quality, your effectiveness as a manager, or both will suffer. That outcome isn’t good for you, your direct reports, or the company you work for.
So, if you’re in the market for a new career opportunity, understand what you’re looking for. If a development lead position is what you want, scrutinize the job description. Ask the sort of questions that will make clear that a role being offered is truly a development lead position. If you desire a development management position, look at the job description. If hands-on development is half the role or more, it’s really a development lead position. If you’re indeed superhuman (or feel the experience is too valuable to pass up), go for it. Just be aware of the size of the challenge you’re taking on and the distinct possibility of burnout. If you’re already in a job that was advertised as a management position but is actually a lead position, learn to delegate. This will prove especially challenging if you’re a skilled enough developer to have landed a lead role, but allowing individual team members to take on larger roles in development will create the bandwidth you need to spend time on the management aspects of your job. Finally, if you’re an employer staffing up a new development team or re-organizing existing technology staff, ensure the job descriptions for development lead and development manager are separate. Whatever your software product, the end result will be better if you take this approach.
Which Programming Language(s) Should I Learn?
I had an interesting conversation with a friend of mine (a computer science professor) and one of his students last week. Beyond the basic which language(s) question were a couple more intriguing ones:
- If you had to do it all over again, would you still stick with the Microsoft platform for your entire development career?
- Will Microsoft be relevant in another ten years?
One thing that seemed apparent even in 1999 was that Java developers (the good ones anyway) had a great grasp of object-oriented design (the principles Michael Feathers would apply the acronym SOLID to). In addition, quite a number of open source and commercial software products were being built in Java. The same could not be said of C# until much later.
To the question of whether Microsoft will still be relevant in another ten years, I believe the answer is yes. With Satya Nadella at the helm, Microsoft seems to be doubling-down on their efforts to maintain and expand their foothold in the enterprise space. There are still tons of business of various sizes (not to mention state governments and the federal government) that view Microsoft as a familiar and safe choice both for COTS solutions and custom solutions. So I expect it to remain possible to have a long and productive career writing software with the Microsoft platform and tools.
As more and more software is written for the web (and mobile browsers), whatever “primary” language a developer chooses (whether Java, C#, or something else altogether), they would be wise to learn JavaScript in significant depth. One of the trends I noticed over the past couple of years of regularly attending .NET user groups, fewer and fewer of the talks had much to do with the intricacies and syntactic tricks of Microsoft-specific technologies like C# or LINQ. There would be talks about Bootstrap, Knockout.js, node.js, Angular, and JavaScript. Multiple presenters, including those who worked for Microsoft partners advocated quite effectively for us to learn these technologies in addition to what Microsoft put on the market in order to help us make the best, most flexible and responsive web applications we could. Even if you’re writing applications in PHP or Python, JavaScript and JavaScript frameworks are becoming a more significant part of the web every day.
One other language worth knowing is SQL. While NoSQL databases seem to have a lot of buzz these days, the reality is that there is tons of structured, relational data in companies and governments of every size. There are tons of applications that still remain to be written (not to mention the ones in active use and maintenance) that expose and manipulate data stored in Microsoft (or Sybase) SQL Server, Oracle, MySQL, and Postgresql. Many of the so-called business intelligence projects and products today have a SQL database as one of any number of data sources.
Perhaps the best advice about learning programming languages comes from The Pragmatic Programmer:
Learn at least one new language every year.One of a number of useful things about a good computer science program is that after teaching you fundamentals, they push you to apply those fundamentals in multiple programming languages over the course of a semester or a year. Finishing a computer science degree should not mean the end of striving to learn new languages. They give us different tools for solving similar problems--and that ultimately helps make our code better, regardless of what language we're writing it in.
Pseudo-random Sampling and .NET
One of the requirements I received for my current application was to select five percent of entities generated by another process for further review by an actual person. The requirement wasn’t quite a request for a simple random sample (since the process generates entities one at a time instead of in batches), so the code I had to write needed to give each entity generated a five percent chance of being selected for further review. In .NET, anything involving percentage chances means using the Random class in some way. Because the class doesn’t generate truly random numbers (it generates pseudo-random numbers), additional work is needed to make the outcomes more random.
The first part of my approach to making the outcomes more random was to simplify the five percent aspect of the requirement to a yes or no decision, where “yes” meant treat the entity normally and “no” meant select the entity for further review. I modeled this as a collection of 100 boolean values with 95 true and five false. I ended up using a for-loop to populate the boolean list with 95 true values. Another option I considered was using Enumerable.Repeat (described in great detail in this post), but apparently that operation is quite a bit slower. I could have used Enumerable.Range instead, and may investigate the possibility later to see what advantages or disadvantages there are in performance and code clarity.
Having created the list of decisions, I needed to randomize their order. To accomplish this, I used LINQ to sort the list by the value of newly-generated GUIDs:
decisions.OrderBy(d => Guid.NewGuid()) //decisions is a list of bool
With a randomly-ordered list of decisions, the final step was to select a decision from a random location in the list. For that, I turned to a Jon Skeet post that provided a provided a helper class (see the end of that post) for retrieving a thread-safe instance of Random to use for generating a pseudo-random value within the range of possible decisions. The resulting code is as follows:
return decisions.OrderBy(d => Guid.NewGuid()).ToArray()[RandomProvider.GetThreadRandom().Next(100)]; //decisions is a list of bool
I used LINQPad to test my code and over multiple executions, I got between 3 and 6 “no” results.
RadioButtonListFor and jQuery
One requirement I received for a recent ASP.NET MVC form implementation was that particular radio buttons be checked on the basis of other radio buttons being checked. Because it’s a relatively simple form, I opted to fulfill the requirement with just jQuery instead of adding knockout.js as a dependency.
Our HTML helper for radio button lists is not much different than this one. So the first task was to identify whether or not the radio button checked was the one that should trigger another action. As has always been the case when grouping radio buttons in HTML, each radio button in the group shares the same name and differs by id and value. The HTML looks kind of like this:
@Html.RadioButtonListFor(m => m.Choice.Id, Model.Choice.Id, Model.ChoiceListItems)
where ChoiceListItems is a list of System.Web.Mvc.SelectListItem and the ids are strings. The jQuery to see if a radio button in the group has been checked looks like this:
$(“input[name=‘Choice.Id’]").change(function(){
…
}
Having determined that a radio button in the group has been checked, we must be more specific and see if the checked radio button is the one that should trigger additional action. To accomplish this, the code snippet above is changed to the following:
$(“input[name=‘Choice.Id’]").change(function(){
if($(“input[name=‘Choice.Id’]:checked”).val() == ‘@Model.SpecialChoiceId’){
…
}
}
The SpecialChoiceId value is retrieved from the database. It’s one of the values used when building the ChoiceListItems collection mentioned earlier (so we know a match is possible). Now the only task that remains is to check the appropriate radio button in the second grouping. I used jQuery’s multiple attribute selector for this task. Here’s the code:
$(“input[name=‘Choice.Id’]").change(function(){
if($(“input[name=‘Choice.Id’]:checked”).val() == ‘@Model.SpecialChoiceId’){
$(“input[name=‘Choice2.Id’][value='@Model.Choice2TriggerId']").prop(‘checked’,true);
}
}
The first attribute filter selects the second radio button group, the second attribute filter selects the specific radio button, and prop(‘checked’,true) adds the ‘checked’ attribute to the HTML. Like SpecialChoiceId, Choice2TriggerId is retrieved from the database (RavenDB in our specific case).
Complex Object Model Binding in ASP.NET MVC
In the weeks since my last post, I’ve been doing more client-side work and re-acquainting myself with ASP.NET MVC model binding. The default model binder in ASP.NET MVC works extremely well. In the applications I’ve worked on over the past 2 1/2 years, there have been maybe a couple of instances where the default model binder didn’t work the way I needed. The problems I’ve encountered with model binding lately have had more to do with read-only scenarios where certain data still needs to be posted back. In the Razor template, I’ll have something like the following:
@Html.LabelFor(m => m.Entity.Person, "Person: ") @Html.DisplayFor(m => m.Entity.Person.Name) @Html.HiddenFor(m => m.Entity.Person.Name)
Nothing is wrong with the approach above if Name is a primitive (i.e. string). But if I forgot that Name was a complex type (as I did on one occasion), the end result was that no name was persisted to our datastore (RavenDB) which meant that there was no data to bring back when the entity was retrieved. The correct approach for leveraging the default model binder in such cases is this:
@Html.LabelFor(m => m.Entity.Person, "Person: ") @Html.DisplayFor(m => m.Entity.Person.Name) @Html.HiddenFor(m => m.Entity.Person.Name.FirstName) @Html.HiddenFor(m => m.Entity.Person.Name.LastName) @Html.HiddenFor(m => m.Entity.Person.Name.Id)
Since FirstName, LastName and Id are all primitives, the default model binder handles them appropriately and the data is persisted on postback.
XUnit: Beyond the Fact Attribute (Part 2)
One thing I initially missed about NUnit compared to XUnit (besides built-in support for it in tools like TeamCity) is attributes like SetUp and TestFixtureSetUp that enable you to decorate a method with variables that need to be set (or any other logic that needs to run) before each test or before all the tests in a test fixture. When I first adopted test-driven development as a work practice, I felt it made things easier. But the authors of NUnit eventually came to a different conclusion about those attributes, and implemented XUnit without those attributes as a result.
Rather than define attributes for per-test and per-fixture setup, the authors of XUnit recommend using a no-arg constructor where you’d use SetUp and IUseFixture where you’d use TestFixtureSetUp or TestFixtureTearDown. While this took me some time to get used to, leveraging the interface made it easier to handle the external dependencies of code I needed to implement and test. One technique I’ve adopted to give myself additional flexibility in my test implementations is to add an extension point to the implementation of the SetFixture method
In this example, the extension point is a method named AdditionalFixtureConfiguration. Calling it inside SetFixture ensures it will be called before each test class derived from UnitTestBase. Making the method virtual and keeping it free of implementation means that I only need to override it if I need additional pre-test setup for particular test scenarios. Because we use StructureMap as our IOC container, the equivalent of UnitTestFixture class in my example has a public attribute of type StructureMap.IContainer. The AdditionalFixtureConfiguration method provides a natural home for any setup code needed to configure additional mappings between interfaces and implementations, set up mocks, and even inject mocks into the container if a concrete implementation isn’t available (or isn’t needed).
While this is the implementation I’ve chosen, there are certainly other ways to accomplish the same thing. Instead of defining AdditionalFixtureConfiguration in UnitTestBase, I could define it in UnitTestFixture instead and call it in every SetFixture implementation (or not , if that customization wasn’t needed). I prefer having the abstract class because it makes for simpler actual test code.
Everyone is Junior at Something--Even You
Hanselminutes #427 was an excellent interview with Jonathan Barronville, the author (perhaps the most intelligent and articulate 19-year-old I've ever heard) of this article on Medium. The discussion covered a lot of ground, and posed a number of thought-provoking questions. Three of the questions struck me as especially important.
What is senior?
In the podcast, Hanselman suggested three criteria: years in industry, years writing code in a language, and age. Since I’ve been in the industry for 18 years, have been writing production C# code for about 10 of them, and turned 40 at the beginning of the year, those criteria argue in favor of me being considered senior. Those numbers can also work against me in a way (and not just because of the field’s well-known problems with age discrimination). Before I took my current job (over 2 years ago), I hadn’t written any production ASP.NET MVC, jQuery or knockout.js. The last time I’d written any production JavaScript before then was before also jQuery and node.js even existed. So from the perspective of those technologies, I was junior (and still am in some respects).
While industry today seems to have a fetish for young developers, there is merit to that interest in one respect. Men and women entering the industry right now, whether they’re fresh out of college (or even younger) are too young to have any memory of a world where Google didn’t exist. They’re too young to remember a world before web pages. Some of them have been writing software for the majority of their lives. It’s natural to them in a way it wasn’t for me because I had to go to college to get access to really good computers and high-speed Internet.
That said, the number of years (or lack of them) isn’t an advantage or disadvantage if you haven’t had the sort of experiences you can grow from as a developer (and learned the right lessons from them). Regardless of what age you were when you had the experiences, if you’ve had to build software that solved enterprise-level problems, dealt with scaling, refactoring and enhancement of large systems, or integration of systems, both succeeding and failing at addressing those challenges are what really make a senior developer. More time in industry may give someone more opportunities to have those experiences, but if they haven’t had them, they’ve just been writing software for a long time.
What is the rush to be senior?
Hanselman made a comparison between tradesmen like carpenters and plumbers (who have to work as apprentices for 3-5 years and pass an exam before they can become journeymen) and our industry, where someone can have senior in their title without much experience. While some of it (if not most of it) has to do with pay, there are drawbacks. Because our field is relatively young in the grand scheme of things, there aren’t universally accepted standards and practices (especially compared to some branches of engineering, which have hundreds of years of history). We place too much of a premium on speed, and not enough on depth of experience (and the time it takes to earn it). One of the end results of this is the sort of interviews I’ve experienced on a regular basis. I’ve seen tons of resumes from people with senior titles who are stymied by interview exercises that ask fairly basic questions (on the level of the Fizz Buzz test).
I’d been working for less than four years when I first got “senior” added to my title. It came with a nice raise (which I was certainly happy about) and more responsibilities (team leadership), but I certainly wasn’t senior in terms of software development experience after that short a period of time. Not unlike what the classic Peter Norvig essay suggests about teaching yourself programming in ten years, that’s about how long it took for me to see myself as legitimately senior from an experience perspective. Even now, having spent over 17 years in industry, I’m sure there are workplaces where I wouldn’t be considered senior because I haven’t architected super-large systems or led a team with dozens of people—and I’m alright with that. I’ve matured enough after this amount of time to be more concerned with what kind of work I’m doing (and what I’m learning) than I am with what title an employer gives me.
Are we okay with not knowing something and then learning?
This question points in two directions:
- are we okay with ourselves not knowing something and then having to learn it?
- are we okay with others not knowing something and then having to learn it?
For me, the answer to the first question is yes. In the case jQuery and knockout.js (and other unfamiliar technologies like RavenDB), I had to be okay with not knowing. Doing my own research, and not being too proud to ask a lot of questions younger developers on the team who clearly had more experience with those technologies was necessary to advance to the point where I could do all that work myself.
The answer to the second question is the key to many of the problems with our industry, particularly when it comes to issues of gender and diversity. Too many in our industry go beyond not being okay with someone not knowing something and cross the line to being condescending, rude, and even hostile. I’ve been on the receiving end of that kind of treatment more often than I care to remember. Too many workplaces allow people with superior technical skills to act like children instead of adults in interacting with their co-workers. There is more and more being written about the sexism in the industry (pieces like this one, and this one), but not nearly enough on the negative impact that environment has on the ability and desires of others to learn and grow as professionals. I think the persistently low numbers of minorities and women in the tech industry has as much to do with the perception (if not reality) that a lot of tech companies have high “a**hole thresholds” as it does with insufficient exposure to math and science in school.
The bottom line for me from the article and the podcast is not only that everyone in this industry starts out as junior level, but that technology changes so quickly that we will all be junior at something at multiple points throughout our careers in the tech industry. We need to keep that knowledge in mind so that we can be more patient with ourselves as we learn and with those we work with as they learn.
XUnit: Beyond the Fact Attribute
After using XUnit for unit testing the past couple of years, I finally got a bit tired of the cut-and-paste-and-modify cycle of expanding test coverage for functionality I was building. I've used "row test" functionality in NUnit and MbUnit in the past, but hadn't gotten around to finding out how to use them in XUnit until quite recently. The process I followed was relatively short: (1) Add the xunit.extensions dependency to my existing test assembly, (2) take a test that class that contained significant duplication and re-factor it to leverage one of the row testing attributes available.
In my case, the area I picked that would benefit from row testing was workflow testing. We implement our workflows as state machines with transition tables that define a starting state, an ending state, and the event that triggers the state change. I'd previously tested these workflows with one unit test per transition, with those three attributes (start state, end state, event) being the only thing different between them. This meant a single test that took these attributes as parameters should be sufficient to test transitions, and that the row testing attribute(s) would contain the actual values.
The Theory attribute supports a number of different data sources, including inline data, Excel data, and SQL Server data. I started with inline data believing it would be the simplest. But I wasn't able to get that to work because the InlineData attribute of Theory only appears to support primitives (int, bool, string, etc). Our workflow events and statuses are classes with attributes that are static and read-only. From there, I moved on to try the PropertyData attribute. This attribute takes one string argument that needs to match the name of a property added to the test class that returns a collection of object arrays with one set of test parameters per entry.
Following that approach worked much better. The results display pretty nicely in ReSharper as well:
I'm still working on a code example of this to make available on GitHub, but here are a few links I found useful in getting up to speed with the additional capabilities of XUnit:
Learning New Programming Languages
Important advice from The Pragmatic Programmer (page 62):
"Learn at least one new language every year."It's advice I've been trying to follow more seriously since I first started reading the book last month. One site I've been using to learn more JavaScript that's proven to be pretty cool for that is codewars.com (thanks Dean). The katas are small enough that it doesn't take a ton of research to figure out how to do something in a language you're learning. Once you've developed a working solution, you can see how others have solved it (and compare your solution to theirs). Since you write and test the katas in the browser, there's none of the overhead of firing up an editor or uploading your solution somewhere. Ideally I'd be writing a few katas per day, but a few katas a week are what I've been able to manage so far.
Since Apple introduced yet another language (Swift) at WWDC earlier this week, I’m starting to learn that language as well. So far, the syntax is a lot easier to grasp than Objective-C. The only real hassle with writing the code examples as I read the language guide is that XCode 6 Beta crashes every half hour.
With both languages (or any language really), the real leap forward comes from building something non-trivial with them. Figuring out what that non-trivial something will be is another challenge altogether. I wish there were a sites like codewars.com (or Project Euler) that put out larger-scale problems intended to be solved with software. Being part of the developer interview loop at work pushed me to create a few problems of that sort for use in interviewing developer candidates, but none of those exercises require more than 20 minutes of work. More significant challenges should make it useful to explore features beyond basic control flow and data structures.
When Third-Party Dependencies Attack
Last week provided our office with an inconvenient lesson in what can happen when third-party dependencies break in unanticipated ways. PostSharp is a key third-party dependency in the line of business web application we sell. On the morning of May 20, our continuous integration server (we use TeamCity) began indicating a build failure with the following message:
The changed file was a Razor template file--nothing at all to do with PostSharp. Only one person on our development team was experiencing this error on their local machine, but the end result--not being able to compile the solution locally--pretty much eliminated the possibility of that person being productive for the rest of the day. As the day progressed, the CI server began showing exactly the same error in other branches--even with no changes to code. It wasn't until the next day that we received the explanation (and a resolution).
- PostSharp.3.1.34\tools\PostSharp.targets(313, 5): error MSB6006: "postsharp.4.0-x86.exe" exited with code -199.
Reading the entire explanation is worthwhile, but the key reason for the failure is this:
"we ... assumed that all failures would be in the form of a managed exceptions. We did not anticipate that the library would terminate the process."The fail-safe code that PostSharp implemented around a third-party licensing component assumed all failures would be managed exceptions (which they could catch and deal with accordingly). Instead, this third-party component simply terminated the process. The end result--any of their customers using the latest version of PostSharp couldn't compile any solution that included it. There's no way of knowing for sure how many hours of productivity (and money) was lost as a result of this component, but the amounts were probably significant. To his credit, the CEO apologized, his development team removed the offending dependency and sacrificed the feature which depended on it.
There are many lessons to be learned (or re-learned) from what we experienced with PostSharp, but I’ll talk about three. First, if a third-party dependency is critical to your application and has a licensing option that includes support, it is best to pay the money so that you have recourse if and when there’s an issue. On the Microsoft stack, this is proving increasing costly as more third-party .NET libraries and tools raise their prices (there are quite a few formerly free .NET tools that have been purchased by companies and re-released as rather costly closed-source software).
Second, whether or not there are licensing costs, it’s a good idea to have more than one option for critical third-party dependencies. In the case of aspect-oriented programming on .NET, there are a number of alternatives to PostSharp. The vendor is even confident enough to list them on their website. So if licensing costs are significant enough a concern, it may be better to choose an open-source option that is less-convenient but gives you the ability to customize it than a paid option which doesn’t (and yokes you to a specific vendor).
Third, It may make sense to avoid taking on a third-party dependency altogether. When it comes to the Microsoft stack, it’s likely that they offer a framework or API with at least some of the capabilities you need for your solution. In the case of AOP, Microsoft offers Unity to support those capabilities. Especially in the case where you’re only considering the free tier of capabilities for a third-party dependency where Microsoft offers a product, if that free tier functionality isn’t a significant improvement, it may be best to stick with the Microsoft option.
Code Generation with LINQPad 4
Today I encountered a task at work that offered the prospect of some pretty dull development work–code that needed to be written that was almost the same in multiple cases (but not quite). It seemed like work that could benefit from the use of T4 templates, but quickly became frustrated by the process of setting up and debugging a template. The interleaving of angle bracket markup with code was never fun in XML, and T4 templates began to resemble that very quickly.
So after abandoning the T4 template approach, I fired up LINQPad to see if I could accomplish my goal in that. As it turned out, writing a small C# program in LINQPad for code generation was a lot easier. I just needed to remember two key things about string substitution in verbatim string literals. Here they are:
- Curly brackets need to be escaped. So "{" should be "{{" and "}" should be "}}". Not doing this will result in a FormatException.
- Double quotes need to be escaped. So " should be "".
I’ve made a code sample available as GitHub gist here. So far, I’ve used this technique to generate nearly 20 files in a fraction of the time it would have taken to write them manually. Very little manual tweaking of the files was needed after generation, which left more time to test the generated code in real scenarios.
How to Debug Small Programs
This brilliant post by Eric Lippert is one I wish I’d had available to point to last month when I spoke to some software engineering undergrads.
Advice for Aspiring Software Engineers
Last night, I gave a talk at Towson University to a section of software engineering students (a friend of mine is their professor). The goal of the talk was to share advice and experiences that would help them in their careers. What follows is the notes I spoke from, with revised and extended remarks (indicated by [extra]).
Overview
- Intro/bio
- Internship(s)
- Interviews(s)
- Work
- Beyond Work
- Keep in touch
- Keep learning
- Keep your skills sharp
Imagine being paid to test drive a car for 12 weeks. That’s what an internship is—the only opportunity you may have in your working life to take a job and a company for a 12-week test drive. You’ll get to see if you like the day-to-day of your particular role. You’ll get to see if you like your boss, your co-workers, the culture of the organization. When you are choosing your first real job, they can be a very useful filtering mechanism to determine which companies you should consider seriously and which ones you should skip. If an internship works out really well, that company could give you your first full-time job.
If at all possible, don’t graduate without one. A successful internship puts you ahead of recent college graduates without one because you have proof that you can do real work.
Internships are also a great opportunity to begin to build networks beyond your classmates. Even if you’re a natural introvert, make an effort to be social. You never know when a friendship might result in a reference, an interview, or a job—for you, or a friend you make.
I was an intern at The Washington Post the summer before I graduated from Maryland. Halfway through the summer, they asked if I could return in the fall as a part-time employee and I accepted. At the end of the year, the Post made an full-time offer for a help desk position. I got better offers and took one of those instead, but I have no doubt it would have been much harder without that internship and part-time experience.
[extra] I said during the talk that all other factors being equal, the candidate with an internship wins over the candidate without one. To be more complete, I should have said that a candidate with a lower GPA and an internship can and does beat a candidate with a higher GPA and no internship. My undergraduate GPA was just shy of 3.0. But because I had a successful internship, 4 months of part-time work experience at the same company, and a great reference from my boss, I had multiple offers before graduation.
Interviews
When it comes to interviews, expect to be asked about anything and everything on your resume. So if you can’t back it up in an interview, don’t put it on your resume. I interview people for jobs on a regular basis, and always ask questions to find out if they really know all the things their resume claims. If you have an internship under your belt, you’ll have real work experience to put on that resume.
Also expect to write code on the spot for a problem you’ll have had no advance notice of. If you’re not asked to write it on a whiteboard, the interviewer may just put a laptop in front of you with an editor and wait for you to write it. In interview situations like these, it’s very important that you verbalize what you’re thinking as you develop your solution to the problem. If you stare at the whiteboard or the laptop for a long time without saying anything, the interviewer might think you’re stumped—even if that isn’t the case. If you aren’t sure about some aspect of the question, it’s better to ask for clarification and be sure than to assume and be wrong. I ask a simple LINQ problem in every interview—and nine times out of ten, the candidate gives the wrong answer because they assumed they knew what I was asking for instead of making sure.
[extra] The following is a version of the LINQ question I ask in interviews: Given the following: var listOfNumbers = new List<int>{3,6,9,12,15,18,21,24,27,30,33,36};
write the LINQ statement that returns every number in the list divisible by 12 and by 6.
Software engineering involves a lot of collaboration. In my role, in order to develop a single feature or piece of functionality, there could be a handful of other people involved:
- business analyst
- other developers
- project manager
- program manager
- tester
- Regularly updating your teammates and bosses on your progress
- Asking for help if you're stuck
- Informing business analysts about missing, incomplete or unclear requirements
- Informing project managers if you're going to miss a deadline
- Writing estimates of how long a task will take
We’d all love to work on brand-new software, but most of us won’t get the chance. It is highly likely that the first work you do will be maintenance of and/or enhancements to an existing system. So developing an ability to find bugs in software (whether you originally wrote it or not) will be important to your success as a software engineer.
Beyond Work
Keep in touch, keep learning, keep your skills sharp (and add new ones). That’s a basic summary of the steps you need to take to go further in your career as a software engineer.
It is impossible to overestimate the importance of networking to your career (and I don’t just mean having a huge number of Facebook friends, Twitter followers, or LinkedIn connections). By networking I mean staying in regular touch with people you’ve worked with in the past or know from school that you’ve enjoyed working with (and would like to work with again). If you’re in the same geographical area, catch up with them in person once a year if you can manage it. If you don’t live in the same area, go beyond a word or two on Facebook. Don’t be the guy or girl who only checks in when they need something.
Having a strong one will not only bring you a wealth of useful advice and support when you need it, it will help you find work too. A couple of examples from my own experience: a. I worked with a guy named Dave at USWeb. We worked together for a couple years before I left and the company folded. We kept in touch off and on over the years, but hadn’t had regular contact in awhile. Over 12 years after we actually worked together, he gets in touch because he’s looking for a job and trying to decide between two companies—the one where I currently work, and one I worked for previously. I tell him my current company is better (not just trying to be a good company man, my former employer was terribly dysfunctional) and write up a referral for him. He gets hired and over a year later he’s still having a great time. b. Two other guys I worked with at USWeb—Murali and Allen. One was director of technology, the other a senior developer. I kept in fairly regular touch with Allen. So after almost four years at Ciena, I’m looking for a change. Murali, our former boss, is program manager at a local consulting firm and wants to surround himself with good people from previous gigs. He contacts Allen first, but Allen likes where he is and isn’t looking to make a change. But Allen gives Murali my contact information. I interview, I get hired, I work there a couple of years before moving on to a management role at a different company.
Don’t pass up opportunities to expand your networks beyond classmates and people you’ve worked with in the past. This area has plenty of software user groups. They’ll have stand-alone website or be on meet up.com. Join one. Get to know people that regularly attend. One of my current co-workers is someone I first met at a .NET user group in Rockville (RockNUG). A year-and-a-half later, when he got tired of commuting from Maryland to Virginia to work, we talked after a user group meeting about openings at my company. He’s been there six months now and is doing great work for us.
Figure out how you learn best. Some of us learn best through reading. Some through lecture. Some through examples. Figure out which one of these you are and spend regular time outside of work learning however works best for you.
Conferences and user groups provide great opportunities to network and learn. Smart companies use them to raise their profile and to recruit new talent. The companies I consider the best ones in my career are the ones that sent at least some of their employees to a conference or training every year. Even when I haven’t worked for companies that pay for employees to get training, I believe it’s important enough that I’ve paid my own way on more than one occasion for a particular conference. I went to SXSW Interactive last year and met up with former co-workers I hadn’t seen in 10 years. I met people from all over the world, as well as a few people who were literally across the parking lot from me. Seriously! I met a couple of programmers and a designer who work at a small firm in a building less than 100 yards away, but we had to fly over 1500 miles to meet.
Blogs, podcasts and screen-casts provide great learning opportunities as well. However you commute between home, school and work, it’s time you could spend listening to one of the many podcasts available on technologies or software development.
Starting a blog is a great way to practice and improve your written communication. I first started back in 2003, and my only purpose was to have a way to remember the process I went through to solve particular problems that I could access from wherever I wanted. On occasion, I would get a comment or two from others on the web that helped me solve a problem I’d posted about or improved on a solution I’d come up with. Sometimes a post has been useful to other developers than myself. I’ve even gotten requests for help in the comments. Once you have a blog, it also makes a handy place to publish a linkable copy of your resume. That’s a piece of advice your professor gave me many years ago, and now I have plenty of visibility to companies (and recruiters) without really having to bother with job boards.
Giving a talk is a great way to practice and improve your verbal communication. User groups are always looking for people to come and speak, and if there’s a technology that you know well (or want to know well), having to give a talk about it in front of an audience is one way to give yourself an incentive to research it and learn it well enough to be able to answer questions about it. I try to give a few talks every year to one of the user groups I attend in Rockville.
[extra] Starting a podcast is another option you might consider. I listen regularly to a few podcasts by veteran software developers (Hanselminutes, .NET Rocks, etc) and learn a lot that way. But I think we would also benefit from hearing software engineers who are relatively new to the work world, or even from interns. In addition to creating opportunities to hone both your writing and speaking skills, you’ll get to learn about audio and production.
With the amount of time you’ll spend working (and otherwise living your life), one of the habits that I’ve personally found the most difficult to maintain is writing code regularly that isn’t specifically for my job. One of the benefits of school, especially in a computer science program, is courses where you have to learn multiple programming languages and produce working solutions in a relatively short period of time. The diversity of languages is a great way to force yourself to think differently about how to solve a particular problem. To the extent you make it a habit to learn a different programming language every year or two, the better your skills in your main programming language will become.
Music, martial arts, sports and medicine are endeavors that have practice built into them. Software engineering doesn’t. We usually treat our work as our practice. You will improve more quickly as a developer if you practice designing and building software separately from the work you do everyday.
Practice! There are plenty of sites out there with different code katas (small problems that you write code to solve). Larger, longer-term side projects are also a great idea—especially if they’re tied to a hobby or something else that interests you outside of your job. In addition to these, the web is full of open source projects that could use contributors (github, codeplex, sourceforge, etc). Try to contribute however you can—even if it’s documentation—and you’ll benefit from the experience.
[extra] When it comes to practice in improving your software engineering skills, it works best if you consistently increase the level of difficulty. John Sonmez explains why here. He also links to an incredible example by Jennifer Dewalt. Programming contests (including things like TopCoder) may be worth your consideration.
String concatentation
Yesterday, Bill Wagner (author of a number of excellent books on C#) wrote a post on string concatenation, string.Format, and StringBuilder. I found it a useful update to what I previously understood. I’d written a brief post on string.Format 7 (!) years ago, and at that time I thought string.Concat (a.k.a. “blah blah blah” + “blah blah blah”) shouldn’t ever be used because of the immutability of strings meant a new string was created for each concatenation operation. Wagner touches on what operations create objects that will need to be garbage collected, as well as providing an example of when string.Concat is actually ok.
(h/t The Morning Brew)
Re-Introducing NuGet (and introducing Chocolatey)
Last month, I presented on the topics of NuGet and Chocolatey at RockNUG as the lead-in to David Makogon’s Polyglot Persistence talk. Since the time I first gave a presentation on NuGet at a previous employer a couple years ago, the package manager has matured quite a bit. Because there was far more than 30 minutes worth of material to discuss, the rest of this post will cover material I didn’t get to, commentary from the audience, and the answer to a question about tags.
In discussing the term package manager, I indicated that it meant more than one thing:
- automation of dependency management for operating systems (think Unix or Linux distributions)
- automation of dependency management for programming languages (think Perl's CPAN, Ruby Gems, Node.js npm)
NuGet enables us as developers to define and re-define what a third-party dependency is. The team at Fortigent (one of RockNUG’s sponsors) has made packages out of some of the functionality they’ve developed internally.
There are a couple of different ways to create packages:
- Package Explorer GUI
- Nuget.exe command-line tool
In addition to creating packages, NuGet gives us the ability to set up our own package feeds. The feed can be a simple as a network share with packages in it. One step up from that is to create an empty ASP.NET Web application and add NuGet.Server to it. This will add everything to the application needed to host your own packages (or others from third parties). You can even publish your packages to this type of application if you wish. The pinnacle of NuGet package distribution is to host your own fork of the NuGet Gallery (available on GitHub). One software vendor, JetBrains, forked the NuGet Gallery to publish documentation on all the plug-ins available for the latest version of ReSharper as well as make it possible to download ReSharper itself. Chocolatey uses the NuGet Gallery code in a similar way. Unlike the ReSharper gallery (which doesn’t let you download plugins), the Chocolatey gallery does allow it (though the actual installs require command-line interaction, which is helpfully displayed next to each package).
One of the NuGet-related projects I found particularly interesting is concierge.nuget.org. Its objective is to recommend NuGet packages in the same way we receive movie, music and product recommendations from Netflix, Spotify or Amazon. Simply upload the packages.config file for your project and get recommendations back. I learned about this (and other .NET development-related topics) on The Morning Brew.
Q & A
While there weren’t any questions at the end, there was one asked during the presentation about the “tags” element of the nuspec file inside each package. When you look at a package in the NuGet Gallery (like EntityFramework for example), you see a list of linkable tags. Clicking on one actually triggers a search for each package that shares a particular tag. So if you’re a package author who wants their package to be discovered more easily, putting the right keywords in the “tags” element will help.