Test-Driven Development, closely associated with Kent Beck, describes a specific, disciplined cycle: write a failing test for a small piece of desired behavior first (red), write the minimum code needed to make that test pass (green), then improve the code's structure while keeping the test passing (refactor, in the specific, behavior-preserving sense discussed elsewhere on this shelf), and repeat for the next small piece of behavior. The order — test before implementation — is the specific, defining detail that distinguishes TDD from simply having good test coverage. A short external definition is available in the Agile Alliance TDD overview.

Why the order matters, not just the eventual coverage

Writing a test first forces a specific kind of thinking that writing tests afterward doesn't: before any implementation exists, the developer has to articulate precisely what the code is supposed to do, from the perspective of something calling it, rather than describing what the code already does after the fact. Tests written after implementation tend to describe the implementation's actual behavior, including any accidental quirks; tests written first describe the intended behavior, which is a meaningfully different and often more useful thing to have documented and verified. Measurement systems invite gaming when the proxy becomes the target; this page catalogs common examples in time tracking.

A commonly cited secondary effect is that code developed test-first tends to end up more testable by construction — because the code has to be callable and checkable before it exists in order to write a test for it, TDD naturally steers away from designs that are hard to test in isolation (tightly coupled to global state, hard to instantiate without heavy setup), the same properties the legacy-code guide on this shelf identifies as making code hard to safely change later.

What TDD is not a substitute for

TDD is a discipline for writing individual units of code against known, specific requirements — it's not, by itself, a design methodology for an entire system's architecture, and it doesn't replace the higher-level design thinking discussed in the architecture guides on this shelf. A common misapplication is treating strict, granular TDD as sufficient for figuring out a system's overall structure; in practice, most experienced practitioners combine TDD at the level of individual functions and classes with separate, more deliberate architectural thinking at a larger scale, rather than expecting the small-scale red-green-refactor cycle to organically produce good system-level structure on its own.

The honest debate about how strictly to apply it

Strict, disciplined TDD — writing the smallest possible failing test, the smallest possible implementation, repeated in very small increments — has genuine, documented advocates and genuine, documented critics within the field, including experienced practitioners who value automated testing highly but find the strict cycle too rigid for exploratory work where the eventual shape of the solution isn't yet clear. A common middle position treats TDD as one valuable tool, well suited to code with clear, already-understood requirements, and less well suited to early, exploratory design work where the requirements themselves are still being discovered through the act of writing code — in which case tests may reasonably follow rather than precede that exploratory phase.

TDD's value comes specifically from the discipline of the order — defining expected behavior before writing the code that provides it — which tends to produce clearer requirements and more naturally testable code, even for developers who don't apply the cycle with strict, unwavering discipline on every single line.

Whether or not a team adopts the full, strict cycle, the underlying habit — being able to state precisely what a piece of code should do before deciding how it will do it — is a transferable discipline that shows up, in a looser form, in most well-tested codebases regardless of exactly when each individual test was written relative to the code it checks.