TDD in Legacy Code Transformation
Legacy Code is a nightmare to maintain. Escape this by introducing TDD. I created the TDD in the Legacy Code series to help you achieve this step-by-step.
You’re stuck in Legacy Code. It’s a Big Ball of Mud, there are no tests. In the book Working Effectively with Legacy Code, Michael Feathers defined Legacy Code:
“To me, legacy code is simply code without tests.” - Michael Feathers
Legacy Code is a nightmare to maintain
The development team is getting slower and slower; they’re constantly under stress. QA is finding lots of regression bugs. The business is frustrated about why releases are so slow and why new features are constantly delayed.
It seems impossible to escape this nightmare. The team knows that the cause is because the Legacy Code is unmaintainable. The only way to escape the Legacy Code is to redesign and refactor the system such that it is more maintainable. But that is not possible because there aren’t any adequate tests to protect us. But we can’t add the tests because the architecture is untestable. So we can’t make the system more maintainable. We’re stuck. Catch-22.
Escape Legacy Code by introducing TDD
Imagine delivering any functional changes, such as User Stories and Bug Fixes, safely and quickly. Imagine redesigning the architecture and refactoring the code, whilst being protected from regression bugs. That’s why you need tests - to protect you. The best way to have a reliable test suite and testable design is - with TDD.
You tried TDD... but it didn’t work. TDD is straightforward in theory but very complicated in practice. The reality is that you’re working on a legacy, not a greenfield project. However, all the online courses are based on simple demo projects that don’t deal with the complexity of real-life code. They’re not practical, and when you tried applying TDD, it didn’t work or faced many challenges.
TDD in legacy code seems impossible. The theory is simple: You read books, you watched courses, and you tried applying it to the legacy project in your job, but it didn't work.
You don’t want to waste your time with trial-and-error on how to introduce TDD in Legacy Code. You also don’t want to risk your job reputation by making risky changes destabilizing the system. That’s why I made this series so that you can introduce TDD step-by-step in a safe way, without overwhelming the team, without stopping feature delivery, and keeping the system running in production the whole time.
✔ Step-by-step guide in introducing TDD to Legacy Code
✔ Applying the TDD transformation to Legacy Microservice Architecture
✔ Ask me questions about the challenges you face applying TDD in Legacy Code
Get the 20% discount now - only 1 day left!
Ultimately, we want to reach the TDD Cycles, but we can’t immediately practice them in Legacy Code. It’s a long journey. That’s why I’m writing the transformation steps to reach that level.
Phase 1. TDD in Legacy Code - Pipeline
Before introducing any tests, we must ensure we have a Pipeline. A Pipeline is central to Continuous Delivery, enabling us to test our release candidates to ensure we can safely and quickly release to production. In this phase, we’ll be setting up the Pipeline Stages, with placeholders for tests.
Part 3 - E2E Test Stage (coming up next Thursday - Oct 10th)
Part 4 - Acceptance Test Stage
Phase 2. TDD in Legacy Code - System Level
We want to introduce outermost scaffolding - Acceptance Tests and External System Contract Tests. The Acceptance Tests help ensure that we meet User Story Acceptance Criteria. We need to make minor changes to the system to make it testable at the system level, then retroactively introduce Acceptance Tests for critical happy path scenarios. Then, we have the foundation to apply ATDD for new User Stories and Bug Fixes.
Part 5 - E2E Tests
Part 6 - Acceptance Tests
Part 7 - External System Contract Tests
Part 8 - Acceptance Test Driven Development (ATDD)
Phase 3. TDD in Legacy Code - Component Level
Assuming we have a Frontend and Microservice Backend, we want scaffolding around the Frontend and around each Microservice so that each team is protected as they redesign their component to be more maintainable. We need to make some changes to make these components component-testable and then retroactively introduce Component Tests for at least the most important Acceptance Tests.
Then, we have the foundation to apply TDD for new User Stories and Bug Fixes for each team at the component level.
Part 9 - Component Tests
Part 10 - Contract Tests
Part 11 - Test Driven Development (TDD)
Phase 4. TDD in Legacy Code - Unit Level
Now, we want to apply scaffolding at the innermost level to get the fastest feedback loop when making changes. This is where it gets more invasive - we need to change our internal component architecture to make it unit-testable, preferably introducing Hexagonal Architecture to introduce separation of concerns (business, presentation, infrastructure).
We retroactively add Unit Tests to test business logic and Narrow Integration Tests to test presentation & infrastructure logic, applying this for at least the most critical functionalities. Then, we have the foundation to start applying TDD for new User Stories and Bug Fixes. Furthermore, we also have a high level of safety and fast feedback when Refactoring Code.
Lastly, we’ll also explore how to use Code Coverage & Mutation Testing as we retroactively write tests to ensure they’re safe and how we can use Static Code Analysis for refactoring.
Part 12 - Hexagonal Architecture
Part 13 - Unit Testing
Part 14 - Narrow Integration Testing
Part 15 - Test Driven Development (TDD)
Part 16 - Code Coverage & Mutation Testing
Part 17 - Static Code Analysis
Disclaimer: The series outline above is a rough draft. As I write the articles, I may update the outline (and may take into account reader feedback and questions).
What you’ll get out of this?
You tried reading TDD books and watching TDD courses, but none of them worked in practice because they showed simple demo projects, whereas, at work, you are dealing with a complex Legacy Project. The books and course didn’t show you how to introduce TDD in Legacy Code.
That’s why this TDD in Legacy Code Series will provide you with:
✔ Step-by-step guide in introducing TDD to Legacy Code
✔ Applying the TDD transformation to Legacy Microservice Architecture
✔ Ask me questions about challenges you face applying TDD in Legacy Code
Get the 20% discount now - only 1 day left!
What do our members say?
How is this different?
You’ve already read books about TDD and watched some TDD courses. Perhaps your company also sent you to TDD training and workshops. They showed you how to build a project from scratch with TDD… applying TDD to new project was straightforward.
However, when you go to work the next morning, the production code that you’re faced with is a horrible mess. It’s Legacy Code - it’s untestable. There are no tests. Those books and courses don't tell you what kind of re-architecting and re-design you must do to support testability. They might not even tell you about Acceptance Testing and Component Testing, but just mention Unit Testing. The biggest challenge in introducing TDD in Legacy Code isn’t TDD itself but the painful transformation process to make the Legacy Code ready for TDD.
Who this is for?
Engineering Leaders who want to transform their teams.
As an Engineer Leader, you can use this as a roadmap to help their teams tackle Legacy Code. This step-by-step guide will make your job easier because you can use it to set up milestones and tasks for your teams to help them improve incrementally without overwhelming them.
Furthermore, I help you achieve this safely to incrementally improve system maintainability while ensuring that your teams don’t destabilize the system running in production. This enables them to continue feature development & keep the business happy.
Software Developers who care about quality
You don’t get an opportunity to learn about best practices in your job; all you see is messy code, and no tests, let alone TDD. Your team might not even care about quality, but you do. You don’t have a Technical Coach but want to improve, and the books/courses you bought up to now didn’t give you guidance on how to implement TDD in real projects. You can use this series to learn, try to apply, and share knowledge with colleagues.
How to ask questions about TDD challenges?
This Series will provide you with rough guidance. However, as you try to apply it in your project, you may face additional challenges due to real-life complexities.
As a Premium Subscriber, I’ll be your mentor. As you read the series and try some steps, as you face roadblocks or challenges, you can ask me any questions by commenting on the article. Comments are only available to Premium Subscribers.
About me
My name is Valentina Jemuović. Thanks to my 23,000+ LinkedIn followers, I write posts about TDD, Hexagonal Architecture, and Clean Architecture.
As a Technical Coach, I’ve helped teams worldwide introduce TDD in Legacy Code, and now I want to help you achieve the same:
✔ Deliver User Stories without loads of back-and-forth with QA
✔ Minimize Regression Bugs and fix them quickly if they appear
✔ Improve the design of the system and refactor code safely
By becoming a Premium Subscriber, you can learn step-by-step how to introduce TDD in Legacy Code and ask me questions if you need help along the way.
I've heard many developers say that they have integration and unit tests that span the entire system to check that it's working, so why would they need more tests?