Where production breaks first
The failures that happy path testing misses fall into five predictable categories.
Each category reveals a different kind of system fragility. Running tests across all five categories before launch does not guarantee the system will never fail. It does mean that the most common failure modes have been designed for rather than discovered under pressure.
Category 1
Missing and incomplete data
Test the system with records that have required fields missing, optional fields blank, and combinations of both. What does the system do when a field it expects to read is not there? Does it fail with an interpretable error? Does it apply a default value? Does it skip the record and log it? Does it crash silently and continue as if nothing happened?
The answer to each of these questions should be the result of a design decision, not an accident of implementation. Missing data is not an edge case in production systems - it is a routine occurrence that the system should handle with explicitly designed behaviour.
Category 2
Out-of-range and unexpected values
Test with values that are technically valid but outside the range the system was implicitly designed for: a negative number where only positive values make sense, a date far in the future, a text field with unusually long content, a numeric field with a value orders of magnitude larger than typical. These are the values that expose assumptions baked into the logic without being made explicit.
Also test with values the system was not designed for at all: a status value that did not exist when the system was built, a new category that was added to the source system after the integration was written. New values that the system does not recognise should produce a specific, diagnosable failure rather than silent incorrect behaviour.
Category 3
Steps completed out of order
Test what happens when the sequence of operations differs from the designed flow. A record updated before it was created. A second trigger fired before the first completed. A status moved backward in the workflow. A step skipped because a user found a shortcut.
Operational systems in the real world are operated by real people who do not always follow the designed sequence. The system should either handle out-of-order operations gracefully or detect them and route them to a human review queue. What it should not do is produce subtly incorrect results that are indistinguishable from correct ones.
Category 4
Dependency unavailability
Test what happens when a dependency the system relies on is temporarily unavailable: the API it calls returns a 503, the database it writes to is unreachable for thirty seconds, the file it reads has not been updated yet when the system runs. The dependency will be unavailable in production eventually. The question is whether the system handles it gracefully or produces a failure that requires manual intervention to resolve.
Transient unavailability should trigger a retry with appropriate backoff. Extended unavailability should trigger a notification to whoever is responsible for the system. The system should never silently drop work because a dependency was temporarily unavailable.
Category 5
Duplicate and repeated triggers
Test what happens when the same trigger fires twice: a webhook delivered twice because of a network retry, a record that matches the trigger condition, gets corrected, and then matches again, a scheduled job that overlaps with a previous run that has not yet completed. Duplicate processing can produce duplicate records, double-charged transactions, or contradictory state changes depending on what the system does.
The system should either be idempotent - running the same trigger twice produces the same result as running it once - or it should detect and reject duplicate triggers. The behaviour should be explicit and tested rather than discovered in production when a webhook retry causes a duplicate order.
The two axes the five categories miss
It works on clean, small, serial data. Production is none of those.
The five categories cover messy input. Two more axes are just as routine in production and just as invisible in a tidy test: size and concurrency. The version that passes on a hundred tidy rows, one request at a time, is the version that fails on a hundred thousand, all at once.
Test at production volume. A loop that compares every record to every other is fine at a hundred rows and unusable at a hundred thousand. Run the real data size before launch, not a sample, or the quadratic surfaces as a timeout in week two.
Test two running at once. A check-then-write that is correct in isolation races when two copies run together: both read "not there", both create, and you have a duplicate. Close the gap with a transaction or a uniqueness rule, and test it under real concurrency.
Watch for the visible middle. Any sequence of separate writes has a moment a reader or a crash can land in. If a half-finished state would be wrong, make the writes one all-or-nothing unit so no one ever sees half of it.
Name the shared shape. All five categories, plus these two, are one lesson: the naive version works on clean, small, serial data, and production is messy, large, and concurrent. That gap is what the tests exist to close.
The production reality
The failures you find in week two of production were all visible in testing, if anyone looked.
Testing beyond the happy path does not prevent every failure. It shifts where failures are found: before launch instead of after, when the fix costs hours instead of days and the impact is zero instead of real.
The testing sequence
Run these in order before launch.
01
Test with bad data
Missing fields, blank optionals, out-of-range values. What does the system do when the data is not what it expects?
02
Test out of order
Steps completed in the wrong sequence. Records updated before they are created. Status moved backward.
03
Test with dependencies down
What happens when the API returns 503? When the database is unreachable for 30 seconds? Does the system retry, alert, or drop work silently?
04
Test duplicate triggers
The same trigger firing twice. A webhook retried. A record that matches, gets corrected, then matches again. Idempotency or detection.
Common questions
Questions about testing operational systems
Happy path testing confirms the system works when everything goes as designed. Production does not always go as designed. Missing data, out-of-order operations, temporary dependency failures, and duplicate triggers are routine occurrences in any system that handles real-world inputs. The failures that appear in the first weeks of production are almost always from one of these five categories, and they are all findable before launch with the right tests.
Enough to have a test for each of the five failure categories, plus a test for the specific failure mode most likely to occur in your system based on its inputs and dependencies. Exhaustive coverage is not the goal, the goal is to have found the failures that would be the most expensive to discover in production. That is usually achievable in half a day for a well-scoped operational system.
Decide explicitly what the system should do when that failure occurs, not just fix the specific test case. Every test failure is an opportunity to design the failure path: should it retry, skip and log, route to a human queue, or halt and alert? Failures that are patched without designing the response tend to surface again in different forms.
