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?

For now, one transactionIn a year, multiple transactions

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:
  1. Add an API to make it look like there are multiple transactions:
    Contract
    Iterator<Transaction> getTransactions() {
        return (new Transaction[] {transaction}).iterator();
    }
  2. Add a parallel implementation to handle multiple transactions in parallel:
    Contract
    Transaction transaction;
    List<Transaction> transactions;
  3. 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...
  4. 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:
    "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.