Versioning Business Rules Like Code
Code has git. Infrastructure has Terraform state. Database schemas have migrations.
But business rules? Most teams manage them through Jira tickets and hope.
The Problem with Unversioned Rules
When a rule changes in production and something breaks, you need answers:
- What changed? — Which rule was modified, and how?
- When? — At what exact moment did the change go live?
- Who? — Who authored and approved the change?
- Why? — What was the business reason?
- How do I undo it? — Can I revert to the previous version instantly?
If your rules live in if/else blocks, you can answer these through git history. But that means every rule change requires a code change, a pull request, a review, and a deploy.
If your rules live in a database config table, you probably can't answer any of them.
MVCC for Business Rules
TIATON uses MVCC (Multi-Version Concurrency Control) — the same pattern that PostgreSQL uses for row versioning and git uses for commits.
The data model:
-- Artifacts are identities (like git refs)
CREATE TABLE artifacts (
pk BIGSERIAL PRIMARY KEY,
domain_id TEXT NOT NULL,
key TEXT NOT NULL,
kind TEXT NOT NULL, -- 'dmn_table', 'workflow', 'policy'
status TEXT DEFAULT 'active',
head_rev_pk BIGINT REFERENCES artifact_revisions(pk),
UNIQUE (domain_id, key)
);
-- Revisions are immutable content snapshots (like git blobs)
CREATE TABLE artifact_revisions (
pk BIGSERIAL PRIMARY KEY,
artifact_pk BIGINT REFERENCES artifacts(pk),
content JSONB NOT NULL,
checksum TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
created_by TEXT
);
-- Releases are snapshots (like git tags)
CREATE TABLE releases (
pk BIGSERIAL PRIMARY KEY,
domain_id TEXT NOT NULL,
tag TEXT NOT NULL, -- 'v1.0.0', 'v1.1.0'
base_release_pk BIGINT REFERENCES releases(pk),
status TEXT DEFAULT 'pending',
UNIQUE (domain_id, tag)
);
-- Release overrides store only the delta (like git tree diffs)
CREATE TABLE release_overrides (
release_pk BIGINT REFERENCES releases(pk),
artifact_pk BIGINT REFERENCES artifacts(pk),
rev_pk BIGINT REFERENCES artifact_revisions(pk),
PRIMARY KEY (release_pk, artifact_pk)
);
Key properties:
- Revisions are immutable — once created, content never changes
- Artifacts point to head —
head_rev_pkis the latest version - Releases are snapshots — they capture a point-in-time state
- Delta storage — releases only store what changed from their base
Release Pipeline
A release goes through a deterministic pipeline:
pending → compiled → indexed → published
↓ ↓
failed failed
Each stage is a discrete, retriable step:
- Pending — Release created, snapshot resolved (recursive CTE walks base chain)
- Compiled — Bundle built with all artifact revisions
- Indexed — Content indexed for full-text and vector search
- Published — Live and serving traffic
If any stage fails, the release stays at its current status with error details. You can retry from the failed stage without re-running earlier stages.
Snapshot Resolution
When you create release v1.1.0 based on v1.0.0, you only specify what changed. The runtime resolves the full snapshot:
v1.0.0 (base):
loan_eligibility → rev_12
risk_scoring → rev_8
approval_routing → rev_15
v1.1.0 (overrides):
risk_scoring → rev_22 (updated)
v1.1.0 (resolved):
loan_eligibility → rev_12 (inherited)
risk_scoring → rev_22 (overridden)
approval_routing → rev_15 (inherited)
This is efficient — a release with 200 artifacts but only 3 changes stores exactly 3 override records.
Rollback in Seconds
Rolling back from v1.1.0 to v1.0.0 is a metadata operation. No recompilation, no redeployment. The runtime switches which snapshot it serves.
# Current production: v1.1.0
# Rollback to v1.0.0
curl -X POST /v1/admin/releases/rollback \
-d '{"domain": "lending", "target_tag": "v1.0.0"}'
# Time: ~200ms
# Zero downtime
# Full audit trail of the rollback event
Compare this to rolling back a code change: revert commit → PR → review → CI → deploy → verify. That's 30 minutes on a good day.
Diff Between Versions
Because every revision stores content and a checksum, you can diff any two versions:
{
"from": "v1.0.0",
"to": "v1.1.0",
"changes": [
{
"artifact": "risk_scoring",
"type": "modified",
"from_rev": 8,
"to_rev": 22,
"diff": {
"rules_added": 2,
"rules_modified": 1,
"rules_removed": 0
}
}
]
}
An auditor can see exactly what changed between any two releases — down to individual rules within a table.
The Lifecycle
Every business rule artifact follows the same lifecycle:
- Author — Create or modify in the management UI or API
- Test — Run test cases against the new version
- Review — Business stakeholder reviews the change
- Release — Create a tagged release with the change
- Deploy — Publish the release (automatic pipeline)
- Observe — Monitor decisions in production
- Rollback — Instantly revert if something's wrong
Every step is tracked. Every version is preserved. Nothing is lost.