In short
Test-driven development is a practice in which a developer writes a small failing test describing the next piece of behaviour, writes the minimum code to make it pass, then refactors while keeping tests green, repeating in cycles of minutes. It produces a test suite as a side effect, but its main effects are on design, since code written to be testable tends to be modular, and on scope, since each cycle forces a decision about what the code should do. It suits logic-heavy code with clear inputs and outputs; it fits badly for exploratory work, UI layout and glue code, where testing after or not at all is often the better trade.
The loop
Red. Write a test for the next small thing the code should do, and run it. It fails, because the code does not exist yet. The failure confirms the test is testing something.
Green. Write the simplest code that makes the test pass. Not the best code, not the general solution; the least that satisfies the test.
Refactor. With the test green, improve the code: remove duplication, name things properly, restructure. The tests catch any change in behaviour.
Repeat, in cycles of a few minutes each. Kent Beck formalised the practice in the late 1990s as part of Extreme Programming, and the loop has not changed since.
Why the test comes first
Writing the test first forces three things. You decide what the code should do before deciding how, which is the specification step that gets skipped when the code comes first. You write code that can be tested, which means small units with clear inputs and outputs and dependencies that can be substituted, which is also the description of well-structured code. And you stop when the tests pass, which limits the tendency to build what might be needed rather than what is.
The test suite that results is comprehensive by construction, since every behaviour was written as a test before it existed. That suite is what makes later change safe: refactoring, upgrades and new features can be made with confidence that nothing else broke, which is the reason tests are worth keeping in the first place.
What the evidence says
Studies of TDD in industry, including work at Microsoft and IBM, have reported defect density reductions of 40% to 90% for teams practising it, alongside increased initial development time of 15% to 35%. Later academic reviews found the effects real but smaller and more variable than the early reports, with the benefit depending heavily on the developers' experience with the practice and the type of code. The consistent finding is fewer defects and better-structured code at a cost in upfront time, with the cost recovered over the life of code that changes.
Where it fits
Business logic: pricing rules, validation, calculations, state machines, parsers, anything with defined inputs and expected outputs. APIs and services, tested through their contracts. Bug fixes, where writing a test that reproduces the bug first is the single most useful application of the practice, since it proves the fix and prevents regression. Code that will live long and change often, where the suite pays for itself repeatedly.
Where it struggles
Exploration. When you do not yet know what the code should do, a test written first is a guess that will be thrown away. Spike first, then test the design that emerges.
User interfaces. Layout, styling and visual behaviour are poorly described by unit tests; visual regression and end-to-end tests suit them better, and those are not written first.
Glue code. Wiring frameworks, configuration, thin controllers that call one thing. The test would be a restatement of the code.
Heavy external dependencies. Code whose behaviour is mostly a database or an API, where the test either hits the real thing slowly or mocks it into meaninglessness.
Prototypes meant to be discarded. Tests for code that will not survive are cost without return.
In these cases, testing after, testing at a different level, or not testing is the better trade, and insisting on TDD produces brittle tests that slow the team without protecting it.
Validation
State machines
Layout and visual behaviour
Glue code and thin controllers
TDD and simply having tests
A team can have excellent test coverage without TDD, by writing tests after each piece of code. The suite protects change either way. What TDD adds is the design pressure and the scope discipline, and what it costs is the rhythm, which some developers find natural and others find obstructive. Both approaches beat no tests; the choice between them is about how a developer thinks, and mature teams usually contain both, with TDD concentrated on the logic-heavy parts.
Adopting it
Start with bug fixes: reproduce the bug in a test, fix it, keep the test. Then apply it to new pure logic, where the loop is easiest. Keep cycles short; a test that takes an hour to make pass is too large. Refactor in the green phase every time, or the code accumulates the crude first versions. Do not chase coverage numbers; coverage measures lines executed, not behaviour verified. And allow the team to test after, or differently, where TDD does not fit, without treating it as failure.
TDD is one of the few practices where the discipline itself improves the code, not just the safety net around it. Applied where it fits, it is worth its overhead; applied everywhere as dogma, it is where teams learn to resent testing. If your codebase has tests nobody trusts, or none, book a call; the first day of TDD on the bug backlog usually makes the case on its own.
Common questions
What is the red-green-refactor cycle?
The core loop of test-driven development: write a small test for the next behaviour and watch it fail (red), write the minimum code to make it pass (green), then improve the code while the tests stay passing (refactor), and repeat in cycles of a few minutes. The failing test first proves the test is meaningful; the green phase limits scope; the refactor phase keeps quality up.
Is TDD worth it?
For logic-heavy, long-lived code that changes often, yes: studies report substantially fewer defects and better-structured code at a cost of 15% to 35% more initial development time, recovered over the code's life. For exploratory work, user interface layout, thin glue code and throwaway prototypes, the overhead outweighs the benefit and testing after or differently is the better trade.
What is the difference between TDD and unit testing?
Unit testing is writing tests for small units of code, whenever they are written. TDD is a practice of writing each test before the code it tests, in short cycles, so that the test drives the design and scope. A team can have thorough unit tests without TDD; TDD produces unit tests as a by-product along with its design discipline.
Does TDD slow down development?
Initially, by roughly 15% to 35% according to industry studies, because tests are written for every behaviour before the code. The time is recovered when the code changes, since the suite catches regressions and makes refactoring safe, and through fewer defects reaching production. On code that never changes, the investment is not recovered.
How do I start with test-driven development?
Begin with bug fixes: write a test that reproduces the bug, watch it fail, fix the bug, keep the test. Then apply the loop to new pure logic such as validation and calculations, where inputs and outputs are clear. Keep each cycle to minutes, refactor every time the tests are green, and allow other approaches where TDD fits badly.