Logic Representation
JSON
Data Modeling
Programming
Computer Science

Representing logic as data in JSON

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Representing logic as data in JSON means encoding conditional rules, boolean expressions, and decision trees as JSON objects instead of hard-coding them in application code. This pattern is used in rule engines, form builders, access control systems, and workflow automation tools. The core idea is to define operators and conditions as nested JSON structures that an interpreter evaluates at runtime, making business logic configurable without code deployments.

Basic Condition Objects

The simplest approach represents each condition as a JSON object with a field, an operator, and a value:

json
1{
2  "field": "age",
3  "operator": "gte",
4  "value": 18
5}

An interpreter evaluates this against a data context:

javascript
1function evaluateCondition(condition, data) {
2    const { field, operator, value } = condition;
3    const actual = data[field];
4
5    switch (operator) {
6        case "eq":  return actual === value;
7        case "neq": return actual !== value;
8        case "gt":  return actual > value;
9        case "gte": return actual >= value;
10        case "lt":  return actual < value;
11        case "lte": return actual <= value;
12        case "in":  return value.includes(actual);
13        case "contains": return actual.includes(value);
14        default: throw new Error(`Unknown operator: ${operator}`);
15    }
16}
17
18const data = { age: 25, country: "US" };
19evaluateCondition({ field: "age", operator: "gte", value: 18 }, data);
20// true

Boolean Combinators (AND, OR, NOT)

Combine conditions with logical operators using nested structures:

json
1{
2  "and": [
3    { "field": "age", "operator": "gte", "value": 18 },
4    { "field": "country", "operator": "in", "value": ["US", "CA", "UK"] },
5    {
6      "or": [
7        { "field": "subscription", "operator": "eq", "value": "premium" },
8        { "field": "trial_active", "operator": "eq", "value": true }
9      ]
10    }
11  ]
12}

The evaluator handles combinators recursively:

javascript
1function evaluate(rule, data) {
2    if (rule.and) {
3        return rule.and.every(sub => evaluate(sub, data));
4    }
5    if (rule.or) {
6        return rule.or.some(sub => evaluate(sub, data));
7    }
8    if (rule.not) {
9        return !evaluate(rule.not, data);
10    }
11    // Base case — a condition
12    return evaluateCondition(rule, data);
13}

This recursive structure can represent any boolean expression.

If-Then Rules

Add actions to conditions to build a rule engine:

json
1{
2  "rules": [
3    {
4      "name": "senior_discount",
5      "condition": {
6        "and": [
7          { "field": "age", "operator": "gte", "value": 65 },
8          { "field": "membership", "operator": "eq", "value": "active" }
9        ]
10      },
11      "action": {
12        "type": "apply_discount",
13        "value": 0.15
14      }
15    },
16    {
17      "name": "new_user_welcome",
18      "condition": {
19        "field": "account_age_days", "operator": "lte", "value": 7
20      },
21      "action": {
22        "type": "show_banner",
23        "value": "Welcome! Enjoy 10% off your first order."
24      }
25    }
26  ]
27}
javascript
1function executeRules(rules, data) {
2    const actions = [];
3    for (const rule of rules) {
4        if (evaluate(rule.condition, data)) {
5            actions.push(rule.action);
6        }
7    }
8    return actions;
9}

Decision Trees

Represent branching logic as nested if-else structures:

json
1{
2  "condition": { "field": "income", "operator": "gte", "value": 50000 },
3  "true_branch": {
4    "condition": { "field": "credit_score", "operator": "gte", "value": 700 },
5    "true_branch": { "result": "approved", "limit": 10000 },
6    "false_branch": { "result": "approved", "limit": 5000 }
7  },
8  "false_branch": {
9    "result": "denied",
10    "reason": "Insufficient income"
11  }
12}
javascript
1function evaluateTree(node, data) {
2    if (node.result) return node; // Leaf node
3    const passed = evaluateCondition(node.condition, data);
4    return evaluateTree(passed ? node.true_branch : node.false_branch, data);
5}

Existing Standards and Libraries

Several established formats encode logic as JSON:

json
1// JsonLogic format (jsonlogic.com)
2{
3  "if": [
4    { ">=": [{ "var": "age" }, 18] },
5    "adult",
6    "minor"
7  ]
8}
javascript
1// Using the json-logic-js library
2const jsonLogic = require("json-logic-js");
3
4const rule = { ">=": [{ "var": "age" }, 18] };
5jsonLogic.apply(rule, { age: 25 }); // true
6jsonLogic.apply(rule, { age: 15 }); // false

JsonLogic is a portable standard with implementations in JavaScript, Python, PHP, Ruby, and more. It supports arithmetic, string operations, array operations, and custom operators.

Access Control Example

json
1{
2  "permissions": [
3    {
4      "resource": "admin_panel",
5      "condition": {
6        "and": [
7          { "field": "role", "operator": "in", "value": ["admin", "superadmin"] },
8          { "field": "two_factor_enabled", "operator": "eq", "value": true }
9        ]
10      }
11    },
12    {
13      "resource": "reports",
14      "condition": {
15        "or": [
16          { "field": "role", "operator": "eq", "value": "admin" },
17          { "field": "department", "operator": "eq", "value": "finance" }
18        ]
19      }
20    }
21  ]
22}

Storing access rules as JSON allows non-developers to modify permissions through an admin UI without changing application code.

Common Pitfalls

  • Unbounded recursion depth: Deeply nested AND/OR/NOT structures can overflow the call stack. Set a maximum depth limit in your evaluator (e.g., 10 levels) and reject rules that exceed it.
  • No schema validation: Without validation, malformed rules silently produce wrong results. Define a JSON Schema for your rule format and validate all rules before storing or evaluating them.
  • Security risks with user-defined rules: If users can define rules, they might craft expensive conditions (like regex matching on large strings). Sanitize inputs and set evaluation timeouts.
  • Missing operator coverage: When you add a new field type but forget to add its operators, conditions silently fail. Throw errors for unknown operators rather than returning false.
  • Performance with large rule sets: Evaluating hundreds of rules per request is slow. Index rules by relevant fields, short-circuit evaluation, and cache results for identical data contexts.

Summary

  • Represent conditions as {field, operator, value} objects evaluated by an interpreter
  • Combine conditions with and, or, and not wrappers for arbitrary boolean logic
  • Add actions to conditions for rule engines, or nest branches for decision trees
  • Use established standards like JsonLogic for portability across languages
  • Validate rule schemas and limit recursion depth to prevent malformed or malicious rules

Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions