Systeric / Docs
Open App →

Cover the Edge Cases

Code that only works when everything goes right isn’t finished, it just hasn’t met its edge cases yet. The happy path, the typical, everything-works case, is the easy half. The half that separates an engineer from a coder is the deliberate hunt for the inputs and situations that break it: the empty list, the missing field, the number bigger than you expected, the two requests arriving at once, the network call that fails halfway.

Production will find these for you if you don’t. The only question is whether you find them at your desk, where a fix costs minutes, or whether a user finds them at checkout, where it costs an order and a trust hit. Engineers find them first. On purpose.


Think in Inputs, Not Just Flows#

What’s expected: For every input your code takes, you’ve asked what happens at its extremes, not just its typical value.

The happy path imagines a typical input: a discount code that exists, a cart with a few items, a card that works. Reality sends the extremes. The discipline is to walk each input and each step and ask, deliberately, “what’s the nastiest version of this?” A checklist that catches most of them:

Empty & missing
Empty cart, no discount code, null address, a field the API didn't send. What does the code do with nothing?
Boundaries
Zero, one, the maximum, one past the maximum. A discount equal to, and greater than, the cart total.
Malformed & hostile
Wrong type, wrong currency, an expired or fake code, input from someone trying to break it on purpose.
Failure & concurrency
The payment call times out. Two tabs check out the last item at once. What state are you left in?

For the Storefront’s discount feature, that checklist turns up in seconds what production would have found in a week: a code worth more than the cart total (charge goes negative), an expired code (does it silently apply?), currency rounding (does $10.00 − 15% become $8.50 or $8.499999?), and two shoppers racing for the last unit of stock. None of these are exotic. They’re just the questions the happy path skipped.

The same four questions work on any feature. Importing a CSV of products: an empty file, a row with no price, a price with a currency symbol typed into it, a duplicate SKU, a 2GB upload. Different feature, identical discipline, walk the inputs and ask for the nastiest version of each.


Decide What Each Edge Should Do#

What’s expected: For every edge you find, you’ve made a deliberate decision about the behavior, not left it to chance.

Finding the edge is half of it. The other half is deciding, on purpose, what should happen, and that’s a product decision as much as a technical one. A discount bigger than the total: clamp to zero, or reject the code? An expired code: silently ignore, or tell the shopper why? These aren’t for the code to decide by accident. The Define session is where the important ones get settled (“every edge case: what happens when data is missing, when the operation fails”), and when one surfaces mid-build that the doc didn’t cover, you make the call and say why, you don’t leave it to whatever the code happens to do.

Bad: total * (1 - pct) with no guard, a code over 100% quietly pays the shopper.

Good: Math.max(0, total * (1 - pct)), with a test that pins the decision, and a note that a code over 100% is clamped, not honored.


Defend at the Edges, Trust the Core#

What’s expected: You validate where untrusted data enters the system, and you don’t scatter defensive checks everywhere else.

Handling edges does not mean paranoid checks on every line, that’s its own kind of complexity, and it makes code unreadable. The skill is knowing where to be defensive: at the boundaries, where data enters from a user, an API, or another system, you validate hard and fail clearly. Once data is inside and validated, the core can trust it. Validate the discount code once, when it arrives; don’t re-check it in every function that touches the cart.

How to get there: Draw the line between “untrusted outside” and “trusted inside.” Push validation to the boundary, reject bad input early with a clear error, and let the interior stay clean. A function deep in the core that defends against a null that can’t reach it isn’t safe, it’s noise, and it hides the checks that actually matter.


Edges Are Where Tests Earn Their Keep#

The happy-path test proves the feature exists. The edge-case tests prove it’s correct, and they’re the ones that catch the regression (a bug that sneaks back into code that used to work) six months from now when someone refactors the discount logic. Our testing discipline is explicit that the edge cases from the definition doc get tests, not just the happy path. Every edge you decided on above becomes a test that pins that decision in place, so the next person can’t quietly undo it. An edge case you found, handled, and tested is worth ten you’ll discover in an incident.


Next: One Change, One Place