ATDD - Dirty code & architecture is ok
The business wanted it done yesterday - and deployed today.
👋 Welcome to the premium Optivem Journal. I’ll help you apply TDD to escape the nightmare of Legacy Code. Join our paid community of 160+ senior engineers & leaders for support on your TDD journey, plus instant access to group chat and live Q&As:
The business wanted it done yesterday - and deployed today.
The dev is told: “I don’t care how, just get it working.”
But that speed comes at a cost: messy code.
So what do we do?
We take shortcuts. We write dirty code. We skip design decisions. And in many cases - that’s completely okay.
You don’t need to start with Unit Tests
It’s not possible to write unit tests if there’s high coupling between business logic and I/O logic. And even worse - many developers write fragile unit tests that are tightly coupled to internal implementation details. So when the design changes, those tests break, and developers have to rewrite them. And there’s no time for this.
But even effective unit tests break when you change the public API (whereas bad unit tests also break when you change the internal implementation). If you redesign the API, the tests still have to be updated.
So does that mean we have to skip tests entirely?
No — it means we need a different kind of test.
Acceptance Tests Don't Care About Messy Code
Unlike unit tests, acceptance tests stay stable - they only care about user behavior.
Acceptance tests validate user-facing behavior, not developer-facing behavior.
That means, for Acceptance tests:
It doesn’t matter how messy your code is.
It doesn’t matter if your architecture is bad.
As long as the functionality works — the Acceptance test passes.
So even if you're delivering fast and writing quick-and-dirty code to get things working — your acceptance tests still give you assurance that the system behaves correctly.
Clean It Up Later (Without Fear)
Let’s say you come back in a few sprints. You now understand the domain better. You’ve seen how the code evolved. You’re ready to clean up the mess.
Great. Because your acceptance tests are stable, you can redesign, refactor, and re-architect — without breaking the acceptance tests. They still describe the same user-facing behavior, regardless of how the code underneath is structured.
Defer Design Decisions - The Power of ATDD
Start with user behavior and write high-level acceptance tests
Implement quickly - even with “dirty” code
Refactor and improve the design later, with full test protection
That’s why ATDD is powerful: you can ship fast, defer clean-up, and still have confidence your system works.
Protect behavior first - with high-level acceptance tests. Improve your architecture & design later.
Want to Learn How?
How can you introduce acceptance tests into your development process?
See the TDD in Legacy Code series, where we have a section ATDD in Legacy Code.
Want the full roadmap? I'm hosting a workshop breaking down exactly how to implement this. I’ll show you how to align the team regarding requirements and eliminate manual QA regression testing, so that you’ll reduce delivery time and reduce regression bugs.
🚀Join me on Wed 6th Aug (17:00 - 19:00 CEST), I’m hosting ATDD in Legacy Code Roadmap (Live Workshop). During this workshop you’ll learn:
Why ATDD will reduce software delivery time and reduce regression bugs, by aligning the team regarding requirements & replacing manual regression testing
How to introduce ATDD in Legacy Code in an incremental way, while still keeping up with existing feature delivery
How to convince Engineering Managers, Software Engineers and QA Engineers to adopt ATDD in Legacy Code
Let's transform your development process together!
P.S. Regular price: $97 (free access for Optivem Journal paid subscribers - see event description for details)
Clean code takes time - but acceptance tests let you protect behavior first. How do you explain this to developers?