Implementation guide
Feature gating by plan, package, or customer.
Plan names change, and customer agreements rarely match forever. A feature gating library gives product code one stable access check while plans, packages, and customer overrides change.
By Jasen Fici · Updated August 22, 2026
New to the model? Start with products, plans, subscriptions, overrides, and resolution order.
The failure mode
Hard-coded plan names spread packaging decisions through product code.
A condition such as plan === "pro" looks harmless. It stops being simple when Growth also has access, Enterprise has a custom limit, an existing customer keeps an old contract, or the product team renames Pro.
The product should ask about advanced-reporting, not guess which commercial records imply it.
// Every new plan or exception changes application code.
const canReport =
account.plan === 'pro' ||
account.plan === 'enterprise' ||
account.id === 'acme-custom-deal';
if (!canReport) throw forbidden();The replacement
Check a stable feature key before the protected operation.
The entitlement catalog maps plans and exceptions to a feature. Application code asks the same question for every customer. Packaging changes do not require a deploy unless the product behavior itself changes.
Use the result in the interface too, but do not rely on a hidden button as the access check.
const canReport = await subscrio.featureChecker.isEnabledForCustomer(
account.key, 'analytics', 'advanced-reporting'
);
if (!canReport) throw forbidden();
return reports.run(account.key);Values and overrides
The same feature check returns limits and customer overrides.
Request a typed value and provide a default. A customer override can change the result without adding a special branch to the calling code.
const seatLimit = await subscrio.featureChecker.getValueForCustomer<number>(
account.key, 'analytics', 'seat-limit', 0
);
if (activeSeats >= seatLimit) {
throw limitReached('seat-limit');
}Tests
Test commercial behavior without naming the package.
Your test cases should describe entitlement outcomes. This keeps the test stable if marketing renames the plan or billing moves to another provider.
it('uses the customer override before the plan limit', async () => {
const limit = await subscrio.featureChecker.getValueForCustomer<number>(
'acme', 'analytics', 'seat-limit', 0
);
expect(limit).toBe(40);
});
it('falls back to the plan after the override is removed', async () => {
const limit = await subscrio.featureChecker.getValueForCustomer<number>(
'beta-co', 'analytics', 'seat-limit', 0
);
expect(limit).toBe(25);
});Before shipping
Make the access check correct before adding a cache.
Plan-based feature access needs stable keys, a check before the protected operation, and a rule for missing or inactive subscriptions. Once that behavior is correct and measured, add caching with matching invalidation and failure rules.
Access-check checklist
- Use a stable product key and feature key.
- Run the check before the protected operation.
- Request typed values with a safe fallback.
- Decide how an inactive or missing assignment should behave.
- Log enough context to explain denied access without logging secrets.
Cache only after measuring
- Choose a cache key that includes customer, product, and feature.
- Invalidate after plan, subscription, and override changes.
- Define a safe stale-value policy for upgrades and downgrades.
- Test behavior when the cache and database disagree.
- Log and monitor uncached database checks.
Common questions
Questions about plan-based feature gating
What is plan-based feature gating?
Plan-based feature gating determines whether a customer can use a feature by resolving the value assigned through a plan, package, purchase, or override. Application code checks a stable feature key instead of a plan name.
How do I restrict features by plan without checking plan names?
Assign a stable feature key to the plans that include it, then check that feature for the current customer. Plan changes and customer exceptions stay in the entitlement catalog instead of spreading through application code.
Can feature gating return a limit instead of true or false?
Yes. Subscrio can resolve numeric and text values as well as toggles. The same feature check can determine access and retrieve a seat or project limit.
Where should feature checks run?
Check access in the code that performs the protected action. The interface can use the same result to explain or hide controls, but interface state should not be the only check.
How should feature gating be tested?
Test the behavior against feature keys and resolved values. Cover the default, plan value, override, missing assignment, inactive subscription, and any cache behavior your application adds.
Is caching required?
No. Start with direct resolution and measure it. Add a cache when feature-check volume or database latency warrants one, then define invalidation for plan, assignment, and override changes.
Next step
Replace one plan check with a feature key.
Use the framework quickstart, create the product and feature, assign a plan, and test the result before expanding the model.