Skip to content

Do TDD and SDD Contradict Each Other? How to Refactor Legacy Code With AI Without Breaking Anything

Jaime Marco · 2026-10-23

A common question when starting to work with AI agents is whether TDD (writing the test first) clashes with SDD (spec-driven development). The short answer: no. They are different layers and they complement each other, which is especially noticeable when refactoring legacy code.

What Each One Is

SDD puts the specification at the center: you describe what the system must do and the agent works against that description. For it to work you need a harness: a set of files, commands and checks that define how work is done in the project.

TDD is a concrete practice within that work: first a failing test (red), then the minimum code that passes it (green) and finally cleanup (refactor). It does not compete with the specification; it is a way of checking that it is met.

The Legacy Code Case

In a system that already exists, the order changes slightly. Before moving a single line you need to pin down what the system does today, bugs included. That is done with characterization tests: they assert the current behavior, field by field, so that any later change that alters it gets noticed.

It can be jarring to see a test that blesses a data leak or an inconsistent response. It is not a mistake. A refactor, by definition, does not change behavior. If you fix a bug along the way, you will never know whether what broke afterwards was caused by the fix or by the move. Fixing a bug is another task, with its own test and its own decision.

Where a Specification Framework Fits

Another frequent question is why a full specification framework is not applied to every refactor. It is a team decision, not a technical obligation. These frameworks provide traceability and a history of decisions, and they are usually worth it when business requirements vary a lot, but they cost time and tokens. For a bounded task, a custom command with clear steps can be enough.

The useful rule is that the steps of the ritual (explore, branch, characterize in red, go green, refactor, separate bugs, commit) live in a file in the project and not in your head or in every prompt. That way task number forty looks like the first one.

An Honest Limit

A generic refactor command rarely goes down to the full detail of a team: merge strategy, how new endpoints are defined, which API contract is used, which libraries. It is worth reviewing it and completing it with your team's decisions before delegating it.

Quick Summary

TDD and SDD are not mutually exclusive. With legacy code, start by characterizing current behavior, refactor without changing it and leave bugs for separate tasks. Choose the specification framework according to how much your business varies and what it costs.