Your app works every time you click through it yourself. That is the happy path, the run where nothing goes wrong. Real users hit the paths where something does, and an agent writes only the happy one by default. This chapter gets your app to stay trustworthy when the rest of reality shows up.
10.6.1Assume every call can fail
Every time your code reaches outside itself, that reach can fail, and eventually it will. Treat failure as a normal branch you handle, not a rare accident you ignore.
The three you hit first:
- The network drops or times out mid-request.
- The disk is full, or the file you expected is gone.
- A service you depend on is down, or slow enough to look down.
Your agent assumes all three succeed. Your job is to assume they will not. Four defenses handle the reach that fails:
- Set a timeout so a slow service cannot hang your app.
- Retry a brief failure, with exponential backoff: wait longer each try.
- Add jitter, a little randomness on each wait, so clients do not retry in lockstep.
- If a service keeps failing, a circuit breaker stops calling it for a while so it can recover, and you fail fast instead of piling up.
10.6.2Catch the error, never swallow it
When a call fails, an agent often buries it: a catch that returns an empty value and moves on. The app does not crash, so it looks fine, but the failure vanished with no record and the user is left staring at nothing.
Handling an error means two things, always: log the real cause, a written record you can search later, and show the user a message they can act on.
Rule of thumb: every
catchearns its place by doing two jobs, a log for you and a message for the user. An empty one is a silent failure you will hear about from a confused user instead.
10.6.3Cover the edge cases the agent skipped
An edge case is a condition at the boundary of what the code expects, the empty input or the huge one, not the tidy middle it was demoed on. Your agent tests the comfortable middle and skips the edges, so name them and hand them back.
| Edge case | What breaks when you ignore it |
|---|---|
| Empty (no rows, blank field, zero items) | The layout breaks, or the math divides by zero |
| Huge (a 2 GB file, 10,000 rows at once) | The page freezes or the server runs out of memory |
| Concurrent (two people save the same second) | One save silently overwrites the other |
| Malformed (letters where a number goes) | The code crashes on the first bad value |
One more the agent gets wrong by default is not an edge case but a storage decision: keep every timestamp in UTC and convert only at the moment you display it. Store local times instead and your first user in another country sees yesterday's data. The weekend the clocks change, some of it is simply wrong.
Later, QA turns these same cases into tests that re-check them on every change. For now, handling them by hand is the win.
10.6.4Leave data consistent when it breaks
The worst failure is the one that stops halfway. A checkout charges the card, then crashes before it saves the order: the money moved and the record did not. That half-written state is far worse than a clean crash, because nothing tells you it happened.
Group the steps that must all happen, or none, into one transaction. The database runs them as a single unit and undoes everything if any step fails, so a crash leaves the data untouched.
For steps outside the database, like charging a card or sending mail, make each one safe to run twice, so a retry never doubles the damage. That property is idempotency. An idempotency key, a token you send with the request, is how a provider makes a repeated call take effect only once.
This prompt hardens one feature without changing what it does:
Do this now: paste the prompt with your riskiest feature, the one that touches money, files, or another service. Let the agent turn its happy-path code into code that survives a failure.