Skip to content

Production Data Systems Handbook / Chapter 14

Relational Systems: The Default You Should Understand Before Replacing

Evaluate relational systems as a strong but bounded default for structured operational data, transactions, constraints, queries, and mature operations.

A Slow Feed and a Fast Conclusion

A collaboration product has an activity feed. Each item may refer to a task, comment, file, assignment, status change, or approval. Loading the feed joins several tables, applies permissions, sorts by recency, and scans too much history. During busy periods the query is slow enough to threaten the page-load target.

The proposed cure is a document store. Feed items look like documents, the argument goes, and document databases scale.

The diagnosis has leapt over the workload. Writes are moderate. Recent reads are frequent. Support needs to filter by actor, project, customer, and event type. Compliance needs deletion propagation and an auditable record. Above all, permissions are strict: after a user loses access to a project, its activity must disappear from that user’s feed.

The product has two different problems. It needs canonical facts whose relationships survive concurrency and change, and it needs a fast shape for one hot read. Moving everything answers the second problem by obscuring the first.

A relational system is an unusually capable default for this kind of operational data. It combines constraints, multi-row transactions, expressive queries, indexes, and mature recovery machinery. Those capabilities are production leverage, not tradition. They also have boundaries. The useful question is neither “Can relational databases scale?” nor “Which newer database should replace ours?” It is: which facts still benefit from the relational core, and which access path has earned a different shape?

A relational fit matrix compares multi-entity invariants, ad hoc queries, global write latency, unbounded events, large text search, replica lag, and migrations across strong fit, weak fit, warning signs, and mitigations.
Relational systems are a strong default when constraints, transactions, and queries matter. The replacement conversation should name the exact boundary they no longer satisfy.

What the Core Protects

The activity feed is assembled from facts with different owners. Projects define membership. Tasks and comments define activity. Approval records may carry durable business meaning. Deletion records prove that restricted data was removed. A single flattened feed document can make the page easy to render, but it cannot decide which copy is authoritative when a project changes, a comment is deleted, or a permission is revoked.

Relational modeling gives each canonical fact a home. Primary and foreign keys identify it and connect it to other facts. Unique, not-null, and check constraints reject states the business says are impossible. A transaction can make an activity event and its source change commit together, or make neither visible. SQL lets an operator ask an unanticipated question during an incident without first designing a new read model.

That combination is particularly strong for accounts, orders, invoices, subscriptions, permissions, inventory reservations, ledger entries, entitlements, and workflow state. These are structured entities, but structure alone is not the reason. Their relationships and invariants matter under retries, deploys, races, audits, and recovery.

Database enforcement is not free. Constraints add work to writes. Foreign keys and large validations can complicate migrations. Long transactions hold locks and preserve old row versions. Yet deleting a constraint does not delete the rule. It moves enforcement into service code, messages, retries, reconciliation jobs, and operator judgment. Make that move only when the new enforcement location is explicit and credible.

Model Truth Separately from Convenience

Normalization helps identify the owner of a fact. In the feed system, the current project membership belongs to the membership relation; it should not be copied into every event as if the copy could remain current forever. An activity event may preserve the actor and action that occurred, while authorization at read time depends on current membership.

Convenient read shapes may and often should duplicate data. A recent-feed table can carry the fields needed to render the common page. A materialized view can precompute joins. A cached permission expansion can avoid repeated traversal. A copied field is disciplined denormalization when the team can name its source, refresh rule, staleness tolerance, deletion behavior, drift detector, and rebuild path.

This is the important relational boundary: the source-of-truth schema describes what must remain correct; derived tables and stores describe how particular readers need to see it. One schema need not perform both jobs equally well.

Repair the Read Path Before Replacing the Truth

The feed query deserves investigation, not loyalty to the current implementation. Its actual plan shows where the work occurs: which rows are estimated and visited, which indexes are selected, where sorting spills, how permissions are joined, and how often the query runs. The slow path may come from a missing composite index, poor selectivity, stale statistics, wide rows, chatty ORM behavior, or years of history sharing the online path.

For the collaboration product, a fair relational trial has several parts:

  1. Index the permission and recency paths from observed query plans, then measure the added write and maintenance cost.
  2. Partition old activity by time and define retention so the online indexes describe a bounded working set.
  3. Build a recent-feed table or materialized read model from canonical activity and membership changes.
  4. Keep long-range reporting off the primary, and make the projection’s freshness and rebuild lag visible.

This design keeps events, resources, permissions, and deletion records relational while giving the page a shape built for its workload. It is not a compromise between database camps. It is a statement of authority: relational truth plus an explicit derived access path.

Other ordinary limits deserve the same diagnosis. Read replicas can move tolerant reads away from the primary, but lag makes them unsafe for permission checks, checkout confirmation, password resets, and “show me what I just wrote” unless the application routes or pins those reads deliberately. Connection pooling can prevent a service fleet from exhausting a healthy database, but only if transaction length, pool size, timeouts, and backpressure agree. Archival can shrink hot indexes and backups. Partitioning can bound maintenance by time or tenant, though it adds routing, planning, and monitoring work.

Sharding belongs later because it changes much more than capacity. Cross-shard transactions, joins, uniqueness, rebalancing, tenant moves, backups, migrations, and incident response all acquire new failure modes. When a measured write rate, tenant size, isolation need, or data volume justifies that architecture, the decision should include those costs rather than treating sharding as a larger index.

Recognize a Real Boundary

Some workloads remain poor fits after a fair trial.

Worldwide low-latency writes cannot escape distance. If every commit coordinates synchronously across regions, the system pays that distance in latency or availability. Regional ownership, asynchronous replication, or conflict-aware data models may be a more honest design than one global primary.

An append history can live comfortably in relational tables when retention is finite, partitions bound the working set, and queries benefit from relational access. A massive unbounded stream with high write volume and replay-oriented consumption may deserve a durable log or object-backed archive. The boundary is the workload and its recovery contract, not the word “event.”

Text search can begin with exact match, prefix search, structured filters, and modest full-text indexing. When linguistic analysis, typo tolerance, relevance tuning, synonyms, faceting, and indexing throughput become product capabilities, a search engine earns a derived boundary. The relational rows may still own product identity, visibility, and lifecycle.

Large analytical scans over cold history should not compete with online transactions. Warehouses, lakehouses, or analytical replicas can serve long joins and exploration under an explicit freshness contract. High-dimensional similarity and deep graph traversal follow the same test: extensions may keep a modest workload simple; specialized systems earn their place when query shape, scale, latency, or ranking exceeds that design cleanly.

Specialization is not failure of the relational core. It is failure when the new store arrives without a source-of-truth boundary, freshness promise, deletion path, rebuild procedure, owner, and exit plan.

Operations Reveal the Real Fit

Relational systems continuously update indexes and statistics, coordinate locks, retain row versions, reclaim space, write logs, replicate changes, and prepare for recovery. When this machinery falls behind, database symptoms become product symptoms: blocked deploys, latency spikes, disk growth, stale replicas, deadlocks, or failed failover.

Schema migrations make the operating model visible. A large type change, backfill, new index, or validated constraint can lock work, amplify writes, saturate replicas, and lengthen recovery. Safer change expands the schema, deploys compatible readers and writers, backfills in bounded batches, validates, switches reads, and contracts the old representation only after a rollback window.

Backups are claims until a restore proves them. The team needs measured recovery time and recovery point, named command ownership, protected credentials, understood replica-promotion behavior, and application-level checks after recovery. Dashboards should expose query latency by fingerprint, lock waits, deadlocks, pool saturation, replication lag, log and disk growth, table and index size, migration progress, backup status, and restore-drill results. Without these signals, operational debt is easily mistaken for a database-class limit.

The relational default is strongest when invariant complexity, changing operational questions, and a small operating team make one well-understood authority valuable. It weakens as a particular workload crosses a measured boundary that ordinary tuning, separation, and lifecycle work cannot meet. Product age by itself decides neither case.

Make a Replacement Carry Its Burden of Proof

Before adding a new database class, write a relational-default challenge. It should make six things inspectable:

  1. The failure. Name the latency, throughput, correctness, cost, recovery, deployment, or operator limit, with the query, lock, pool, lag, migration, retention, or load evidence that exposes it.
  2. The relational trial. Record the indexes, query rewrites, schema changes, partitions, archives, replicas, materialized views, caches, or batching strategies tested, including why they failed or cost too much.
  3. The displaced guarantees. Identify which transactions, constraints, uniqueness rules, references, ad hoc queries, or audit properties move out of the database and how they will be enforced or repaired.
  4. The new boundary. Say whether the added store is authoritative, derived, cached, archival, or analytical. Give it a freshness contract, deletion behavior, drift signal, and rebuild source.
  5. The operational delta. Name the backup, restore, monitoring, access-control, migration, incident, cost, and on-call work the team is accepting.
  6. The exit. Define how to reduce scope, rebuild, migrate, or remove the store without losing authoritative data.

For the activity feed, the challenge does not support replacing the relational core. It supports a precomputed recent-feed path with a bounded freshness target and rebuild from canonical events. Search and analytics may later earn their own projections. Current permission and deletion facts remain authoritative where their invariants are easiest to see and enforce.

Take a painful relational workload from a system you know and run the same challenge. If latency is the pain, bring the query plan, cardinality, index choices, write cost, and summary-table option. If write throughput is the pain, bring lock waits, transaction length, pool behavior, hot indexes, skew, and archival pressure. If migrations are the pain, bring table size, lock behavior, backfill rate, replica pressure, and rollback path.

Then design a read-replica policy for one workflow. Decide which reads may be stale, which must be current, how the application routes them, what the user sees during lag, and which alert declares the policy unsafe.

The relational default has earned its keep when it makes important facts easier to preserve, question, change, and recover. When one workload crosses its boundary, replace that boundary—not the understanding that let you find it.