An experience I had as a participant in XpImmersionFive
led me to a surprising understanding of how radical DoTheSimplestThingThatCouldPossiblyWork
really is. At first I had trouble accepting the extremeness I discovered, but I think I've come to terms with it. In any case I think the story is worth telling and discussing as part of an exploration of DTSTTCPW.
In one pair-programming session my partner and I set out to add "track year-to-date savings plan contributions" to a payroll system our group of six was developing. Several of us had a quick design discussion in which we decided for various reasons that this value did not belong being stored in instances of Employee, a decision I would have argued strongly for in other contexts and was happy to accept here. Instead, we decided that the responsibility belonged to Payroll. My partner and I set off to implement a hash table that stored the YTD value for each employee. For a variety of reasons (all of which could be explained away, but which in an XP context count just as much as "real" difficulties) we got bogged down and ended up working for an hour before getting a green bar and going way over our task estimate. (In the context of the exercise, an hour was about 5-10 times too long to go without a green bar.) OK, well, stuff happens.
The next day, I coincidentally ended up with a different "add year to date total" story to implement. This time, with the pace of development having picked up and a deadline looming, it seemed obvious to just stick an instance variable in Employee. I even asked my team "tell me again why we decided not to put yesterday's YTD number in Employee?". It was a strange experience to find the arguments -- which I normally would have agreed with strongly -- very unconvincing. We put the new YTD number in Employee and had our story done in less than 10 minutes. Wham. Made the previous day's fiasco look ridiculous.
Later I posed the question to several of the course's coaches "how do you resolve the conflict between DTSTTCPW and what you know to be good design?" (In this case the tension I was experiencing was between the simplest thing being just sticking extra instance variables in Employee and the design experience warning me of the trouble that approach will eventually cause.) After much discussion and reflection, and realizing how closely this interacts with YouArentGonnaNeedIt
, I came to a new, deeper understanding of DTSTTCPW and XP in general: OK, fine, I expect this simple approach to lead to trouble, so when difficulties show up I won't be surprised, I won't be confused, and I won't be upset. I will have anticipated those difficulties and I'll be prepared to deal with them when they actually arise, but don't need to do anything yet. Moreover, although in principle the difficulties are waiting for me, I may never actually encounter them on this particular project. Meanwhile, I give my user new functionality very quickly. And how big a deal is it to rip out an instance variable and move a responsibility to a different class?
I've been doing incremental programming for over 25 years. Most of my development work is prototyping and tools. I thrive on chaos. I hate Methodology. (Actually, I have never even seen anyone follow a methodology or a process in the places I've worked and consulted.) I abhor up-front Design. I am naturally so undisciplined that my main fear about XP is the rigid disciplines it imposes. (You're supposed to laugh there.) Yet, in this experience I encountered a way in which XP is so extreme it even makes me
nervous. DTSTTCPW really is telling me not to automatically follow even the smallest bit of experience-based design wisdom about responsibility separation, but instead to just blast ahead with the simplest possible thing. That is truly extreme! But I think I believe it will work in the context of the other XP practices.
[reflecting further, I add:]
What I learned here is that there is an important difference between knowing the dangers of a particular design choice
and avoiding them at a particular time
. I suppose that with all the things that traditionally make changing software difficult -- inadequate tools, inability to change other people's code, concern over breaking other things, insufficient encapsulation of responsibilities, etc. -- one learns from the pain of experiences that it's better to do it "right" the first time than have to redo it later.
With appropriate tools, policies, and practices, the economics of this rule change, and in fact it may not
be better to do it right the first time when right is more complex. (In my experience people hearing about XP at first don't hear and then don't take seriously the crucial, but subtle, statistical argument that whatever you are doing now may well turn out to be irrelevant, so spend as little time as possible on it -- you may have misunderstood a customer need, the customer may change their mind, the project may get cancelled (for lack of early results, perhaps!), the external environment may change the relevance of the feature, something else might get bought or built that fills the need, etc.)
There are two parts to the "do it right the first time" rule. One is the implicit extolling of understanding what the right way to do it is; the other is simply a scheduling claim. What I learned, in the end, is that these are separable. I can do it simply now
, while noting (or trusting myself to later recognize) the disadvantages to the design choice and switching to the more complicated design when it becomes necessary
. The economics of XP make this feasible.
You abhor up-front design. But wouldn't you agree that your decision to put the responsibility in Payroll (as a result of your design experience) is an example of up-front design? If not, how do you define up-front design? Excellent piece, BTW. --RandyStafford
Well, even the decision to add it to Employee is up-front design. I think
what was meant is Big up-front design. --Robert
By up-front design I meant design meetings, design documents, days of design discussions, etc. For me design has always gone along with coding: design a (very) little, code a little, think about design a little, change the code a little, etc. I can't stand anything that is just
design. And I'm not saying that it isn't right, good, or effective to do up-front design (outside of XP), just that I don't find it useful and my personality doesn't tolerate it. -- MitchellModel
I'm in the same boat. The only time I ever produce design documentation anymore is after-the-fact, on demand, if someone wants to know how the system works at a high level because they might be maintaining it or something. And the essence of what's needed is typically some deployment diagrams, interaction diagrams, and component and package diagrams. When it comes to up-front stuff, here's an anecdote from one of my recent projects. DaveMuirhead and I pair-designed a SystemMetaphor while carpooling to a team meeting. We used an ElectronicWhiteboard to draw a CartoonDiagram? of the metaphor, and to draw a class diagram of the DomainModel. The next day, we fired up our editors... Several months later, after we had delivered the system, I was asked to document the design, and I reverse-engineered a nice 50-page document with all the right UML diagrams and component descriptions. I don't think it's possible to get the same results by forward-engineering. But I wonder if people sometimes mistake requirements development as BigDesignUpFront. Because the PlanningGame depends on estimation, and estimation on a pile of stories, some amount of a-priori story identification and elaboration, i.e., requirements development, is necessary (similar theme on WeWillTry). --RandyStafford
If it is too extreme, it could not be right! You will always have to decide if and how many design the problem at hand needs. This will be affected by your knowledge ((possible) future requirements, implications of a design decision, what is 'good' design). Prominent forces in this decision are DoTheSimplestThingThatCouldPossiblyWork
. I'm not convinced that design is a four letter word. It's just that simplicity is more important than we've believed. --ManfredSchaefer
Try reading the story again. Look more for the transformation of a person than the transformation of a design.
The simplest isn't always right. Please read http://web.archive.org/web/20010602040800/http://www.newscientist.com/features/features.jsp?id=ns226844
as a proof of this. Just by the way, the article got one of the tests wrong. Test 1 is misstated in the print. Oh, well.
Proof might be an overstatement. I learned from the article that humans favor the simplest logic and may not enumerate all cases when evaluating interacting clauses with negation. We would be wise to avoid such logic in our programs even if it takes a little extra effort to find the necessary simplification.
This is an excellent point. If anything, the article proves that moving beyond simple
statements may introduce recondite errors in logic evaluation. So, maybe simple is better than right
Of course. And XP is not appropriate for all software efforts. What I discovered was that "right" is largely an economic
decision, and that the XP practices change the economic context of software development in some surprising ways -- enough to challenge, for the purposes of doing XP
, some decisions I've made over the years about doing things right the first time. -- MitchellModel
I arrange my code to get the potential problems out of the way first. If arguments need to be checked or some other method, thread, process, or what-have-you needs to take place before my method can continue I do that early in the stream. I even set up my C/C++ evaluation statements to take advantage of logical short-circuiting -- any false condition causes the evaluation to exit false. The reason for all of this is that I check to make sure that the results of my efforts are going to be valid before going through the effort itself.
My designs are the same way. I like to design in error detection and correction early on. This kind of thinking makes the operational part of the design easier to deal with; the problems have all been eliminated before the stream got to this point, so I can forget about checking and just deal with the actual job. This may not be the simplest thing, but it certainly feels to me like the right thing.
I refactored the code of another team member on a fairly substantial project and made a lot of these kinds of changes to his stuff. The results were shorter and made more sense to me because all of the methods checked for all possible errors way up front in the procedure. Does this not make the rest of the code simpler? -- MartySchrader