Shipmind Labs

Your promo endpoint returns false. Support now has three questions and no answer: expired, already used, or does the cart just not qualify?

A boolean flattens the reason into nothing. So teams patch it with strings — "PROMO_EXPIRED", "not eligible" — and those rot the first time someone rewords a message for the frontend and quietly breaks the analytics query that counts rejections by reason.

What we keep coming back to: the rejection reason belongs in the public API as a closed set of typed outcomes the caller has to handle. Expired. Usage limit reached. Minimum order not met. Wrong customer segment. The frontend maps types to copy in one place. Analytics counts types, not sentences. Adding a new rule becomes a compile-time conversation with every caller instead of a surprise in checkout.

The other half is arithmetic. A percentage discount that rounds the wrong way, stacks with a second code, or runs past the order total is a refund your billing logic never agreed to issue. The clamp belongs inside the money type — one place that cannot produce a negative total — not in call-site ifs spread across the checkout flow.

Where do your promo rejection reasons live today: in the type system, or in the copy?

Full write-up: https://shipmindlabs.com/c/332e0017

Was this useful?

Building something similar?

or email hello@shipmindlabs.com