Baby Steps in TDD
By: Heaton Cai
Tags: Test Driven Development, Refactoring, Tdd, Agile Development, Baby Steps

There are 2 places that require following Baby Steps in TDD.

Baby Steps is one of the basic practices you must master before you become a TDD expert. It helps you speed up your TDD process a lot and eventually, it will significantly raise your coding speed when you become a master. However, many people choose to skip steps because they feel doing baby steps is slow. This would stop you to become a master and fail to enjoy the benefits. So I suggest you don’t skip steps until you have mastered it, which means you are able to do baby steps much faster than not doing it.

First place, Baby Steps in TDD iterations

What is an iteration?

TDD is in essence, follow three simple steps repeatedly:

Write a test for the next bit of functionality you want to add.

Write the functional code until the test passes.

Refactor both new and old code to make it well structured.

Each time you finish a cycle, we call it an iteration.

Why should we follow Baby Steps in iterations?

Many people fail to do TDD (and get benefits) is because they don’t know how to find a proper order of tests that drive the implementation. They end up writing only a few complex test cases that each test case covers multiple scenarios, which results in too few iterations that each iteration has a lot of pre-designed code (code is not driven by refactoring). This is almost the same as not doing TDD.

Why big iterations lead to pre-designed code?

When people need to pass a test for multiple scenarios, it would be hard to just write the code without thinking about the design. Even they do, it would be hard to do refactorings because too many bad smells introduced at once. In this case, people tend to rewrite, which ends up with pre-designed code. On the other hand, if a test only introduces one scenario, it would be easy to just make it pass without thinking too much, it would also be easy to refactor the code.

Emerging design & Pre-design

Emerging design (objective): write functional code without thinking about the design principles and clean code, then refactor code by removing code smells until no code smell left

pre-design (subjective): think about the design before writing the code, it normally leads to over-design

Emerging design vs pre-design is one of the biggest differences between TDD and non-TDD. Design is about making decisions. They are subjective and very similar to bets. The more cognitive load we have, the less chance we win a bet. By reducing the size of each iteration, we can reduce the cognitive load of each design decision, so we can always win the bets. And we should get the best design at the end since we are able to keep the best result after each iteration.

reduce the cognitive load by reducing the iteration size to have a better design

Of course, Baby Steps isn’t enough. You need to follow other TDD principles (such as simple design and YANGI) all the time to achieve the best outcome. However, Baby Steps is the basis. It’d be hard to follow other principles if you don’t do Baby Steps.

By the way, predesigns can be removed entirely when you master TDD.

Real just-enough

Another benefit of Baby Steps is that we can easily find out just enough implementation. There is less chance to implement ahead of the tests because we only need to focus on one very small thing in each iteration. So we can make sure each piece of code is driven by a test that represents the business value.

Take baby steps between iterations

Each iteration starts on a new test. So the principle is to minimise the difference between the next test and the previous test, and fail.

Let’s take an example:

Bowling Kata

baby steps for the bowling kata

The above is just an example of how the baby steps look like. You can have your own order of test cases. Just make sure you follow the principle all the time.

Summary of Baby Steps in iterations

To be able to enjoy the benefits of TDD, having the implementation really driven by tests and emerging the best code design, we need to follow Baby Steps by minimising the difference between tests. Otherwise, you might just end up writing code and tests separately, which is not TDD.

Second, Baby Steps in the Refactoring

Why should we follow Baby Steps in Refactorings?

No new behaviour added during refactorings is one of the key principles in TDD. Adding new behaviours without tests breaks the safety net that ensures the code meeting business requirements.

Book <Refactoring> defines a lot of standard refactoring methods that guarantee no impact on the code behaviour. By only following these methods, we can make sure no new behaviour is introduced.

However, in most cases, a refactor requires a combination of many methods together to remove a code smell. Even sometimes these methods are not enough. It is very hard for developers to do it without deliberate practices. So most developers end up rewriting code instead of refactoring, which increases the risk to introduce extra behaviours.

By following Baby Steps, you can minimise subjectivity in the refactoring steps so that the risk to add extra behaviours is also minimised.

Take baby steps in refactoring

In each refactoring, we should follow:

only use standard refactoring methods unless it’s not possible (such as duplication to for-loop, or generalising code) avoid to do refactoring methods manually if it can be done by your IDE run all tests for each change and make sure they pass all the time, otherwise revert the change

Let’s continue to use the previous kata as an example:

After the following test cases:

tests for the first 4 iterations

The code looks like this:

Now we find a smell that 3 branches in if-else, let’s do a refactor to remove it.

The first step, make a plan:

the line 2 and line 3 looks like the same pattern, so generalise line 3

rolling[0] and rolling[1], looks like it is just an incremental index, so a for-loop might be introduced

Let’s do it, the steps look like the following (don’t forget run tests for each change):

refactoring by baby steps

Now, it looks good enough. Refactoring finished. Move on to the next test case.

Summary for Baby Steps in refactoring

Keeping each change as small as possible in refactorings is one of the key aspects in TDD to reduce the risk of introducing new behaviours accidentally. It is also the basis of mastering your refactoring skill. So make sure you follow Baby Steps all the time. Otherwise, you might end up with rewriting, which is not expected in the TDD process.

Summary

In fact, the TDD process is trying to remove subjectivities from your coding activities. If you practice it well, all your code can be just driven by the business requirements. It results that every line of code has a value defined by your tests. No extra code, no extra tests, no extra work, self-proven for completion.

Following Baby Steps in TDD is normally missing from a lot of developers. Without it, the TDD process would hardly work because people just write code subjectively as not doing TDD. So always do baby steps in both iterations and refactoring steps so that you can enjoy the benefits of TDD.