Let’s consider the implications of small-step development on our three shadow views of the program.
We begin from my fundamental belief, which is that if I were ever again condemned to create a software product, I would insist that we produce it incrementally, always maintaining a working, tested, growing version of the program.I wrote about that in How to Impose Agile Lo! these many years ago.
We begin our product journey with high hopes and grand wishes, including a lot of features, probably under a small number of headings: editing, searching, viewing, or data entry, statistics, reporting, or some other set of topics. We want the resulting product to be nicely filled with just the right features:
That’s the Behavioral shadow as we envision it: what the product does. As for the Interpretive shadow, we of course intend that we will have a very clear understanding of our product, so that every aspect of it is clear in our minds. We’ll make sure that it is modular, cohesive, with low coupling and meaningful names.
When we think of the Reactive shadow, how easy the product is to change, we of course intend that the design we chose was easy to modify, and that all the components are created so that they can be arranged, replaced, and added, all with little difficulty. Our imagined design’s components all slide together nicely.
There’s our product goal: a coherent grouping of valuable capabilities, with a clearly understandable design, and, even more, a design that is readily changed.
Nothing to it. At least not until we start.
Growing the Product
We know on day one that we’ll be growing the product. We know that week in and week out, for the long period of the product’s development, we’ll be adding to it. We’ll start from nothing and add capability daily.
And we have to be ready to release. The hidden secret of agile, incremental development is that we can add capability in any order, which means that if there’s a customer out there who only needs two of our twenty capabilities, we can do those two first, and begin to make our customers satisfied.
We may have the final design more or less in mind. It is my view that a few days spent thinking about the design before coding is only a few days wasted, so it can’t really hurt and might help. I take horse chestnut for the same reason: can’t hurt, might help. But I don’t eat the whole tree, and I wouldn’t recommend spending weeks or months designing. That violates the rule: we must have a running tested increment all the time.
And, let’s face it, a paper design is like a paper doll. It’s pretty but it won’t hold up in the rain. We want to be verifying our design ideas in the code, and modifying our design ideas as the code suggests. So we move quickly to code, to get our increment.
Our first increment is tiny. It does just a one tiny thing. It doesn’t require much in the way of design, and very likely we can understand it. If not, we should consider a career in another domain. Come on, it’s just one tiny feature! We can do that as clean as a whistle.
As we build this thing, we need to be confident. That’s why the phrase is “running, tested”, not something like “seemingly operating” or “possible functional”. We need to be confident in our first Increment and in every Increment after that. One good way to have that confidence is via testing.
If the product does a lot of complex operations, or the domain is messy–like, say, tax–we may well want a growing suite of tests that can be understood by the business-side people on our team. We may call these Acceptance Tests, but I prefer the term Customer Tests, because acceptance of a feature should be more of a collaborative act, and not quite so much as a final gate.
I freely grant that some thinkers feel that these business-level tests are not needed. I also grant that my own view changes. Sometimes I think our Programmer Tests, which I’ll come to in just a moment, will give us enough confidence. Other times, I’ve worked with customers who wanted a separate tier of tests. I’ve even seen tests used effectively as a gate. So we may want tests at this level.
As a programmer, and at base that is pretty much all that I am, I have found the confidence that comes from micro tests to be invaluable. When I have just written a tiny test and seen it not work, and then, a few minutes later, it does work, it gives me great confidence, and a little jolt of Yippee or Woot.
When I do not have that open-do-close bracket of a micro test, my confidence in the code declines slowly, and my tension increases slowly. I say “slowly”. Within 20 minutes with no Woot, my neck is getting tight. In an hour or two, I am edgy and impatient with the cat. I’d even be impatient with my wife but I’m not that much of a fool.
The tests serve to help with all three shadows. They give me behavioral confidence, the confidence that the program does what I intend it to do. They give me interpretive clarity, because each test says what the program does and isolates each added bit of functionality. And they give me reactive flexibility, because it turns out that if a program is readily testable, it will tend to break out nicely into small components, each one handing a small part of the overall computation.
The tests don’t just magically provide these benefits. They aren’t the only thing that provides the benefits. But used well, they contribute to all three shadows. Overall, they provide confidence in the behavior, the design, and its flexibility. Nice.
But then, by Wednesday or Thursday, we do the second capability.
The behavioral side of things looks good. Used to have one capability, now we have two. At this rate we’ll have a thousand by week after next. Well, maybe not. But we’ll have maybe 6.
But what about the other shadows? With the second feature, what has happened to our formerly very nice design? Is it still neat, clean, understandable? What about the ability to change the program? Is it still easy to change.
Very likely, after the second feature, things are still pretty clear and flexible.
But we do more and more. We have a half dozen capabilities, then a dozen. Often, if we are not very careful, our interpretive design shadow and our reactive flexibility shadows will be showing trouble:
We cannot allow this! If we were to allow it, each new capability would be harder to design, because our design is not clear to us. It would be harder to install, because our code is not responsive to our attempts to change it.
And, believe me, when we’re ten or a dozen features in, we can create a mess so bad that we may never be able to sort it all the way back to clarity and responsiveness.
This must not happen.
Continuous Design Improvement
If the design is going to be clear and flexible when we have a dozen features done, then it must have been clear and flexible at eleven. And ten, and nine …
It was good at one. Guess what: we have to make sure that all three shadows are good, before and after each added capability.
Sometimes, if we’re very good, we can start with our clear and flexible design at capability N, and just go smoothly to capability N+1, still clear and flexible. This can happen, for example, when plugging in number N+1 is little more than a new table entry, or a single chunk of code.
More commonly, at least in my long life, we make a bit of a mess adding capability N+1.
It’s not bad. It’s tempting to let it go, clean it up later. That’s always a mistake. Let me tell you why:
We know that we need all three shadows to be good. If we let one or two shadows slide for a bit, those capabilities take less time than would be necessary to do them right. We’re lying to ourselves. We only charged ninety cents for a capability that should have cost a dollar. We’re a dime in the hole. And that dime costs us more next time. The next feature should have cost a dollar, but now, because the design is less clear and the code less flexible, the capability’s cost will be maybe $1.05, just a bit more. And as long as we let that cruft remain in the shadows, every feature will cost a bit more than it should. And cruft grows, it’s not like we just skipped one little thing. We’ve set a precedent, we think we can do all our capabilities for ninety cents now. So we fall further and further behind, cutting more and more corners, hating the program more and more, hating our jobs more and more.
The most likely outcome of this spiral is more and more defects, slower and slower development, dogs and cats living together, general unpleasantness, and a much less satisfactory product.
Don’t let’s do that.
For every feature, let’s do our best to bring the design back to understandable, future changes easy to make.
Now the truth is, we can’t do this perfectly, but we can do it far better than our worst day, and often there comes a future day when we see Oh! a way we could have done things that is much better. If our design is clear in our minds, and flexible in its code, we can put in that improvement and accelerate.
I was working with a team in Omaha, and one of their repetitive tasks was to take a business rule and code it up and put it into their system. One of their people, we’ll call him George, was the specialist in doing that. I happened to attend three different planning sessions, spaced out over time.
At the first meeting, George signed up for two business rules, because he knew just how long it took him to do a rule.
At the second meeting, a couple of weeks later, George took four. Asked why, he said “I’m getting good at it”.
At the third meeting, a couple more weeks later, George said he’d do all the test of the rules. Everyone was shocked. He said “I got tired of doing it by hand and wrote a tool to do it”.
So we may sometimes accelerate when we learn more, but meanwhile, we need to keep our system clear in our minds and flexible in its code, so that we do not slow down.
When you slow down, the bear catches you. You do not want the bear to catch you.
Tell ‘Em What Ya Told ‘Em
The three shadows, behavioral, interpretive, and reactive, can each be observed as we create our many increments of software. Each one needs to be brought along together with the others.
If our understanding or our ability to change lag behind, we’ll slow down and in my experience. almost never fully recover.
If we let our design get too far ahead of capability, we’ll produce fewer capabilities than we might, and we are in danger of designing for things that never happen, and designing things that don’t quite fit our need.
We can probably never get it just right, but if we stop pushing to keep the design and flexibility high, it’s all too easy to fall down into the pit. There might be a Sarlacc down there. You never get out of those, you know.