Let’s say you’re building a SaaS app with a premium feature — say, exporting reports.
When you hard-code entitlements, the logic might look like this inside your backend:
if company.plan in ['pro', 'enterprise']:
allow_export = True
else:
allow_export = False
Seems simple, right?
But now a customer on the ‘starter’ plan negotiates with sales to get export access. So your developer hacks in a one-off override:
if company.id == 12345:
allow_export = True
elif company.plan in ['pro', 'enterprise']:
allow_export = True
else:
allow_export = False
Then Product adds a new tier.
CS needs to enable exports temporarily for a few accounts.
Marketing wants to run a limited-time trial.
Pretty soon you’re scattering logic like this everywhere:
if (
company.plan in ['pro', 'enterprise', 'growth']
or company.id in [12345, 67890]
or company.trial_flags.get('export') is True
or company.custom_flags.get('export_enabled') is True
):
allow_export = True
else:
allow_export = False
It’s brittle. It’s invisible. Nobody knows who has access to what, or why.
Now multiply that by every feature you gate — usage limits, add-ons, API calls, roles, products — and suddenly your business logic is a landmine.
Pricing experiments? Risky.
Trials? Dangerous.
Billing bugs? Guaranteed.
A robust entitlements layer solves this by externalizing that logic into a structured, queryable system. Instead of coding it in, you check:
if entitlements.check(company, 'can_export'): # proceed
And that check pulls from a central config: the source of truth for what each company is entitled to — across plans, overrides, trials, whatever.
Clean. Flexible. Safe.