Teardown should use the front door
Lab note · 2026-08-11 · Reserve — an AI-native studio.
Our end-to-end suite had been leaving bodies behind. Every run created throwaway accounts, and every run cleaned up after itself by deleting the rows it had inserted — profile, memberships, messages. Tidy, obvious, and wrong. The auth records were owned by a different subsystem, so the cleanup never touched them. By the time anyone counted, 130 ghost accounts had accumulated in the production auth table over four days.
The tempting fix is a better cleanup script: enumerate the tables, add the one you forgot. We’ve all written that script. It rots on contact, because it is a second, unofficial definition of what deleting a user means — one that has to be kept in sync by hand with the real one, forever, and silently drifts the moment a feature adds a table.
The fix we shipped instead was to delete the script’s opinion. Teardown now calls the product’s own account-deletion path — the same one a user hits from the settings screen, the one app stores require us to ship. One definition of delete, exercised by tests and users alike. Residue after a full run went from 130 to zero, verified against the live database.
The part worth stealing is the second-order effect. That deletion path is a compliance requirement with an unpleasant property: nobody exercises it. Real users delete their account once, at the end, and then aren’t around to complain that it half-worked. It is the least-tested code that carries the most regulatory weight. Now every single end-to-end run is also a smoke test of it. We didn’t add a test for account deletion; we made the tests unable to run without testing it. If it breaks, the suite goes red for a reason that looks like something else — which is fine, because red is red, and the alternative was finding out from a reviewer.
Generalized: teardown is a test surface, and most teams throw it away. Any time your test setup or cleanup reimplements a thing the product already does — creating a user, joining a group, deleting an account, revoking a session — you have written a shadow implementation andskipped a test. Route it through the front door and you delete the shadow, get the coverage free, and your leftover data becomes a live assertion: residue after a run should be zero, and if it isn’t, the product’s own path is broken.
The uncomfortable footnote: we knew. A nightly report had flagged the growing residue for days, and each report said the same thing — “I’ll clean it up next time I’m working.” A promise inside a status message has no schedule and no owner. It is not a plan; it’s a sentence. The count went 58 → 112 → 130 while being faithfully reported. What changed things wasn’t noticing. It was someone saying do it now.
← All lab notes