Design approach
Data-Driven Design
Move business rules out of your source code and into data — then write one small, fixed engine that interprets whatever rules it's given.
✗ The problem
Rules hard-coded as if/else
Every pricing tweak, new discount, or country rule means editing the function itself — and redeploying.
function priceOrder(order) {
let total = order.spend;
if (order.spend >= 100) total *= 0.9;
if (order.member) total *= 0.95;
if (order.country === 'EU') total *= 0.97;
// new rule? new country? → edit + redeploy…
return total;
}
Logic is buried in code — only devs can change a discount, and every
tweak risks a deploy.
✓ The approach
↓
↓
Externalize rules into data; write a fixed engine
The rules become a table (config / JSON). A small generic engine interprets them — behavior changes by editing data, not code.
const discountRules = [
{ label: 'spend>=100', pct: 10 },
{ label: 'member', pct: 5 },
];
function apply(rules, order) {
let pct = 0;
rules.forEach(r => {
if (r.test(order)) pct += r.pct;
});
return order.spend * (1 - pct / 100);
}
Data
rules table
Engine
apply(rules, order)
Result
price
✓ See it live
↓ engine
Edit the rules — the engine never changes
Fixed sample order: $220 spend, member customer. Toggle or add a
rule row — the same apply() engine re-evaluates it instantly.
| Rule | Discount | State |
|---|
Order
$220 · member
Result
—
✓ Takeaway
Behavior lives in data, not code
- Change behavior without redeploying — edit a row in the rules table/config/JSON, done.
- Non-devs can drive it — ops, pricing, or game-design teams can own the data file.
- One engine, many rule sets — the same
apply()powers every campaign, country, tier. - Great fit: pricing engines, feature flags, game balancing, validation rules, workflow configs.
- Caution: data can hide logic — keep rule sets small, typed, and validated, or debugging turns into archaeology.
🎯 Relates: the engine-over-data idea is Strategy + Interpreter, and it delivers Open/Closed (new rules = new data, not new code).
Back to all topics →