When to Move Business Rules into the Database
Some rules change often or vary by tenant or region. Keeping them in code means more deploys and harder configuration. Here is when and how to drive them from the database.
The tension between code and configuration
Business rules live in code by default: if conditions, lookups, formulas. That works when rules are stable and the same for everyone. When rules change often (e.g. tax rates, fee schedules, eligibility thresholds) or vary by tenant, region, or product, hardcoding them forces a deploy for every change and makes it hard for non-engineers to adjust behaviour. Moving those rules into the database (or into a structured config store that the app reads at runtime) can reduce deploy frequency and put control in the hands of ops or product.
The tradeoff: code is versioned, reviewed, and testable; database-driven rules can be changed without a deploy but are easier to misconfigure and harder to test in the same way. So the decision is not “always database” or “always code,” but “which rules are stable and which are configuration?”
Good candidates for database-driven rules
- Rates, thresholds, and lookups that change periodically or per region: tax rates, fee tiers, eligibility cutoffs. Storing them in tables (e.g.
fee_schedule,tax_rule) lets you update them without a code change and keeps history if you add valid-from/valid-to. - Conditional logic that product or ops owns: e.g. “which report sections to show for this contract type,” “which steps to run in this pipeline.” If the structure is consistent (e.g. a list of conditions and outcomes), storing it as JSON or normalised rows lets you change behaviour via admin UI or migration instead of code.
- Multi-tenant or per-state rules: when the same codebase serves many tenants or jurisdictions, rules that differ by tenant or state are a good fit for the DB. You read the rule set for the current context and evaluate it at runtime.
What to keep in code
- Core invariants and safety checks: authentication, authorization, and “must never do X” logic should stay in code so they are reviewed and tested.
- Complex algorithms that are hard to express as data: if the rule is effectively a small program (e.g. a custom parser or a recursive calculation), code is usually clearer and safer than encoding it in the DB.
- Rules that change only with a product release: if a rule is tied to a feature and only changes when you ship, code is fine and keeps everything in one place.
Design patterns for database-driven rules
- Store rules as data, evaluate in code. The DB holds the rule definition (e.g. “if state = NSW and value > X, apply rate Y”); the application loads it and runs a single, tested evaluator. That way you do not put arbitrary code in the DB; you only store parameters and structure.
- Version and audit. If rules affect money or compliance, keep history (valid-from dates, or an audit log of changes) so you can explain what was in effect at a given time.
- Validate on write. When rules are updated (via admin or migration), validate them (e.g. schema check, or a dry-run evaluation) so bad data does not go live and cause runtime errors.
Summary
- Use the database (or config store) for rules that change often, vary by tenant/region, or are owned by non-engineers.
- Keep core invariants and complex logic in code.
- Store rule definitions as data and evaluate them with a single, tested engine; avoid storing executable code in the DB.
- Version and validate rule changes so you keep control and auditability.
Finding the right split between code and database-driven rules reduces unnecessary deploys and makes the system easier to operate without giving up safety or clarity.
Have thoughts on this post or questions? Get in touch. More blog posts.