Every time I hit a bug painful enough to remember, I've started asking the same question: how do I make sure my AI coding assistant never reintroduces it, even months from now, even in a repo it's never seen? Writing it down in a notes file isn't enough — notes get skimmed, context gets summarized, details get lost. So I built a small set of hooks that block specific, known-bad patterns outright, before they ever land.
The idea: hard-won lessons should be enforced, not remembered
A memory note is a suggestion. A hook that runs on every file write and blocks the write with a nonzero exit code is a rule. I only promote something to a hard rule once it's actually cost me real time — this isn't a wishlist of best practices, it's a short list of specific incidents.
Right now that list blocks things like:
- A specific dependency-import pattern that once pulled ~9,000 modules into a dev server's compile graph and hung an entire VM (the exact "convenient" import path stays blocked; importing the same package normally is fine).
- Container state getting bind-mounted into a repo's working tree as a root-owned directory — it silently breaks file watchers and formatters, and looks like an unrelated tooling bug when it happens.
- Writing private key material into any file.
- Wildcard or reflected CORS origins paired with
credentials: true. - Auth tokens written to
localStorage/sessionStorage. - An RNG seed derived from the system clock, anywhere near code that needs to be fair or unpredictable.
Each of those started as "I can't believe that just happened" and ended as four lines in a hook script.
Guarding against a different failure mode: false confidence
The blocking hook stops bad patterns from landing. A separate hook nudges against a subtler failure: declaring something "done" when it hasn't actually been proven. If there are uncommitted source changes in a git repo, it reminds me — once per session, not naggingly — to actually typecheck, run tests, and prove the change works end to end before calling it finished. A model (or a tired human) will happily say "this works" based on the code looking right. Proof is a different bar than plausibility.
Specialists instead of one generalist re-deriving rules every time
The other piece: instead of one assistant trying to hold every domain's rules in its head simultaneously, I split some review responsibilities into narrow specialist reviewers, invoked only when relevant — one that only checks money-handling invariants (does every mutation go through a single audited path, is anything computed in floats that shouldn't be), one that only reviews smart contracts, one that only checks a specific web framework's performance footguns, one that only checks auth/session patterns against a hardened standard. Each one is narrow on purpose. A generalist asked to check "everything" tends to check nothing very deeply; a specialist asked to check one thing tends to actually catch it.
What I'd tell past-me
- If something costs you real debugging time once, don't just write it down — figure out if it can be turned into something that mechanically blocks itself from happening again.
- Keep the blocklist short and specific. A rule for "don't do the exact thing that hurt me" is enforceable; a rule for "write good code" isn't.
- Separate "prevent bad patterns" from "verify claimed completion" — they're different failure modes and deserve different guardrails.
- Narrow, specialized reviewers beat one reviewer trying to hold every domain's rules at once.
Top comments (0)