As I said in Nailing the nominals, the two keys to successful big projects (100K+ LOC) are thinking ahead and defining done. Thinking ahead is about design and planning. Defining done is about setting a quality bar and sticking to it. Yet many big projects go astray even when people think ahead and define done. Why?
Often failure is due to poor executive decisions that place their own agendas above shipping. (Given you hit your quality bar, shipping is better—much better.) However, an even more frequent form of failure comes from engineering teams over thinking and over generalizing—trying to solve world hunger instead of feeding the kids in front of you.
Once you’ve hit your quality bar, why is shipping always better than messing around with the code? After all, there may be competitive features an executive really wants added at the last minute. How bad can that be? Oh, it can be really bad.
Before you ship, everything is a guess—postponed bug fixes, key features, and design decisions are all guesses until you ship. Once you ship you learn the truth. Putting off the truth in favor of more guessing is never profitable. It upsets customers and partners, and the continuing chaos corrupts the code.
It’s so tempting, so intellectually pleasing, and so self-serving to examine the customer problem you’re solving and see the bigger problem, the more general problem, the problem you can solve for all time, all peoples, and all nations. Oh spare me, and spare your customers. Cultivating the green fields of broad ideas is not only self-serving, it’s a recipe for feature-rich and value-poor products and services your customers will use begrudgingly, despise utterly, and abandon gleefully at the first opportunity. Green fields are full of maggots.
Yet, I can’t walk down a hallway without hearing people trying to come up with ideas, algorithms, or class structures that “will work for this problem, and all problems like it.” Evil. Evil! Warning! Watch out. This line of thinking is EVIL!
What’s so evil about general solutions? After all, your code could be both a floor wax and a dessert topping. There are three primary pitfalls:
§ You rarely work through the full general solution in one ship cycle. The unfinished framework isn’t quite right, but you’ve shipped it. Now you are stuck with an unworkable legacy code base—forever.
§ You introduce a broader test surface and a broader security attack surface. Neither is desirable.
§ You put the problem at the center instead of the customer. When the customer isn’t at the center, your code loses its soul. It goes from being astounding to being adequate, from marvelous to mediocre.
Why is the unfinished general framework not quite right? Because it’s impossible to anticipate everything you and your customer need. After all, you are foolishly solving a general problem, instead of wisely solving a specific problem that you have a chance to iterate toward and get right.
You saved me from this fanatic
Before we break down the three primary pitfalls of general solutions, I need to calm down my agile zealot readers. Because agile methods put a premium on working software and customer collaboration, they tend to avoid the over-thinking trap. In particular, test-driven development and emergent design deliberately focus on keeping solutions as simple as possible and laser-targeted at customer requirements.
Because agile methods avoid the pitfalls of general solutions and general thinking, many agile zealots believe all big design up-front is vile. They are wrong. Sure, the regular refactoring and rework needed for emergent design isn’t a problem for small code bases. However, when you get above 100,000 lines of code serious rework becomes extraordinarily expensive. That’s why customer-focused, up-front architecture is essential for large projects.
This was researched by Dr. Barry Boehm, et al, in May 2004. They found that rework costs more than up-front design on projects larger than 100 KLOC. The larger the project, the more up-front design saves you.
The good news is that many agile methods like Scrum, continuous integration, and test-driven development (TDD) work well within a large project with a customer-focused architecture. These great techniques can keep a team locked onto the customer, instead of straying off into the green fields of self-gratifying intellectual exercises in futility.
TDD is an Extreme Programming (XP) and agile technique for implementation design that produces tight, robust code. As a side benefit, it provides unit tests with great code coverage. The process is fairly simple:
1. Write a new unit test for a requirement of the API or class.
2. Compile and build your program, then run the unit test and ensure that it fails.
3. Write just enough code to make the new unit test pass (and the old ones).
4. Refactor the code as needed to remove duplication.
5. Repeat until all API or class requirements are tested.
Who will save your soul?
Okay, back to the three primary pitfalls of general solutions— incomplete, unworkable legacy code, expanded test and attack surface, and losing your soul.
Say you are developing a big product like Halo or SharePoint. In either case, you can start with a general framework or you can start with a customer story.
The green fields approach is to start with a general framework. The framework supports all forms of weapons, landscapes, and content viewers. Putting together a game or a Web site is simply a matter of choosing the desired combination of weapons, landscapes, and content viewers. Bingo! You’ve got yourself a cruddy game or Web site.
The game or Web site has no soul because you focused on the framework instead of staying with the story. In addition, to test the framework you’ve got to consider every combination of weapon, landscape, and content viewer—each of which also presents an attack vector for hackers. And, should you be foolish enough to expose the framework as an “extensible interface,” all these problems become orders of magnitude more diabolical.
You should still have frameworks to help organize your code and classes, but the frameworks shouldn’t be general purpose. They should serve a particular purpose defined by the customer story.
Anyone who has played Halo or compared recent versions of SharePoint to the original version knows the value of focusing on the customer story.
It’s not gonna get any easier
What’s even worse is when you design and develop Halo II and SharePoint 2.0. Inevitably, there are many considerations you didn’t address in the prior release that invalidate significant portions of your framework. Unfortunately, those portions are already built and shipped, so you get all the enjoyment of legacy backward compatibility with little of the benefit. Aren’t you glad you were just so very clever to focus on the general problem instead of the customer and the story?
“But frameworks and extensible interfaces are critical to our platform!” scream the clueless cretins. Yes, they are when the customer is a developer and the story involves building upon our platform. However, the customers of Halo and SharePoint aren’t primarily developers. They are consumers and enterprise customers. The story line for them involves crushing the Covenant and sharing information. Focus on the customer, not on the framework.
Can I tell you a story?
What does it mean to focus on the customer and the story? It means don’t solve the general problem—solve the customer problem. The problem customers are trying to solve.
It means knowing the story line (the scenario) for customers. Who are they? What are they trying to accomplish? How are they used to accomplishing it? How might our software help? What would that look like to the customer?
Remember, many of our customers are developers. It’s no different for them. Who are they? What are they trying to accomplish? How are developers used to accomplishing it? How might our software help? What would that look like to the developer using our platform and tools?
Once you are focused on the customer and the story, design, develop, test, and deploy that story for those customers—nothing more, nothing less. Make that story compelling and delightful.
Along the way, certain generalizations will emerge. Avoid them unless they are needed for the story. Only engineer what’s needed to realize the story for the customer. Everything more general is wasted effort because by the time you need it the story will be different.
You are better off following the YAGNI philosophy of software development—only implement what you need when you need it. Never implement what you might need for a later feature. YAGNI stands for “You Ain’t Gonna Need It.” Sometimes the truth isn’t polite.
Temptations always come along
So you code up one story (scenario) and start working on the next. Perhaps it’s even the next release. Aren’t you going to wish you had generalized? No. No, you aren’t. If you had generalized you would have done it wrong. You wouldn’t have known what you know now.
Say while working on the next story, you realize you need to add a new weapon, landscape, or content viewer. No problem—refactor the design and add what you need now that you know what it is and why you need it. The result is better code, tighter code, and better tested code because the tests can focus on the story line.
Focusing on the customer and their story line isn’t difficult conceptually or even in practice, but it does require restraint. It is so tempting to solve the big problem for humanity. Resist that temptation. You can’t please everyone, so do your best to at least please your customers. In time, humanity will sing your praises.
PingBack from http://blog.a-foton.ru/index.php/2009/02/01/green-fields-are-full-of-maggots/
I agree with you for the most part, but you lose me on the "big design up front" part.
If you do big design up front, it’s exceedingly hard to come up with a design that’s customer-focused. Yes, you may have to do less rework if you do it that way, but you are likely to end up with something that mostly kindof works for the customer scenarios, but because you didn’t evolve it it’s really hard to adapt your big design to do what the customer needs.
I do think that a lot of people misinterpret what agile says about design. The message is not "don’t do design", the message is "only design for what you need right now" (or alternatively, "put off design for as long as practical").
I agree with the overall approach here as long as it is focused on "applications". The world is much more gray when you are designing/developing a "platform".
Let me tell a tail about frameworks… I created a product that was tailored to the needs of what I thought was a representative sample of my customers. I released it to the wider world and got many more customers who liked it, but many many more who wanted it to work differently in small or large ways. Some of these prospects said that they would write large checks to me if I fixed their problem. I find large sums of money compelling. Call me funny that way.
And here’s where things go awry. I figured "Hey, my customers are developers, so I can write a framework for them! They’ll code their own solutions and I only have to develop one framework rather than a zillion solutions! This’ll be great! Write one block of code, get lots of big checks & retire to the Bahamas!"
No, that didn’t work. First off, in the time it took to develop the framework, many customers took their checks elsewhere. When I finally got it to market, the remaining customers with big checks now said, "Hey, your tool almost does what I want, I see you have a great framework that’d allow someone to write a module tailored to our needs. Why don’t you write it?" Well, large checks are large checks, but writing the code to solve their problems was cutting into my Scuba training.
Still, lesson learned: You can’t anticipate all your customer needs, and frameworks are really great when you see a real pattern to customer requirements. But designing frameworks is expensive, you need to be really sure of your RoI before you start down that road.
If you really can make a solid case for a framework, justify that framework as an internal tool. If it really pans out internally, and reaches some sort of stable state then, *maybe* you consider making it public if there are enough folks waving large enough checks around.
In my case, the internal interface really did eventually do great things for my product. I was able to sell to a much wider audience and react to changing requirements much faster than I otherwise would have. Further, modularizing my product brought me a whole host of testing and deployment benefits. There was a positive return on developing the framework, but it wasn’t anything like the huge immediate win I anticipated.
And without question, making the making the API public cost me much, much more than it delivered.
Focus on the customer, not on the framework.
Could you post a definitive citation for the 2004 Boehm publication? Dr Boehm is quite prolific so there are a few candidates to choose from in 2004.
This post is available as a podcast here: http://blogs.msdn.com/microsoft_press/archive/2009/02/17/i-m-wright-podcast-green-fields-are-full-of-maggots.aspx