#5 - TDD, Pair Programming, and Katas: Refactor with tests

Do you want to improve your code reusability and readability for your team? Try refactoring your code in small, predictable, low cognitive, easily trackable, and revertable changes using tests.

The Problem

While completing yet another requirement of the bowling game kata, Peter Combee, and I noticed that the logic we spent developing until that point was not fitting for a new requirement; we understood we had to revert the whole thing! We simply couldn’t fit the old code for the new requirement. We needed another logic to make both the old and the new requirement to be satisfactory. But how could we do this while preserving the behavior expected from the code?

Thankfully, we were translating the old requirements into tests while also decoupling from the production code, so we knew whether the code was satisfactory as we were changing it; by checking if the tests were passing at all times. Unfortunately, we noticed that during this process, not only one test but ALL failed with each step we made! The process made us insecure while creating the new logic because we didn’t know if the code would work until all tests were passing.

Tests are not enough!

Even if we had tests and we could resolve the problem of the new requirement by modifying our logic, this process took us too much time, and we were very much unpleased because a lot of times we found ourselves handling a lot of problems at the same time:

  • We had a higher cognitive load.

  • The changes weren’t easily trackable.

  • We couldn’t revert changes quickly.

This added more frustration for us, making resolving the problem even more challenging while preserving the old code behavior.

Note: Notice I say same behavior, which means the same expected result of the code, not to be confused with the same previous logic from production code.

To avoid frustration and time, we decided to do this process again by trying out a different approach that didn’t take too much cognitive load and was safe enough to experiment and revert changes easily.

Refactoring twice!

This is when we used the “Refactor” Phase of TDD. There’s not only “red, green, refactor… and then add more features”; the refactor phase includes refactoring what we already had refactored by understanding if a new requirement doesn’t fit with the old logic of the code. If it doesn’t fit, we could hold on adding the new requirement (translated into a test) by making sure the old code is refactored once again but this time to give path to a new code that while preserving the old behavior.

So, how did we achieve this? We used the expand-shift-contract refactoring technique to carefully refactor our code in small, predictable steps. These small steps should be made with small, focused commits, so the changes are trackable and easy to revert. This technique makes the code more flexible and lowers the cognitive load, allowing us to experiment with different approaches before adding the new requirement.

After applying this technique, the old code was correctly refactored, and we could easily add the new requirement, lowering our frustration and giving us enough energy to tackle new requirements as they appeared.

Check out my friend Jon Reid with an interesting Refactoring Demo:

Conclusion

This article explains the importance of the refactor phase, why we should refactor in small, safe, revertable, and predictable changes before new requirements come along, and how it improves your team’s development time and effort.

We will share Pair Programming insights weekly as we continue our journey in Pair programming.

Stay tuned in the link below. In the meantime, stay safe!

Previous
Previous

How to migrate your code to Swift’s Async/Await - Part I

Next
Next

#4 - TDD, Pair Programming, and Katas: How Driver and Navigator Work in Pair Programming?