DBRaven

Systems Principles

25 principles

Foundational operational laws for distributed systems: encoding the mechanics of failure propagation, consistency drift, temporal behavior, and operational burden accumulation.

8

critical impact

16

high impact

1

moderate impact

0

low impact

25 principles

Audit Trails Require Architectural Commitment, Not Afterthought

OwnershipHigh impact

Adding a comprehensive audit trail to a system that was not designed for auditability requires rewriting the write path of every operation that must be audited: and the cost scales with how much business logic has accumulated since launch.

A payment processing system built without audit logging records that something happened, but not the state before, the state after, who authorized it, or why. Retrofitting this requires instrumenting every code path that touches financial state: triggers (brittle), application-level dual-write (error-prone), or CDC (operationally complex). If the write model had been append-only events from inception, the event log would be the audit trail. The compliance cost is set at schema design time, not at compliance audit time.

Back-Pressure Is the Only Reliable Cascade Failure Prevention

ResilienceCritical impact

A system without back-pressure will accept work faster than it can process it indefinitely: the buffer between acceptance and processing is the failure surface, and it grows until OOM, disk saturation, or queue backlog terminates the system.

Back-pressure is the mechanism by which a slow consumer signals a fast producer to slow down. HTTP synchronous systems have natural back-pressure when the caller blocks waiting for a response. Async queue systems have no inherent back-pressure: a Kafka producer can write 1M messages per second while the consumer processes 1k per second, and the lag grows until disk exhaustion. The correct implementation is consumer-driven flow control, deliberately sized connection pools, HTTP 429 as explicit back-pressure signal, and circuit breakers as emergency back-pressure of last resort.

Boundaries Define Failure

Failure IsolationHigh impact

The blast radius of any failure is bounded only by the architectural boundaries that contain it. A system with no explicit boundaries has infinite blast radius.

Failure propagation in distributed systems follows the path of least resistance through architectural boundaries. Services sharing a connection pool, a database, or a synchronous call chain are failure-coupled whether or not their codebases are decoupled. Every architectural decision that reduces boundary clarity expands the failure surface area.

Cardinality Determines Where Indexes and Partitions Break Down

PartitioningHigh impact

An index or partition key on a column with low cardinality does not distribute data : it concentrates it, converting a theoretical distribution into a practical hotspot.

High-cardinality columns like user_id or timestamp make effective index and partition keys because each value maps to a small, selective subset of rows. Low-cardinality columns like status (3 values) or message_type (5 values) produce the opposite: indexes the query planner ignores because a sequential scan is faster, and partition keys that concentrate all writes into the one partition for the most frequent value. The failure is not visible at design time: it becomes visible when the most popular partition absorbs 90% of write volume and its neighbors sit idle.

Connection Management Is a First-Class Operational Concern

Operational BurdenCritical impact

Every database connection is a server-side resource: not just a client-side abstraction: and every architecture decision that ignores the connection cost ends up paying it under load.

PostgreSQL's process model forks an OS process per connection, consuming 5-10MB of RAM regardless of whether that connection is executing a query. 1000 connections consume 5-10GB of RAM purely for connection overhead. With 100 app instances each configured with a pool of 10 connections, the database receives 1000 connections: approaching PostgreSQL's practical scheduler contention threshold. The fix is not a smaller pool size; it is connection multiplexing via PgBouncer in transaction mode, which allows 100 instances with 10 logical connections each to share 50 real database connections.

Consistency Is a Spectrum

ConsistencyHigh impact

There is no binary choice between consistency and availability: only a spectrum of tradeoffs that determine under which conditions, for which operations, and for how long your system presents stale or inconsistent state.

Distributed systems do not offer perfect consistency without tradeoffs. Read replicas introduce replication lag windows. Caches introduce invalidation windows. Event streams introduce consumer lag windows. CQRS splits read and write models across a temporal boundary. Each consistency mechanism makes an explicit choice about who sees what and when: and those windows have operational consequences that compound under failure.

Data Gravity Resists Migration

ScalingHigh impact

The cost of migrating a dataset grows super-linearly with its size: not because the migration itself is hard, but because every other system that has coupled to it must be migrated simultaneously.

Once a database reaches 1TB with 50 downstream consumers, changing its schema, location, or technology requires coordinating 50 systems in addition to the database itself. Each new service that reads from a database adds a migration dependency. After three years, a central PostgreSQL instance may have 20 services reading from it, each with subtly different assumptions about the data model. The dataset has acquired gravitational pull: changing it requires moving everything in its orbit at the same time.

Distributed Systems Fail Gradually, Not Instantly

ResilienceHigh impact

Distributed system failures manifest as degradation along a spectrum: not as binary up/down transitions: and the most dangerous part of the failure curve is the gradual phase, where the system appears operational but is accumulating damage.

Database connection pools filling 60% → 80% → 95% → 100% is a gradual failure curve with identifiable intervention points at each stage. Kafka consumer lag growing 100 messages → 1000 → 100,000 is a gradual failure with distinct intervention windows. Replication lag growing from 10ms → 500ms → minutes has a point at which the system is "degraded but functional" and a later point at which it is "failing." Most production incidents had visible warning signals in the preceding 30-60 minutes. The signals were not acted on because they appeared within acceptable bounds or because the alert thresholds were not calibrated for gradual failure detection.

Eventual Consistency Introduces Temporal Uncertainty

Temporal BehaviorHigh impact

Eventual consistency is not a consistency guarantee: it is a propagation delay promise. Under normal conditions, propagation is fast. Under failure, propagation delay is unbounded.

Eventual consistency systems are correct during periods of low propagation delay. They are uncertain during high-load periods when replication lag grows. They are incorrect during failure scenarios where propagation halts entirely. Every system that adopts eventual consistency must explicitly model: the acceptable propagation window, the operational procedures when that window is exceeded, and the data hazards created by prolonged staleness. Without this modeling, eventual consistency is a correctness risk, not just a latency tradeoff.

Every Cache Creates Invalidation Complexity

Operational BurdenModerate impact

A cache is not a read optimization: it is a consistency contract. The cost of that contract is invalidation complexity: ensuring that every path that writes data also invalidates or updates every cache that reflects that data.

Caches improve read throughput but introduce a consistency dual: every write to the primary data store creates an obligation to update or invalidate the cache. Miss a write path, and users see stale data. Invalidate too aggressively, and the cache provides no protection. Invalidate incorrectly (race condition between write and invalidation), and users see a brief inconsistency window. The cache is only as consistent as the most obscure write path that touches the cached data.

Every Distributed Lock Is a Potential Availability Bottleneck

Distributed CoordinationHigh impact

A distributed lock converts a concurrent, independent operation into a serialized, dependency-coupled one: and any failure or slowdown in the lock service propagates directly to every operation that requires the lock.

The use cases that appear to require distributed locks: preventing duplicate processing, enforcing single-leader behavior, rate limiting: frequently have safer alternatives: optimistic locking via database CAS, idempotency keys, Raft-based leadership in systems designed for it. When a distributed lock is genuinely necessary, the lock service (Redis, ZooKeeper, etcd) becomes a synchronous availability dependency for every operation that holds the lock. Lock timeout tuning is subtle: too short causes false release under GC pause; too long means a dead process holds the lock for minutes.

Network Latency Is Irreducible in Geo-Distributed Systems

ConsistencyHigh impact

The speed of light imposes a minimum latency floor on any operation that requires coordination between geographically separated nodes: no software optimization eliminates this floor, and architectures that ignore it accumulate latency debt.

Light travels through fiber at approximately 200km per millisecond round-trip. NYC to London is 70ms minimum. NYC to Tokyo is 150ms minimum. A synchronous 2-phase commit across NYC and London adds at least 70ms to every transaction's commit latency, no caching, prefetching, or protocol optimization can reduce this below the propagation constant. Architectures that require synchronous cross-region coordination for every user-facing write have built this latency floor into their p50 response time.

Observability Must Be Designed In, Not Added After

Operational BurdenHigh impact

A system instrumented after a production incident captures what happened last time : it cannot reliably reveal the failure modes you haven't encountered yet or the slow degradations that compound over weeks.

The three pillars of observability: metrics, logs, and traces: address orthogonal questions: what happened in aggregate, what happened event by event, and why it happened across services. Retrofitting distributed tracing to a mature service requires instrumenting every request path, dependency call, and background job. The cardinality trap in logging and the bimodal latency problem in metrics cannot be solved after the fact without significant re-engineering. Services shipped without observability are services you cannot operate safely at scale.

Operational Complexity Compounds

Operational BurdenCritical impact

Every infrastructure component, consistency mechanism, and distributed coordination pattern adds operational burden that is not linear: it compounds multiplicatively as the system grows and as the components interact under failure.

Adding Kafka to a system adds replication management, partition sizing, consumer group coordination, schema evolution, and dead letter queue handling. Adding Elasticsearch adds index management, mapping migrations, cluster state coordination, and shard rebalancing operations. Each component is individually manageable; their combination creates an operational burden that grows faster than the sum of its parts because failure modes interact across component boundaries. Small teams significantly underestimate this compounding effect. This principle is the cost mechanism; its companion, Topology Simplicity Is Operational Leverage, is the design response, minimize the component count in the first place.

Partitioning Delays Bottlenecks: It Does Not Eliminate Them

PartitioningHigh impact

Partitioning distributes load across N nodes, but the hotspot problem, cross-partition coordination cost, and uneven data distribution ensure that a new bottleneck emerges within every partitioned system: typically at the rebalancing boundary or at the hot partition.

Horizontal partitioning (sharding, Kafka topics, read replicas) delays the arrival of the original bottleneck by distributing load. But it introduces new bottlenecks: hot partitions concentrate load, cross-partition operations (aggregations, transactions spanning keys) require coordination that does not scale, and rebalancing events cause temporary degradation. The original bottleneck is deferred; a different one takes its place.

Recovery Paths Matter More Than Happy Paths

ResilienceCritical impact

The operational quality of a distributed system is determined by how it behaves during and after failure: not during normal operation. Any system that has not been tested through its failure recovery paths has unknown operational quality.

Architecture review typically focuses on the happy path: how requests succeed, how data flows correctly, how components interact under normal load. Recovery paths : what happens when a database fails over, when a Kafka broker goes down, when a replica falls behind: are designed at architecture time but rarely tested until the incident happens. Untested recovery paths are not recovery paths. They are theoretical recovery intentions. The cost of this gap is paid during the most stressful operational moment: the production incident.

Replayability Is an Operational Burden

ReplayabilityHigh impact

Every system that promises replayability must also maintain the operational infrastructure, schema compatibility, and consumer coordination to make replay actually work: indefinitely.

Kafka's replayability is one of its most frequently cited advantages and one of its most frequently misunderstood operational commitments. Replay means: retaining events long enough to be useful, maintaining schema compatibility across all retained events, coordinating consumer offsets during replay, handling out-of-order processing implications, and managing the storage and compute cost of replaying large event volumes. Each of these is an ongoing operational commitment that does not amortize: it compounds as the event log grows.

Retry Logic Can Amplify Failure

Failure IsolationHigh impact

Retry logic written to improve individual request reliability can: under failure conditions: increase total system load by 2-10x, converting a partial degradation into a complete outage.

When a downstream service begins failing, naive retry logic in all callers causes each failed request to generate multiple retry attempts. If 100 callers each retry 3 times on failure, the downstream receives 400 requests instead of 100 : 4x the load on an already-degraded system. This is a retry storm. It is not a pathological edge case: it is a predictable consequence of retry logic without backoff, jitter, and circuit breaking. Well-intentioned reliability engineering produces the outage it was designed to prevent.

Scaling Increases Coordination Complexity

Distributed CoordinationCritical impact

Every horizontal scaling decision that adds nodes also adds coordination overhead. For all-to-all coordination (gossip, full-mesh membership) the number of pairwise paths grows as N². Leader- and quorum-based protocols (Raft, Paxos) are engineered to avoid this, they coordinate in O(N) messages per decision, but add their own costs (leader bottleneck, quorum latency, rebalancing). The rule holds either way: more nodes means more coordination, even where it is not literally N².

Scaling Kafka to 20 brokers, scaling to 50 microservices, or scaling to 10 PostgreSQL replicas introduces coordination overhead that does not exist at smaller scale. Leader election, quorum writes, consumer group rebalancing, replica catch-up, and split-brain prevention all require cross-node coordination. The protocols governing this coordination: Raft, Paxos, ZooKeeper, ISR: have failure modes that only manifest when the cluster is large enough that coordination becomes the bottleneck, not the individual nodes.

Schema Changes Are Operational Events, Not Code Changes

Operational BurdenCritical impact

A schema migration that acquires a table lock on a 100M-row table is a production incident waiting to happen: its execution plan, duration, and blast radius must be treated with the same rigor as a major infrastructure change.

ALTER TABLE ... ADD COLUMN NOT NULL on PostgreSQL requires a full table rewrite and holds an AccessExclusiveLock that blocks all reads and writes for the duration. On a 100GB table, this takes minutes. The safe path is the expand-contract migration pattern: add nullable, backfill in batches, add constraint. Engineers who treat schema changes as code changes learn otherwise during their first 3am page about a locked table.

Shared Ownership Creates Governance Drift

OwnershipHigh impact

Any infrastructure component with multiple owners effectively has no owner for the purposes of operational governance: decisions are deferred, alerting coverage is duplicated but incomplete, and runbooks are written by whoever had the incident last.

A shared PostgreSQL database owned by three services is not triply covered : it is triple-uncovered. Each team assumes the others are handling schema governance, index maintenance, query performance tuning, and backup validation. In practice, none of them are doing it completely. The shared ownership model is operationally equivalent to no ownership for every task that has not been explicitly assigned. This produces governance drift: the database accumulates unused indexes, unreviewed schema changes, undocumented query patterns, and operational practices that are inconsistent across owning teams.

Synchronous Coupling Amplifies Fragility

CouplingHigh impact

In a synchronous call chain, each caller inherits the latency of everything below it, so one slow component degrades every caller above it. The sum of the components' P99 latencies is a loose worst-case bound on the chain's end-to-end P99, not an equality, since percentiles are not additive (the true P99 of the sum is lower), but the coupling is real: any single component degrading drags the whole chain.

Synchronous coupling turns latency problems into distributed ones. If Service A calls Service B calls Service C, and Service C experiences a 500ms P99 latency spike, Service A's P99 latency increases by 500ms regardless of Service A's own performance. The chain is as fragile as its slowest component: not its weakest: because every component in a synchronous chain inherits the latency of everything below it. This is one of the most common causes of unexpected P99 latency spikes in production.

Time Is a Core Distributed Systems Dimension

Temporal BehaviorHigh impact

In distributed systems, time is not uniform: it is observed differently by different nodes, propagates asynchronously across the network, and must be modeled as an explicit system dimension, not assumed to be a shared ground truth.

Clock skew, replication lag, consumer lag, TTL expiry, retry backoff intervals, circuit breaker recovery windows, and WAL slot accumulation are all manifestations of time as an active variable in distributed systems behavior. A read replica's view of the world is time-shifted relative to the primary by the current replication lag. A Kafka consumer's view of the event stream is time-shifted by consumer lag. Reasoning about distributed system correctness requires reasoning about what each component knows and when: not just what the system knows overall.

Topology Simplicity Is Operational Leverage

Operational BurdenCritical impact

Each component removed from a system's operational topology eliminates not just its direct maintenance burden but also every interaction failure mode, cross-component alert correlation, and runbook coverage gap it introduced.

Adding components to solve a problem is visible and immediate. The operational cost of those components is deferred, invisible, and compounding. A 3-component system with one failure mode interaction point is fundamentally simpler to operate than a 6-component system with 15 failure mode interaction points. Simplicity is not naivety: it is a deliberate architectural choice that reduces the operational burden floor. Every component added should be justified against its long-term operational cost, not just its short-term capability gain. Where Operational Complexity Compounds explains the superlinear cost of interacting components, this principle is the actionable directive: reduce the count of components and interaction points.

Write Amplification Compounds With Every Secondary Index

Operational BurdenCritical impact

Every secondary index on a write-heavy table multiplies the write I/O by exactly one additional B-tree update per insert, update, or delete: and that cost is permanent, not amortizable.

A table with 5 secondary indexes writes 6 B-tree pages for every 1 logical row write. On NVMe-backed PostgreSQL at 50k writes/second with 5 indexes, that is 300k I/O operations per second from index maintenance alone. Indexes are added one at a time, each individually justified: the compound effect is never evaluated at time of addition, and it accumulates silently until the write path saturates.