Architecture & Methodology
Official siteTest-Driven Development
TDD — red, green, refactor. Tests first at the unit level, integration and contract levels around the seams that matter.
Overview
TDD is how I turn uncertainty into feedback. Writing a failing test first forces a decision about behaviour before implementation, and the red-green-refactor loop keeps the design honest and the suite protective.
The testing pyramid shapes where effort goes: many fast unit tests, focused integration tests against real seams like databases and HTTP, and a thin layer of end-to-end coverage. Chasing 100% coverage is not the goal — protecting behaviour is.
Beyond the mechanics, TDD composes with DDD and CQRS: domain logic is pure and testable, while infrastructure is covered at the integration level. The result is a suite that is fast enough to run constantly and precise enough to localise failures.
The red phase is a design act, not a chore. Writing the failing test first forces a decision about what "done" means, which API the code should expose, and what the edge cases are — long before any implementation has a chance to drift.
I keep the feedback loop fast. Tests at the unit level run in milliseconds and gate every commit; integration tests hit real seams to catch contract breaks; a thin end-to-end layer proves the whole path. The pyramid is how I keep the fast loop fast.
Coverage is a by-product, not a target. A metric that can be gamed produces tests that assert smoke, so I measure whether the suite protects the behaviours that matter rather than chasing a percentage that proves nothing.
Contract testing earns its place at service boundaries. Consumer-driven contracts let each side of an API evolve against an agreed expectation, so a microservice change cannot silently break its consumers in a hard-to-reach e2e test.
TDD composes with the architecture I already reach for. Pure domain logic tested in isolation, infrastructure behind thin seams tested at the integration level, and the result is a suite that makes refactoring safe and confidence cheap.
The discipline compounds: a suite that runs fast and fails precisely is the thing that lets a team refactor boldly, ship small, and move without fear. That is the real return on TDD — not a ritual, but the courage to change code confidently.
How I Work With Test-Driven Development
I write the failing test as the design act. It forces a decision about the intended behaviour and the exposed API before implementation has a chance to drift.
The loop is red, green, refactor, kept genuinely short. A test run in milliseconds gates each step, so the feedback stays tight and the discipline stays habit rather than ceremony.
The testing pyramid shapes effort. Many fast unit tests, focused integration tests at real seams like the database and HTTP, and a thin end-to-end layer — coverage is about protecting behaviour, not chasing a percentage.
I isolate the pure, valuable logic. Domain rules are tested in complete isolation, infrastructure sits behind thin seams, and integration tests cover the actual boundaries.
Contract tests guard service seams. Consumer-driven contracts let each side of an API evolve against an agreed expectation, so a microservice change cannot silently break its callers.
Tests are a design tool, not just a net. A hard-to-test module is a signal of coupling, and I let the friction point to where to extract.
Mutation testing, where useful, finds the gaps. I verify the suite actually catches bugs rather than asserting smoke, so the passing suite means something.
CI runs the suite on every push. The fast, precise failures it gives lets a team refactor boldly and ship small, which is the real return on TDD.
I keep the suite fast and deterministic. A slow or flaky test loses all value, so speed and reliability of the tests are maintained like the product itself.