People are always looking for that amazing breakthrough technology or process that solves all their problems—enhances their love life, trims their waist, and improves the productivity of their development team. That's why process manias like Agile and Six Sigma are so enticing. Just splat the Scrum tag on your development team and "bam!"—your team is suddenly ten times as prolific.
It would be hilarious if it weren't true in the minds of the narcissistic ninnies with an adiction to magic methodologies. I'm happy that people are willing to try something new. I just wish they wouldn't confuse new with necessary, or minor fixes with major problems.
The truth is that there's nothing magic about producing great software with high quality on time. (And no, software services aren't different in this regard.) Human beings have had large teams building complex engineering marvels for thousands of years. There is no magic. There is solid design and disciplined execution. Period.
When your code base is less than 100,000 lines, and you've got fewer than 15 people on the project, you don't need solid design and disciplined execution. You can wing it—use emergent design, have a loose upfront design bar, rewrite and refactor the code endlessly while the customer looks over your shoulder. When your code base and your project is bigger, it's solid design and disciplined execution or it's broken code, broken teams, and broken schedules.
I worked at Boeing for five years before coming to Microsoft. The areospace industry uses the terms nominals and tolerances to refer to acceptable values and tight refinements. A nominal might be a support beam being placed two feet from a heating duct. A tolerance would be that location give or take 1.5 inches.
There were always inexperienced engineers who concerned themselves with tolerances. They were constantly looking for a clever technique to give them an extra fraction of an inch of tolerance, as if that mattered. Real engineers who designed and built the airplanes knew these tolerance tinkerers were misguided, out-of-touch, naïve fools who missed the point entirely.
The key to building an airplane isn't tinkering the tolerances—it's nailing the nominals. It's not, "Does the steel stringer bend within 0.058 inches of the specified skin?" It's, "Does the steel stringer pass right through a cooling vent?" Stop worrying about the fancy details and get the basics right, ding-a-ling!
You might think I'm criticizing Boeing, but it's quite the opposite. When lives are in the balance, it's important to get the basics right—getting the passengers to their destination safely. If the stringer is a few millimeters off, you can shim it. If two key systems run through the same space, a minor accident can lead to a major catastrophe. Never let the details distract you from what customers truly care about.
The software industry suffers greatly from dingbats tinkering tolerances instead of nailing nominals. You can see it in most software, regardless of how or where it ships. The focus of the engineering was clearly on all kinds of fancy little features instead of getting the software to work right for basic cases.
Maybe the basic cases don't appeal to engineers. Maybe engineers can only relate to tinkerers, not to folks who want solutions that are simple and reliable. However, I don't think so.
I think nailing the nominals requires solid design and disciplined execution, both of which require software engineers to put the project and the customer ahead of their personal interests. Unfortunately, that's not what cowboy coders crave, which is why cowboy coders should either grow up or move out.
Still here? Still believe the customer and project should come first? Still realize that true customer value only comes when you act as a team, putting the customer at the center instead of yourselves? Good, let's talk about the nominals of software.
As I said, the nominals revolve around solid design and disciplined execution. But that's true for any significant engineering venture. What does it mean for software? I'll keep it simple.
Solid design means:
§ Understanding what the customer is trying to accomplish with your software (product planning, value proposition/vision).
§ Thinking through the end-to-end experience, including pitfalls (experience design, scenarios).
§ Decomposing that experience into distinct engineerable pieces (engineering design, architecture).
Disciplined execution means:
§ Ordering work based on priority and dependency.
§ Creating a schedule based on data from past projects.
§ Establishing and upholding completion criteria for every piece.
In the simplest of terms, the nominals are "think before you act" and "define done."
I've written about experience and engineering design many times before (particularly, in "The other side of quality (chapter 6)"), so this time let's drill down on defining done. For a distinct engineerable piece of the architecture, be that a feature, component, API, or Web service, what should the completion criteria be?
You could set bug limits or test code coverage, but those evaluate intermediate results. What do you care about at the end? What should the final result of an engineerable piece of the architecture be? Well, it should fully satisfy its part of the end-to-end scenario, including performance targets and avoiding pitfalls, and it should be secure and reliable.
What causes software to fail scenarios and reliablity targets? Smart dedicated people, at Microsoft and around the globe, have been studying this problem for decades. The net result, which you can read in every study ever done, is that problems stem from failing to complete only five practices—five practices that should define done:
§ Documented design. Think before you act and capture it so that peers can do a …
§ Design review, which catches up to half the errors before you write a line of code which you then …
§ Code review, that, if you use inspections, can catch more than 70% of errors, though some errors will only be caught by …
§ Code analysis, like lint or PREfast that catch patterns humans can miss, but doesn't entirely replace …
§ Unit and component testing, which catches the remaining issues, including stress, performance, and edge cases, as well as end-to-end scenario checks.
Without defining done, tasks never finish (sound familiar?). Even worse, cowboy coders can claim to be finished with no accountability for quality work. Ever wonder why great developers end up cleaning up after poor developers? It's because poor developers claim completion for crud. Without defining done, who else can keep their crappy code from customers?
Notice how none of the five practices that define done are terribly exotic or revolutionary? They are basic. Everyone knows you should do them. Yet engineers will fall over themselves to become certified Scrum Masters, create cool new tools, or learn the latest technology before they'll actually run design reviews or write comprehensive unit and component tests.
Nail the nominals before tinkering the tolerances. It's not magic. It's not complicated. It's not hard. It takes discipline. It takes putting the customer and the project before yourself. It takes being a professional instead of a chump. Think you can handle it?