Skip to content
Courtix
August 27, 2026

How we write tests worth trusting

The line between coverage theater and tests that actually catch regressions: what we test, what we deliberately skip, and how we decide.

Every client asks about test coverage at some point, usually by naming a percentage they’ve heard is good. We don’t chase that number. A suite can hit 95% coverage and still let a real bug through, and a suite half that size can catch every regression that matters. Coverage tells you what ran, not what was checked. This post is about the difference, and how we write tests that earn the confidence people put in a green CI run.

Coverage is a smell, not a goal

A coverage report tells you which lines executed during a test run. It says nothing about whether the test asserted anything meaningful when those lines ran. We’ve seen suites where a test calls a function, catches every exception, and asserts true—100% coverage, zero protection. We use coverage the way we use a linter: as a signal to point us at unexamined code, not as a target to optimize for its own sake. If a number is dropping in a PR because a genuinely untested branch got added, that’s worth a conversation. If it’s dropping because someone deleted a dead code path, it isn’t.

We test behavior, not implementation

The question we ask before writing any test is: what would have to be true about the code’s outward behavior for this test to fail? If the answer involves a specific internal function name, a private method, or the shape of an intermediate variable, we rewrite the test. Tests coupled to implementation break every time someone refactors, which trains a team to treat red CI as noise rather than signal. Tests coupled to behavior—given this input or this sequence of actions, the system produces this output or this side effect—survive a rewrite and still mean something a year later.

The bug you just fixed gets a regression test, no exceptions

Every bug fix ships with a test that reproduces the original failure and would fail against the old code. This is the single highest-leverage rule in our testing practice, and it costs almost nothing to follow. It converts "we hope this doesn’t come back" into "we’d know within a minute if it did." Over the life of an engagement, this is how a test suite ends up encoding the actual failure history of the system, not a generic checklist someone wrote on day one.

Where we put the effort: the failure-prone edges

Most bugs don’t live in the happy path—they live at boundaries. We concentrate testing effort on:

  • Inputs at the edge of valid: empty collections, zero, negative numbers, maximum lengths, unicode in text fields, timestamps at DST transitions.
  • State transitions with more than one caller: anything that can be triggered concurrently, retried, or called out of order.
  • Money, auth, and anything irreversible: payment amounts, permission checks, deletions, and anything else where "we’ll just fix it in prod" isn’t an option.
  • Integration points with systems we don’t control: third-party APIs, webhooks, and anything that can change shape without our code changing.

The happy path still gets covered, but it’s rarely where the interesting bugs are, so it isn’t where we spend the marginal hour.

What we deliberately don’t test

We don’t write tests for framework behavior, third-party library internals, or straightforward pass-through code with no branching. A getter that returns a field doesn’t need a unit test; the type system and a compile step already cover it. We also avoid snapshot tests of large, frequently-changing UI trees—they fail on every cosmetic change, train reviewers to click "update snapshot" without reading the diff, and stop being a signal for anything. Every test in the suite should be a test someone would notice go missing. If nobody would, it’s not pulling weight, and it’s slowing the suite down for the tests that are.

Flaky tests get fixed or deleted within a sprint

A test that fails intermittently for reasons unrelated to the code under test is worse than no test at all, because it teaches the team to re-run CI instead of investigate. We track flaky tests the moment they’re noticed and either fix the root cause—usually a timing assumption, shared state between tests, or a real race condition the flakiness is exposing—or delete the test. A quarantined-but-never-fixed test is a lie the suite tells about its own coverage.

Why it matters

A client handing us a codebase, or inheriting one we built, needs to trust that green CI means the software actually works—not that it merely executed. That trust is what lets a team ship on Friday afternoon, refactor with confidence, and let an AI agent draft a PR without every reviewer re-deriving correctness from scratch. It’s the same discipline behind how we ship to production and how we keep AI-generated code reviewable: tests are one of the few artifacts that outlive the person who wrote them, so we hold them to that standard.