Why I Didn't Block Every Merge on 35 Pre-Existing Type Errors
Blocking on pre-existing errors would have destroyed adoption of the new CI pipeline.
· 2 min read

# Why I Didn't Block Every Merge on 35 Pre-Existing Type Errors
Standing up the first CI pipeline for an inherited codebase, the obvious move is to add a build gate and a typecheck gate and require both green before anything merges. I added the build gate. I made the typecheck gate deliberately non-blocking, and that was the right call — but it's worth explaining why, because "don't gate on the type errors" sounds like cutting a corner until you look at what was actually in the codebase.
## What the audit found
The inherited application carried roughly 35 pre-existing TypeScript errors, accumulated over however long the codebase had gone without a working CI pipeline at all. None of them were newly introduced — they were baseline debt, present before I touched anything, scattered across files with no clean way to fix all 35 without a dedicated pass that had nothing to do with whatever the next feature branch actually needed to ship.
## The actual choice
A hard-blocking typecheck gate on day one means the first PR after CI goes live can't merge — not because it's wrong, but because it happens to touch a file with a pre-existing, unrelated error. That trains people to route around the gate, disable it, or add sweeping ts-ignore comments just to get unstuck, which is worse than not having the gate. So I split the two: the build gate blocks, because a build that doesn't compile is an unconditional problem. The typecheck gate runs and reports, visibly, but doesn't block — so the 35 existing errors are tracked and visible without holding every unrelated change hostage to a cleanup that hadn't happened yet.
## What this is actually a bet on
This is a gate placed at the achievable bar, not the ideal one, with a plan to tighten it later once the baseline is paid down rather than pretending the baseline doesn't exist. A CI setup that can't be adopted because it's too strict for the code it's protecting isn't stricter in practice — it's just off, because someone will disable it under deadline pressure. Gating on what a team can actually hit gets you a gate that survives contact with a real deadline; gating on the ideal gets you a gate that gets bypassed the first time it's inconvenient, which is strictly worse than never having shipped it.

