← All Articles

Error Prevention Checklist: Stop Bugs Before Production

At a Glance
Same bug classes keep returningIncidents trace back to config or error pathsWant a pre-release sanity pass

Most production errors come from five families: nulls, boundaries, configuration, concurrency, and error handling. Each has one habit that kills most instances.

The Five Families and Their Habits

FamilyTypical errorPrevention habit
Null/undefinedCannot read properties of undefinedValidate at boundaries; fail fast on missing required data
BoundariesIndexError, off-by-one, empty arraysTest with 0, 1, and max-size inputs — every time
ConfigurationWorks locally, 500 in prodFail at startup on missing config, never at first request
ConcurrencyRace conditions, stale readsIdentify shared mutable state; make critical sections explicit
Error pathsSwallowed errors, cascading failuresEvery catch must log with context, or rethrow

Before Writing Code

  • Define what "done" looks like for edge cases — empty list, missing field, duplicate submission, zero quantity: decide now, not when the bug arrives.
  • Know your failure mode for each integration: if this API times out, do we retry, queue, or fail the request? Undecided = undefined behavior under load.
  • Write the one weird test first when the logic is tricky — the test that encodes the edge case.

While Writing Code

  • Boundaries get explicit validation. Parse and validate external input once, at the edge (API handler, file reader, message consumer) — then trust it internally. Scattered defensive checks mean nobody knows where the truth is enforced.
  • Fail fast with a useful message. throw new Error('JWT_SECRET is required; set it in .env') at startup beats a mystery 500 on the first login attempt an hour later.
  • Prefer immutability for shared state. Most concurrency bugs are "two writers to one object"; const-by-default and copy-on-write remove the category.
  • Every network call gets a timeout. The default is often no timeout, which turns a slow dependency into your outage.
  • No empty catch. If you must swallow, log at minimum a metric — silent failures are the most expensive kind.

Before Committing

  • Run the test suite with the new edge-case test included; a red suite is information, not an obstacle.
  • Check the diff for leftover debug code, commented-out logic, and TODOs without owners.
  • Ask of the diff: "what happens when the input is null / the list is empty / this runs twice?" If any answer is "unclear", that's a bug you just got a free preview of.

Before Deploying

  • Config checklist: every env var read by the app exists in the target environment (automate this check — a startup validation block takes ten minutes and pays forever).
  • Migrations are backward-compatible: deploy code that works with both old and new schema before dropping anything. Two-phase migrations prevent the classic "deploy broke the old pods" incident.
  • Know the rollback: what exactly do you run if this deploy misbehaves, and has anyone run it before? An untested rollback is a plan, not a capability.
  • Watch the first five minutes: error rate, latency, and one business metric — deploy verification is a habit, not a formality.

The Test-Case Checklist That Finds Real Bugs

For any function taking a collection, these five inputs catch a disproportionate share of bugs: empty, single element, many elements, all-identical elements, and elements that hit the boundary values (0, negative, max int, very long string). For any time-dependent code: midnight, month-end, leap day, and DST transitions. Teams that add these as table-driven test cases stop re-fixing the same off-by-one every quarter.

Monitoring as Prevention

Some errors cannot be prevented, only detected fast: alert on error-rate changes (not absolute counts — traffic varies), on queue depth growth, and on the business metric that drops when checkout breaks. A five-minute detection window converts an incident into a blip; the same bug unnoticed for a day becomes a postmortem.

The One-Line Summary

Validate at the edges, fail loudly at startup, test boundaries deliberately, and let no error pass silently — those four habits prevent more incidents than any tool you can buy.

Related Errors

Systematic debugging workflow · HTTP status codes guide