Test Once, Measure Always
Tests prove known cases before release. Measurement tells you what reality is doing after release.
Happy-path tests prove the system works when nothing goes wrong. Failure testing proves it survives when things do. Observability keeps checking that claim in production, where the world is larger than your test suite.
Start with a hypothesis:
This checkout flow does not double-charge, double-fulfill, or strand paid users.
Then try to disprove it.
Test the Unhappy Paths #
- Assert on error branches, not just success.
- Simulate timeouts, 5xx, malformed input, and empty/huge payloads.
- Cover permission denied, not-found, and conflict cases.
- Assert that the user sees the right state and that their work is safe.
Fault Injection #
- Mock dependencies to fail on demand.
- Inject latency, dropped connections, and partial responses.
- Verify retries, fallbacks, and circuit breakers actually fire.
For checkout, simulate Stripe timing out after the session was created, a webhook arriving twice, fulfillment failing after payment succeeds, and the success page loading before the webhook arrives.
Chaos Engineering #
- Deliberately break things in staging (or prod) to build confidence.
- Kill instances, sever network links, exhaust resources.
- Start with a hypothesis and a small blast radius.
Boundary & Property Testing #
- Edge cases: zero, negative, overflow, unicode, timezones.
- Property-based tests to explore inputs you'd never enumerate.
- Fuzzing for parsers and anything touching untrusted input.
Recovery Testing #
- Test restart/replay: does the system recover cleanly?
- Verify idempotency by running an operation twice.
- Backup restores and failover drills — practice before you need them.
Measure the Same Claims #
You cannot test every real-world failure. You can measure whether the important claims are holding.
- Count paid-but-not-fulfilled orders.
- Count duplicate webhook deliveries and duplicate fulfillment attempts.
- Track checkout error rate, payment decline rate, webhook lag, and fulfillment lag.
- Track lost shipments, delayed shipments, refunds, replacements, and disputed deliveries.
- Alert when the rate or age of unresolved failure states crosses a threshold.
These measurements are not vanity metrics. They are production tests that run all the time.
Logs, Metrics, and Traces #
- Logs capture discrete events and decisions. Use structured fields and correlation IDs.
- Metrics show rates and trends: error rate, latency, queue depth, retry count, stuck orders.
- Traces show one request or workflow across boundaries.
Log at boundaries and decisions: session created, webhook received, duplicate ignored, fulfillment retried, reconciliation repaired an order. Avoid noisy logs that say little, and never log secrets or unnecessary personal data.
Close the Loop #
Every incident should leave the system sharper:
- Add a regression test for the failure if it is repeatable.
- Add a metric if it can happen again in production.
- Add an alert if humans must act.
- Add a dashboard or report if support needs to explain it.
- Remove alerts that are not actionable.
Front End vs. Back End Notes #
- Front end: test loading/error/empty/offline states; throttle the network in tests; assert the UI offers a way forward and preserves user input.
- Back end: contract tests at boundaries; integration tests with dependencies failing; replay tests for events and webhooks; metrics for stuck or inconsistent states.
Related chapters: Failure Modes · Do or Do Not. Then Retry. · Writing Insanely Great Error Messages