A customer cancels a flexible ticket ten days before the event. Last year, that's a full refund. This year, flexible fares need fourteen days of notice, so it's a $3.50 fee if they're lucky. Same fare type, same cancellation window, different year, different answer.

Ask support which policy applies to a ticket bought back in November and you'll get a shrug. Ask the software and you'll get whatever the current if statement says, because in most codebases that's all that's left of the old policy. Somebody edited it, and the old version just isn't there anymore.

That's not a bug. It's how almost every business rule gets written, and it works fine right up until the rule changes.

One version, and only one version

Pull up the code behind a pricing tier, a discount, a refund policy, an eligibility check, and you'll usually find the same thing: a method with a few if statements encoding whatever the policy happens to be today. When the policy changes, someone edits that method in place. New numbers go in, old numbers come out, and there's no record left behind, because a Git diff isn't something anyone thinks to check in the middle of a customer dispute.

That's fine as long as nobody asks about the past. But businesses get asked about the past all the time. A customer disputes a refund from four months back. An auditor wants to know why two orders placed a week apart got charged differently. The new hire has to explain a discount that makes no sense under today's rules, because it was correct under rules that don't exist anymore.

The code can't answer. Nobody ever asked it to remember.

Teaching the rule to remember itself

Laravel News covered a package this month called Laravel Rulebook that's a good illustration of the alternative. Instead of one rule that gets edited over and over, each version of a policy is its own class with a validity window. The 2025 refund policy runs from January 1, 2025 to January 1, 2026. The 2026 policy picks up right where it left off. You're not asking "what's our refund policy" anymore, you're asking "what was our refund policy on this date, for this ticket," and the system works through every candidate rule in order, keeping track of why each one applied or didn't.

That second part is the one that actually matters. A rule that doesn't apply says why: insufficient notice, wrong fare type, outside the policy's window. If two rules genuinely conflict and the system can't pick a winner, it throws an error instead of quietly guessing. A system that can only tell you what it decided isn't the same as one that can tell you why, and only one of those holds up when someone pushes back.

You can freeze the whole decision into a record and store it next to the order. Ask about it six months later and nobody has to guess.

Where this shows up outside of refunds

This isn't really about refund policies. It's about any rule that changes on a schedule while the software only remembers the newest version. A promotional rate that expired shouldn't still apply, but it also shouldn't invalidate the orders placed while it was live, and most systems can't do both cleanly at once. Tax rates and thresholds change by date and sometimes by jurisdiction, and software that hardcodes "the current rate" falls apart the moment you have to reprocess something from before the change. Overtime rules, shift differentials, staffing eligibility, all of it shifts with policy updates, and a payroll dispute six months out needs the rule that was true then, not the rule that's true now.

None of these fail loudly. They just answer confidently, with the wrong version of the truth, and nobody notices until someone asks a question the system was never built to answer.

What we ask before we build anything

When we're scoping custom software with a client, this is one of the specific questions on the list: which of your rules are already scheduled to change, or already have, without the software noticing? It's related to something we ask on every project (see the decisions your software needs to make), but it earns its own line item, because "what changed and when" is a different problem than "what should happen right now."

If you've got a pricing rule, a refund policy, or an eligibility check that's changed in the last year, and your software genuinely can't tell you what applied before the change, that's not an abstract audit risk. It's a support ticket that hasn't landed yet. Custom development that treats a business rule as data with a history, instead of a line someone edits in place, is what keeps that ticket from turning into a dispute.

Your rules already have a history whether the software knows it or not. The only real question is whether you can get to it when someone asks.