First One, Then
Many
Kent Beck, Three Rivers
Institute
Blog Hiring
Kent
Abstract: The
system will eventually
have to deal with an arbitrary number of some element but for the
moment it only has to deal with one. What to do?
Introduction
To make the question concrete, suppose we are building a system
that will have one transaction posted every year. The moment we
absolutely need the system to
handle more than one transaction is one year from now. What should the
implementation and API be in the intervening year?
Applying the Leap strategy is tempting, because the
end result is clear and leaping avoids the need for any refactoring:
Contract
List<Transaction> transactions;
Iterator<Transaction>
getTransactions();
We know what the future holds, we program for it,
what could go wrong?
Half and Half
Suppose that we ask our customer what reports should look
like when they have more than one transaction in a contract and they
can't tell us. They are too busy, they just don't know, or they want
some experience with the system before deciding.
One strategy is to leap to the multi-transaction
implementation and API, but let clients assume that there is a single
transaction:
ContractReport
Transaction first= contract.getTransactions().next();
format(first);
In so doing we have left behind a trap. In a year,
the second transaction will be posted and our report will no longer
reflect the information we're holding. The contract report will run to
completion but the answer will be quietly misleading.
The problem is that the client code has subtly
different assumptions from the Contract
API, assumptions that 1) aren't explicit, 2) can't be checked by a
compiler, and 3) are easy to make in the heat of the moment. Get that
report done, even if it only works with a single transaction!!! Yes,
boss...
We could put a bandaid on the client code to
explicitly check our assumption that there is a single transaction:
ContractReport
Iterator<Transaction> all= contract.getTransactions();
Transaction first= all.next();
if (all.hasNext())
throw new Error("We can't yet report on contracts with
more than one transaction");
format(first);
The guard clause prevents our code from silently
producing incorrect answers but 1) we have to be wise and disciplined
enough to add guards everywhere we make the single transaction
assumption and 2) the guard doesn't give us any feedback until it
triggers. It would be useful to get feedback earlier.
Succession
The Permaculture design principle of succession suggests an alternative
solution. In agriculture, succession refers to a design that
deliberately changes over time: plant fast growing manzanita to
stabilize the soil while slower growing (and more valuable) Douglas fir
emerges. Even though the eventual goal is a forest of tall first,
planting something else first may be the more energy-efficient way to
get there.
Applied to software design, succession refers to
creating a design in stages. The first stage is not where you expect to
end up, but it provides value. You have to pay the price to progress
from stage to stage, but if jumping to the final stage is prohibitively
expensive in time or money, or if you don't know enough to design the
"final" stage, or if (as postulated above) the customers don't know
enough to specify the system that would use the final stage, then
succession provides a disciplined, reasonably efficient way forward.
Using succession in our example, we would design
both the implementation and API in terms of a single transaction:
Contract
Transaction transaction;
Transaction
getTransaction();
This design reflects our current reality: we have
one transaction. The beauty of the succession-based design is
harmony: the assumptions of the Contract and the assumptions of its
clients always match. As far as everyone is concerned, there is only
one transaction:
ContractReport
Transaction first= contract.getTransaction();
format(first);
And Then...
We pay the price for succession when the time comes to post the second
transaction. Suitably in advance of this date, we need to upgrade
Contract and all its clients (the customer needs to be prepared to tell
us what multi-transaction reports look like), but we change both
Contract and its clients as a unit. The assumptions of the API and the
clients are always synchronized.
To reduce the risk and increase the efficiency of
succession, I have templates in my head for common successions (I may
have enough successions to begin a coherent and comprehensive catalog,
but that's a topic for the future of Responsive Design). (When I'm
thinking clearly, I even use my own templates.) The one-to-many
succession works like this:
- Add an API to make it look like there are multiple transactions:
Contract
Iterator<Transaction>
getTransactions() {
return (new Transaction[] {transaction}).iterator();
}
- Add a parallel implementation to handle multiple transactions in
parallel:
Contract
Transaction
transaction;
List<Transaction>
transactions;
- Everywhere the old
transaction is set, set the list of transactions as well:
transaction= ...;
transactions= Collections.singletonList(transaction);
Now we are guaranteed that the list will always contain a
single item, and that item will be identical to the contents of
transaction, so we can...
- Replace every reference to transaction with the equivalent
reference to the list:
Contract
Iterator<Transaction>
getTransactions() {
return transactions.iterator();
}
All this "safe stepping" might seem like a lot of work. My experience
is that if I practice I can go through the steps very quickly and with
minimal chance of a mistake. Eventually many of the templates turn into
automated refactorings, which is even faster and safer. When I rush and
skip some steps (in the name of efficiency, natch) is when succession
gets slow and unpredictable. This bit me just last week. I was turning
all uses of file names into uses of streams (it's generally a good
thing to convert to streams as high as is reasonable and write the rest
of the code in terms of streams, but I had ignored this in the name of,
you guessed it, efficiency). I kept plowing ahead with the next change
I could see without regard to the template I use and I ended up having
to throw away a half a day's work. When my priorities are straight, I
prefer keeping to the templates rigidly so I can spend my time thinking
about bigger issues.
Conclusion
Here are some connections between the topics in this paper and other
work:
- Succession in general is an example of the lean software
development principle of pull. Starting with the customer's needs, we
make just enough system to satisfy those needs. Not a haphazard hack
satisfying those needs, a consistent, coherent design satisfying those
needs, but one with no extra fat.
- Succession, and "First One, Then Many" in particular, is also an
example of the Responsive
Design principle of simplification. We couldn't jump right to the
design we could imagine, so we did something simpler that still
provided value.
- "First One, Then Many" is an example of the lean principle of
completed work. By designing a multi-transaction API and using it in a
single transaction way, I have left the system in a half done state.
Breaking the work into self-consistent phases lets me complete half the
work and then later the rest instead of half completing the whole job.
"First One, Then Many" comes with the cost of the
transition between one and many, and this cost must come with a payoff
for the succession to make sense. If I already know what the system
should do with multiple elements, I generally just leap. If I am
proceeding deliberately, either because I am not at the top of my game
or I am coding with someone who isn't ready to leap with me, applying
the succession makes sense. If the client doesn't know what to do with
multiple elements, applying the succession makes sense. If the
possibility of multiple elements is uncertain or far in the future,
applying the succession may make sense. The cost of the succession
needs to be weighed against the benefits of finishing sooner and the
costs of inconsistent assumptions. In short, I find it better to stand
on solid ground and take a confident step forward than try to stand
with one foot in each of two boats.
Coda
I'm intrigued by the idea of a taxonomy of incremental design
successions. I'll be watching my own design to see if "First One, Then
Many" is the first of many. If you notice successions that repeat,
please let me know.