Engineering

Defensive Coding: Writing Code That Refuses to Break in Production

15 September 20266 min readBy the Addvaluez team

Production systems rarely break where the code is clever. They break where the code trusted something it should not have: a response that never came, a field that arrived empty, a webhook that fired twice. Defensive coding is the discipline of trusting nothing, and it is the least visible reason our platforms hold 99.9% uptime.

This article explains the practice in plain terms. If you run a business, it tells you what to demand from whoever builds your software. If you are technical, it is a checklist you can hold against your own codebase.

Validate at every boundary

Data crosses boundaries constantly: from the browser to your API, from a third party into your system, from one service to another. Defensive code validates at every crossing, with a schema that states exactly what is acceptable, and rejects everything else loudly. We use schema validators at the edge of every service we build, so bad data is refused at the door instead of discovered three tables deep a month later.

The business translation: data corruption is the most expensive class of bug, because by the time you notice, the bad data has spread. Validation at boundaries is how you keep it from entering at all.

Design the failure path first

Every external call your system makes will eventually fail. The gateway will time out, the insurer's API will return nonsense at 2am, the network will drop mid-request. Amateur code handles the success path and improvises the rest. Defensive code designs the failure path as carefully as the feature: timeouts on every call, retries with backoff where retrying is safe, and a clear answer for the user when the dependency stays down.

Our motor insurance platform talks to more than ten insurers, which means more than ten independent ways for a quote request to go wrong. It returns quotes in under two seconds anyway, because a slow or broken insurer degrades one row of results instead of the whole response. That behaviour was designed, and it is the difference between a hiccup and an outage.

Make duplicate work harmless

Payments, webhooks, and queues all share an awkward truth: the same message can arrive twice. Defensive systems make repetition harmless, so charging a customer, issuing a policy, or sending a notification checks whether the work was already done before doing it again. The technical term is idempotency. The business term is "nobody gets billed twice."

Let the type system carry the load

We build in strict TypeScript across our stack, which turns a whole category of production crashes into red underlines on a developer's screen. A function that might receive an empty value must say so, and every caller is forced to handle it before the code will even compile. This costs nothing at runtime and removes the single most common JavaScript failure from production entirely.

Fail fast, fail loudly

When something is genuinely wrong (a missing configuration value, a database the service cannot reach), the worst response is to limp along half-working. Defensive services check their requirements at startup and refuse to run without them. A deployment that fails in thirty seconds during a release window is an inconvenience. A service that starts successfully and quietly writes garbage for six hours is a crisis.

What to ask your development team

You do not need to read the code to audit this. Ask three questions. What happens when your main third-party dependency goes down? What happens when the same payment confirmation arrives twice? Where does the system validate incoming data? Teams that practise defensive coding answer immediately and specifically, because they made those decisions on purpose. This is also a fair question to put to us: how we build web applications describes the standards, and our case studies carry the uptime numbers those standards produce.

Frequently Asked Questions

Does defensive coding slow development down?

It slows the first week and speeds up every week after. Validation, typed contracts, and explicit failure paths cost hours upfront and prevent the production incidents that cost days. Across a project, teams that code defensively ship faster because they debug less.

Is defensive coding the same as writing lots of tests?

They overlap but differ. Tests check that the code does what you expect. Defensive coding makes the code behave sensibly when reality does something you did not expect: a malformed payload, a slow third party, a duplicate webhook. You need both.

How do I know if my existing system is fragile?

Ask your team what happens when the payment gateway times out, when the same webhook arrives twice, and when a user submits a form with a field your validation never considered. Confident, specific answers mean a defended system. Silence means an audit is worth commissioning.

Have a Project in Mind?

Tell us what you're building and we'll respond within 24 hours.