Agile Tips

#54-Changing Change, Part 4

Scott L. Bain

This week I will relate the notion of "changing change" to the test-driven development process, in two different senses.

Changing change, part 4.

This series has been about altering our relationship to the changes that we must accommodate When developing business automation products.

So far I have examined three design issues, namely the focus on interfaces in early decisions, compositional approaches, and the encapsulation of variation throughout. But how does all of this relate to test-driven development, which is a discipline that I strongly recommend?

This depends on the kind of change that is required.   Sometimes we need to alter a behavior of the system under certain circumstances, or to add a new behavior, while other times we need to re-architect the system without changing its behavior, often in preparation for some future need or business opportunity.

If we are making a change to an existing behavior, that behavior must have a test that initially drove it into being. If it does not, then we need to stop immediately and determine how this was missed, because failing to write a test for a required behavior is an abandonment of the test-driven process. Broken process needs to be addressed quickly before it gets worse.  In fact, Agile says that we must constantly improve process, and part of this is knowing where improvements are needed.

Once we identify the source of the mistake, then the missing test is added, executed, and observed to fail. 

If such a test does exist then we begin by changing the test to reflect the new version of the behavior that the requirement indicates. That test should be executed and observed to fail, to ensure that we are not about to do work that is already been done. Then the system is altered to produce the new behavior, and then this test is executed and observed to pass. This ensures that the test is valid, and that the change we made was correct.

A brand new behavior is added in the same way: the test is written, it fails, the work is done until the test passes.  All changes to existing behavior must be done in this way, by strict policy.

On the other hand, if an architectural change is required of the system, to make way for a new requirement, or as a basic concern about its quality, this is something called "refactoring". Refactoring is defined as the process of improving the design of a system without changing its external behavior.

As always we want to make sure that our actions are in alignment with our intentions. If the intention is to refactor the system, and we want to confirm that this is what we are doing, then we need to ensure two things:

  1. That we are not changing behavior, and 
  2. That we are improving the design.


The tests we write in test-driven development are written before the product is created. Acceptance tests are written to capture all the requirements we know about for a given iteration of work. Unit tests are written along the way as we create each individual feature or behavior. In either case they are created before the work is done, and so they are not "tests" in the traditional sense because there is nothing to test at the moment that they are created. What they are is a reflection of the required behavior, and writing them is an analysis process that proves that we know what that behavior is with enough detail.

So when we intend to refactor the system the tests should continue to pass because they reflect the behavior that was required in the first place, for which the system was created.

But how do we know that we have improved the design?

That requires that we know what good design is in the first place, and whereas many people believe they know the answer to that question, not all of them do. Also what one person believes to be "good design" may be different from what someone else thinks. If we're going to work in a collaborative way we must come to an agreement about what good design is, otherwise our commitment to it could be meaningless.

This is all a matter of quality, and quality is something that must be an agreed-upon contract so that everyone can align themselves with it. But assuming we can do this, and assuming that we have improved the quality of the system, and assuming that all of the existing tests still pass, then we know we are refactoring.

What if we need to improve the system and change its behavior? It's my strong recommendation that this is done in two steps. The first is the systemic improvement, without behavioral changes, the second is changing or adding a new behavior. Test-driven development supports both of these steps, and allows us to respond to change in a way that is orderly, safe, and free of waste.