I see a great many projects start off on the wrong foot by trying to decide all of their implementation technologies at the beginning of a project. Usually there’s a project kick-off meeting where everyone flies in from various locations to get introduced to all of the players and decide on things like what database engine are we going to use? What technology will we use to handle our data access? What user interface technologies should we use? Should we use web services?
While it seems like deciding these things up front would be worthwhile, it’s an endeavor that’s fraught with peril due to a number of uncertainties that are common at the start of most non-trivial projects:
Trying to make these decisions early is an exercise in making decisions with minimal understanding of the problem to be solved and the options available for solving it. “But”, I hear you ask, “Don’t I need to make these decisions now since they’ll drive how we build the application?” I’d like to convince you that you shouldn’t make these decisions now and to let that drive how you build the system.
That doesn’t mean that you don’t have to make those decisions, but it means that you can make them later with better information and still have confidence that the system can accommodate your decisions. And by later I don’t mean late. I mean at the latest responsible moment that allows you to reach your release goals.
So, how would I build the system if I didn’t make a decision about how I would ultimately store the data or interact with the user? I’d begin by focusing on building the classes that represent the domain of the system and look at how they interacted with the boundaries of the system. These boundaries would include those places where I was deferring a decision. Since I’m an Agile guy, I’d go through the exercise of determining which stories would have the highest business value and which would allow me to address the areas of highest risk (quite possibly those areas where I put off making a decision) and address them first. After a few short iterations (probably several weeks of calendar time), I’d have a much better understanding of the technology needs of my application and would be in a much better position to analyze what my technology needs really were.
To be a little more concrete here, let’s say the decision that I had deferred was about how I was going to manage persistence for my system. I’d try to implement several of the high business value user stories that would exercise the persistence requirements of my system. If the problem looked like it was moderately complex (more than just editing data that would be stored in a database), I’d probably use a Domain Driven Design approach to the problem. I’d follow the tenants of Domain Driven Design and use Repositories to manage the lifetime of created objects. I’d start with just an in-memory implementation of the repositories to allow me to do rapid development and testing of the domain model. The in-memory implementations would also ensure that my tests ran rapidly and would provide enough support for early demos to solicit feedback from users and customers.
From this early feedback, I’d continue to evolve the domain model and the interfaces to the repositories. At some point (often surprisingly quickly), the model would begin to settle down and I would be able to use the knowledge I’d gained to determine my real requirements for the persistence layer.
For example,
So, what did I gain and what did I lose by waiting in this case?
I gained:
What did I lose?
“But”, I hear you ask, “Didn’t I just increase my risk of finishing the project on time? Aren’t I just going to end up in a situation where I finally make the decision to use a particular technology and then realize that the cost of using it (usually in schedule time) will be too great to recover from?”
No, I’d argue. What you’ve done is traded in invisible unmanageable risks for visible manageable ones.
The invisible unmanageable risk of making the decision too early has to do with how the software gets built if those decisions are made too early. The risk is of incurring Opportunity Costs and being faced with the Sunk Cost Dilemma. These risks will materialize on your project because of the Path of Least Resistance – Software Development Variation and the Kudzu Effect™.
The Path of Least Resistance – Software Variation states that under pressure, developers will take the path of least resistance to achieve their goal (which itself is subject to the Law of Diminishing Expectations). This pressure may take the form of schedule pressure or the pain involved in learning new ways to do things. The end result is often taking shortcuts that enable the developer (or team) to meet a short term goal. These shortcuts often involve bypassing architectural boundaries for the sake of expediency as in “Hey, we can just bind these datasets directly to the controls and we’re done.” Or “If we just generate the classes from the schema, we can get those edit screens up for that first iteration.”
A few instances of taking the path of least resistance quickly leads to the Kudzu Effect in which those underlying technologies begin to take over the application like Kudzu has taken over the American South (see http://en.wikipedia.org/wiki/Kudzu). Very quickly half the classes in the system seem to know about (i.e., have dependencies upon) what should have been an implementation technology that was only meant to be used to keep the access ramps (see Kudzu) of the system working properly.
The Opportunity Costs are the costs of doing what you did versus what you might have done with that money or resource. In this case, it’s the cost of having each developer spend the time and effort to understand the underlying technologies that have now crept throughout the system versus having them focus on learning the domain of the application and getting further along as team in understanding the domain (which in turn increases the odds of building a successful system and provides you with information to choose the most appropriate underlying technology).
The Sunk Cost Dilemma surfaces when the team finally understands enough of the system’s requirements and what needs to be done that it’s apparent that the initial technology choice isn’t going to work out. And the dilemma is that now you know what you should do, but so much of the initial technology choice has found its way into so many nooks and crannies of the system that there is no way to pull it out and replace it, and even if you could do that, so much of the schedule time and development budget has been spent getting to this point that you’ll never meet your release date.
The end result is probably familiar. Some dedicated people will find a way to slog through getting something working mostly the way it needs to work. And they’ll get there later (maybe a lot later) than they (and their customers) had hoped.
“Okay”, I hear, “If those are the invisible manageable risks, what are the visible ones that I can manage?”
The visible risk is of choosing the implementation technology too late to implement it. You can start to manage that risk by what you do at the kick-off meeting.
So what would I do at that kick-off meeting?
I’d create an initial risk management plan. These technology decisions that you’re deferring aren’t the only risks you’ll have in that plan (unless you’re really lucky), but for the technology risks I’d do the following:
And then what…?
Assuming I’m using a typical Agile development plan with multiple short iterations (e.g., two weeks), I’d allocate appropriate tasks to drive decreasing the risk. This could vary from ensuring that stories that would drive our understanding were implemented, to creating prototypes using the technology, to sending someone to a class or having them search the web for the information we needed. And I'd timebox those efforts so they didn't become resource sinks.
I’d review the risk plan at the end of every iteration. This would include updating estimates of when the decision needed to be made and ensuring that we were learning what we needed to in order to make a good decision. Then I’d repeat the process. And finally, I’d make the decision at the latest responsible time. And I’d be making it on much sounder footing.
Comments
Hi Robert!
What you say makes a lot of sense, no surprise there, but I'm currently working on a project where the decision to use a particular database technology was made early on... pretty much at the beginning. Why? Well I don't want to use the wimpy excuse that "I wasn't working on the project at the time this decision was made", and even if I was I might not have been in position to directly work around this decision. Some of the decisions to select a database early were based on the following:
Having said that, I also should tell you that: A) We are using Agile just as you describe and B) I >think< we used something akin to the repository concept in order to isolate the DB from other production code so that the other groups (we have multiple scrum teams running) wouldn't be blocked. I say Akin because we don't have a true in memory repository. We use NMock to "stub" out interfaces and inject test data.
With both of these statements, we are in a position (should we need to go in this direction) where we could change the DB, remove the DB etc, in a "safe": we have unit tests to catch dependencies but is anything truly 100% safe ;), and speedy (in a sprint) manner.
Having said all that, I do struggle (as I mentioned to Rjae) with the fact that we have what some people would consider business logic written into stored procedures, and I worry what the implications of changing the DB technologies would be underneath the interfaces we have.
So I have no problem with you saying "start with an in memory repository first." I truly feel that Agile and your 4th point "I’d make remaining neutral with respect to the piece of technology a design requirement and ensure that the development team knew to isolate that decision behind the appropriate interfaces" above are the ones that help us rule the day.
Great post! There is a TON of good material here. I'll come back to this post frequently.