👋 Hello, this is Valentina with the free edition of the Optivem Journal. I help Engineering Leaders & Senior Software Developers apply TDD in Legacy Code. To get these articles in your inbox every week, subscribe:
Andreas Frömer, Head of Software Development at Finanztip, shared his success story of using TDD to rewrite a legacy application, to replace the old unmaintainable code with the new tested one. He used TDD to successfully rewrite 3 million lines of code!
Lesson #1. Start with system level tests
One thing that Andreas did very well was to start with black-box tests first (E2E tests) around the application, to provide protection.
Unfortunately, many teams make the mistake of jumping straight into Unit Tests. That is very dangerous - if we jump straight into Unit Tests (with no high level tests), then as we break dependencies and introduce unit tests, we could cause regression bugs at the system level!
That is why even throughout the TDD in Legacy Code series we start from System Level and then go downwards.
Lesson #2. Use feature flags to replace old code
When we write tests that are coupled to behavior, it means that we can replace the structure, but still retain the behavior.
When we write System Level tests, since they are coupled to behavior of the system, we can completely rewrite the System (e.g. refactoring/rewriting monolith, refactoring/rewriting microservices, moving from monolith to microservices or vice versa) and ensure that our System retains its behavior from the End User perspective.
Similarly, when we write Component Level tests (for testing the Frontend, for testing individual Microservices), those tests are coupled to the behavior of the Component. Thus, we can refactor the component or even rewrite the Component and our tests tell us that we didn’t break the behavior.
By having both tests AND feature flags, we can safely replace the old code with the new code.
Rewriting vs refactoring… and TDD
When dealing with Legacy Code, you have the choice of rewrite vs refactoring at any level.
Irrespective of whether you choose to rewrite or refactor, you should start with System Level tests (Smoke Tests, E2E Tests, Acceptance Tests) for protection.
If you choose to rewrite a Component, write tests to cover an existing component, then rewrite the component with TDD. If you choose to refactor a component, write tests retroactively, but apply TDD to future changes in the component behavior.
Share your success story
I would like to feature TDD success stories in future editions. To share your story, write a comment below. 💪
Want to apply TDD in practice?
You tried TDD, but it didn’t work. That's why I'm going to help you practice TDD step-by-step. Apply TDD on a sandbox project, and get my feedback and answers to your questions. Access TDD in Legacy Code.
Achieving this in 6 months is impressive!
> Unfortunately, many teams make the mistake of jumping straight into Unit Tests. That is very dangerous - if we jump straight into Unit Tests (with no high level tests), then as we break dependencies and introduce unit tests, we could cause regression bugs at the system level!
Welp, I wish I thought about this or read this 2 years ago...
Thanks for clear explanation! Maybe I will be able to come back with a case study to you in a few months :D