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:
1if company.plan in ['pro', 'enterprise']:
2 allow_export = True
3else:
4 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:
1if company.id == 12345:
2 allow_export = True
3elif company.plan in ['pro', 'enterprise']:
4 allow_export = True
5else:
6 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:
1if (
2 company.plan in ['pro', 'enterprise', 'growth']
3 or company.id in [12345, 67890]
4 or company.trial_flags.get('export') is True
5 or company.custom_flags.get('export_enabled') is True
6):
7 allow_export = True
8else:
9 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:
1if 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.