Distributed Systems Series — Part 5.12: Scalability & Performance
The End of the Beginning
This is the forty-third and final post in a series that began with a simple question: what does it mean to build distributed systems correctly? Not just systems that work in development, not just systems that pass their tests, but systems that remain correct under partial failure, that maintain data integrity under concurrent writes, that serve users reliably when their dependencies fail, and that scale to handle growth without fundamental redesign.
Forty-two posts have answered five sequential questions. Part 1 established the environment — the network model, the failure model, the time model — the physical and logical constraints that no distributed system escapes. Part 2 established how systems communicate and coordinate within that environment — the protocols, the reliability mechanisms, the coordination services that allow independent nodes to work together. Part 3 established how data is stored and replicated correctly — the consistency models, the consensus algorithms, the replication protocols that preserve data integrity across failures. Part 4 established how systems survive failures and maintain availability — the redundancy patterns, the fault isolation mechanisms, the observability and chaos engineering disciplines that keep systems operational when things go wrong. Part 5 has established how systems handle growth — the scalability mechanisms that allow load, data volume, and geographic reach to increase without architectural replacement.
This final post synthesises Part 5 into ten engineering principles for scalability and performance, provides the complete design review checklist that captures the series as an operational tool, and closes the arc that began with Post 1.1.
Ten Engineering Principles for Scalability and Performance
Principle 1: Measure Before Optimising
Every scalability and performance optimisation decision must begin with measurement. Adding capacity to a system without identifying its bottleneck moves the bottleneck — it does not eliminate it. Optimising a component that is not the bottleneck produces no user-visible improvement while consuming engineering time and infrastructure cost.
The measurement process: use the four golden signals from Post 4.8 to identify the saturated resource. Use distributed tracing from the same post to find where time is spent within a request. Use profiling under production-representative load to identify the specific code paths and data structures that consume the most resources. Identify the bottleneck precisely, then address it precisely. The bottleneck identification process from Post 5.1 is the prerequisite for every other principle in this list.
This principle is the most violated in practice. Engineers who have experienced a specific bottleneck in a previous system apply the same optimisation to the next system without measuring whether the bottleneck is the same. Systems that struggled with database load in the past receive heavy caching investment in the next iteration — sometimes before confirming that database load is actually the constraint. Measure first. Always.
Principle 2: Design Stateless Services First
Stateless services — services that carry no state between requests and read all necessary state from external stores — are the prerequisite for horizontal scaling. A stateless service can be replicated behind a load balancer without coordination. Any instance can serve any request. Adding instances adds proportional capacity. Autoscaling works correctly and immediately.
Stateful services — services that carry session data, in-progress computation, or accumulated state in local memory — cannot be replicated cleanly. They require sticky routing, which undermines load balancing. They require state transfer on failover, which adds complexity and failure modes. They require explicit partitioning to scale write capacity, which adds operational overhead.
The architectural discipline: push state outward to dedicated stateful services (databases, caches, coordination services) that are explicitly designed for replication and partitioning. The application tier should be stateless. State lives in the data tier. This separation — stateless compute, explicitly stateful storage — is the design pattern that underlies every horizontally scalable production system from Netflix’s API tier to Stripe’s payment processing services.
Principle 3: Partition to Eliminate Coordination
Amdahl’s Law from Post 5.1 establishes the mathematical ceiling that coordination overhead imposes on scalability. The sequential fraction — the work that cannot be parallelised because it requires coordination — sets an absolute ceiling on how much adding resources can help. Reducing the sequential fraction is more valuable than adding resources, because it raises the ceiling itself.
Every distributed lock, every consensus round, every cross-shard query is sequential work that consumes the parallelisable fraction of the system’s capacity. Partitioning eliminates coordination by dividing the key space so that each partition operates independently — writes to different partitions proceed in parallel with no coordination between them. The partition key design from Post 5.3 determines how much coordination is eliminated and how much remains as scatter-gather overhead.
The principle extends beyond data partitioning. Service decomposition eliminates coordination by assigning each service clear ownership over a bounded domain — the payment service owns payment data, the inventory service owns inventory data, neither requires cross-domain coordination for its primary operations. Microservices architecture applied correctly is Amdahl’s Law applied to service decomposition.
Principle 4: Cache at the Right Layer
Caching is the highest-leverage performance technique in distributed systems, but only when applied at the layer where it absorbs the most load. Caching at the wrong layer absorbs load from a component that is not the bottleneck while leaving the actual bottleneck unaddressed.
The five placement layers from Post 5.5 protect different downstream components. Client-side and CDN caches protect origin servers from global read traffic. Application-level caches protect databases from query load. Database buffer pools protect disks from repeated I/O. The correct layer is the one that sits immediately in front of the bottleneck — it absorbs the load that is saturating the bottleneck resource.
Cache hit rate is the measure of whether caching is working. A cache with 95% hit rate is absorbing 95% of reads from the protected downstream. A cache with 40% hit rate is providing marginal benefit at the cost of additional infrastructure and invalidation complexity. If the hit rate is low, either the cache is too small to hold the working set, the TTLs are too aggressive, or the access patterns are not amenable to caching.
Principle 5: Instrument p99, Not Average
Average latency is an engineering anti-pattern for user-facing services. It hides tail behaviour, does not reveal the experience of the users who matter most (the ones experiencing failures), and does not capture the fan-out amplification that makes tail latency the dominant factor in end-to-end response time for distributed systems with significant fan-out depth.
P99 latency from Post 5.2 is the minimum useful latency metric for production alerting. P99.9 is required for high-scale systems and for setting per-component SLOs that will be met at the end-to-end level under fan-out. Latency percentiles must be calculated from histograms aggregated across instances — not from averaged percentiles, which are mathematically incorrect and cannot be used as SLO compliance metrics.
The latency budget discipline — allocating end-to-end latency across component budgets and tracking each component against its budget using distributed tracing — is the operational tool that makes p99 instrumentation actionable. Without latency budgets, p99 SLO violations produce alerts but not diagnosis. With latency budgets, every violation immediately identifies which component consumed its allocation, focusing investigation rather than requiring guesswork.
Principle 6: Implement Backpressure Before You Need It
Backpressure and rate limiting should be implemented as part of the initial system design, not added after the first overload incident. The overload failure mode — unbounded queues filling memory, cascading timeouts, retry storms amplifying load — is severe and difficult to recover from under active production load. Building the protection before it is needed is dramatically cheaper than recovering from the failure it would have prevented.
The backpressure implementation hierarchy from Post 5.6: bounded queues with explicit rejection at each service boundary prevent unbounded memory growth. Rate limiting at the entry point controls external traffic before it reaches internal components. Adaptive concurrency limiting (Netflix’s approach) tracks actual capacity dynamically rather than relying on fixed configuration. Backpressure signals propagate upstream through gRPC flow control and Kafka consumer pull semantics automatically for services built on those protocols.
The asymmetry between implementing backpressure proactively and reactively: implementing it takes days during normal operation. Implementing it during an active incident while the system is failing takes much longer, under conditions of high stress, with the risk that every configuration change will make the incident worse before it makes it better.
Principle 7: Scale on Business Metrics, Not Infrastructure Metrics
Autoscaling from Post 5.8 must use metrics that accurately represent the work the service needs to do. CPU utilisation is the correct metric for CPU-bound services and the wrong metric for I/O-bound services. A service that spends 80% of its time waiting for database responses will show low CPU while users experience high latency — scaling on CPU adds compute that is not the constraint.
The correct scaling metric depends on the service’s bottleneck: requests per second for stateless API services, queue depth or consumer lag for async processing services, active sessions for session-intensive services, database connection pool wait time for database-bottlenecked services. Business metrics are often the most direct signal — active checkouts for a checkout service, concurrent renders for a rendering service, pending inference requests for a machine learning service.
Predictive autoscaling from the same post is the correct complement to reactive autoscaling for services with predictable demand patterns. A service that spikes every weekday morning should have that spike provisioned predictively, not reactively — reactive autoscaling cannot add capacity faster than its provisioning lag, and the spike that triggers reactive scaling is the spike that is already affecting users.
Principle 8: Plan for Geographic Distribution Before It Is Urgent
Geographic distribution from Post 5.9 is significantly harder to retrofit into a system that was not designed for it than to incorporate during initial design. A system built with a single-region mental model — one database, one application tier, one set of infrastructure — requires fundamental architectural changes to support multiple regions: data partitioning by geography, conflict resolution for active-active writes, data residency compliance for regulated data, CDN integration for global read performance.
The decisions that matter earliest are data model decisions. Which fields contain personally identifiable information that must stay within a specific region? Is the data model amenable to regional partitioning, or does it assume global joins? Do the application’s query patterns align with regional data ownership, or do they require cross-region aggregation? These questions are much easier to answer before data is written to production than after.
Geographic distribution also requires compliance architecture decisions from the start. GDPR, healthcare data regulations, and financial data regulations impose geographic constraints that are architectural requirements, not operational ones. A system that discovers GDPR data residency requirements after building a globally centralised data store faces a migration project measured in months, not days.
Principle 9: Cost-Model Every Significant Decision
Infrastructure cost is an engineering output, not a finance input. Every significant architecture decision — replication factor, consistency model, caching strategy, data retention policy, managed service selection — has a cost implication that can be calculated before the decision is made. Engineering teams that understand the cost implications of their decisions make better trade-offs than teams that discover cost in the monthly bill.
The unit economics framework from Post 5.10 makes cost visible in the terms that both engineering and business stakeholders understand. Cost per request, cost per active user, and cost per transaction answer the question that matters: is the system becoming more or less efficient as the business scales? A cost per request that decreases as traffic grows means the architecture is capturing economies of scale. A cost per request that increases as traffic grows means a scalability efficiency problem requires architectural attention.
The cost optimisation ladder provides the correct sequence: right-size before reserving, reserve before using spot instances, address egress before optimising within a region. Each step in the ladder produces maximum benefit when the previous steps have already been completed. Teams that skip right-sizing and go directly to reserved instances commit to the wrong instance types — a common and expensive mistake.
Principle 10: Decouple With Asynchronous Processing Wherever the Caller Does Not Need an Immediate Response
Synchronous communication is appropriate when the caller needs the result of the operation before proceeding. It is inappropriate when the caller does not need the result — when it only needs confirmation that the work has been accepted. In the second case, synchronous communication creates tight coupling that degrades performance, reduces availability, and prevents independent scaling.
The decoupling discipline from Post 5.11: any operation that the caller does not need to wait for should be made asynchronous. Order confirmation emails, analytics event processing, inventory reservation, fraud detection, recommendation model updates — none of these require the caller to wait. Synchronising on them adds latency to the user-facing request and couples the user-facing service to the availability of each downstream.
Asynchronous processing also enables the fan-out pattern — one event producing multiple independent downstream processing pipelines, each scaling independently based on its own backlog. This is the architecture that makes large-scale event-driven systems tractable: each consumer owns its backlog, scales based on its own load, and fails or recovers without affecting other consumers. The outbox pattern ensures that the transition from synchronous to asynchronous does not introduce the dual-write consistency problem.
The Complete Scalability Design Review Checklist
Before any significant distributed system component goes to production, apply this checklist. Each item corresponds to a principle above and the series posts that cover it in depth.
Scalability foundations
Is the bottleneck identified and measured before any optimisation is applied? Can the service scale horizontally — is it stateless, or does it have documented state that is managed externally? Is the partition key designed to align with the most frequent access patterns, avoiding scatter-gather on latency-sensitive queries? Is the partition strategy designed to avoid hot partitions — are sequential keys randomised, are large tenants isolated?
Latency and performance
Is p99 latency instrumented correctly using histogram aggregation across instances? Is there an explicit latency budget that allocates end-to-end latency across components? Are latency percentiles included in the service’s SLOs, and are those SLOs set with fan-out depth accounted for? Is distributed tracing in place to make latency budget violations diagnosable without guesswork?
Load management
Are all queues bounded with explicit rejection on overflow? Is rate limiting implemented at the service entry point? Is backpressure propagated upstream through gRPC flow control, Kafka consumer pull, or explicit queue depth signalling? Are retry strategies using exponential backoff with full jitter to prevent retry storms? Is a circuit breaker in place for each critical downstream dependency?
Caching
Is the cache placed at the layer that is in front of the actual bottleneck? Is cache hit rate instrumented and meeting the target? Is cache invalidation designed with explicit handling for the race condition between concurrent reads and writes? Is cache warming implemented as part of the deployment process? Is a thundering herd mitigation (probabilistic early expiration or mutex locking) in place for high-traffic cache entries?
Autoscaling
Is the scaling metric the one that most accurately represents the service’s actual load — not defaulting to CPU without verification? Is the scaling floor set high enough to handle baseline load from cold instances without requiring scale-out? Is the scale-down cooldown period long enough to prevent oscillation? Is cold start time accounted for in the provisioning lag budget? Is graceful shutdown implemented so that scale-in does not lose in-flight work?
Geographic distribution
Is the data model compatible with regional partitioning, or does it require cross-region joins that would be prohibitively slow? Are data residency requirements identified and mapped to infrastructure placement decisions? Is the multi-region model (active-passive, active-active, regional isolation) chosen deliberately with explicit trade-off analysis? Is there a documented conflict resolution strategy if active-active writes are used?
Cost
Is unit economics calculated — cost per request or cost per active user — and tracked over time? Is the capacity planning formula applied, sizing for peak demand with safety margin and target utilisation? Are instances right-sized before reserved instances are purchased? Is data lifecycle management configured to transition data to cold tiers automatically as it ages? Is network egress cost included in infrastructure cost models?
Async processing
Are operations that the caller does not need to wait for decoupled through async messaging? Is at-least-once delivery assumed, and are all message consumers idempotent? Is a dead letter queue configured for all queues, and is DLQ depth alerted on? Is the outbox pattern or CDC in place for any workflow that requires both a database write and a message publication?
The Complete Series Arc: Five Questions, One Discipline
Every post in this series addressed a specific question that distributed systems must answer. The answers build on each other — each part assumes the previous parts are correctly implemented, and the combined answer is what it takes to build systems that work reliably at scale.
Part 1 — What is the environment? Networks drop packets and partition. Nodes fail in partial and Byzantine ways. Clocks drift and provide only bounded approximations of global time. These are not edge cases — they are the operating conditions of every distributed system. The eight fallacies of distributed computing are the assumptions that engineers make when they forget these conditions. Every mechanism in Parts 2 through 5 exists because Parts 1 through 5 describe a genuinely hostile environment that naive assumptions cannot survive.
Part 2 — How do systems communicate within that environment? Retries with idempotency, circuit breakers, service discovery, distributed locks, logical clocks, and coordination services — each addresses one failure mode that the environment from Part 1 introduces. Communication in distributed systems is not function calls over a network. It is a discipline of designing for failure, retrying correctly, detecting failure promptly, and coordinating without assuming reliable delivery.
Part 3 — How do systems store data correctly? Replication, consistency models, the CAP theorem, quorums, consensus algorithms — these answers establish that storing data correctly across multiple nodes is fundamentally harder than storing data on one node, and that the difficulty is not implementation complexity but inherent impossibility. You cannot have strong consistency and availability during network partitions. You cannot replicate without choosing between synchronous and asynchronous trade-offs. You cannot run consensus cheaply. These are not problems to be solved — they are constraints to be designed within.
Part 4 — How do systems survive failures? Failure taxonomy, redundancy patterns, failure detection, recovery and self-healing, load shedding, fault isolation, observability, chaos engineering — each addresses the operational dimension of correctness. A system that is correct in its algorithms but fragile in its operations fails its users just as surely as one with incorrect algorithms. Fault tolerance and high availability are engineering disciplines as rigorous as consistency and consensus.
Part 5 — How do systems handle growth? Scalability, latency, load balancing, partitioning, caching, backpressure, indexing, autoscaling, geo-distribution, cost, async processing — the mechanisms that allow a system correct at one order of magnitude to remain correct and performant at the next. Scalability is not what you add after the system works. It is a property you design in from the beginning, because the systems that fail to scale are usually the ones that were not designed to scale, not the ones that ran out of hardware.
What Comes After This Series
Forty-three posts are the foundation, not the ceiling. Distributed systems is a field deep enough that forty-three posts establish the vocabulary and the fundamental mechanisms while leaving significant territory unexplored.
The topics that this series has introduced but not covered in full depth include: distributed stream processing (Apache Flink, Spark Streaming, the stateful computation model that extends Kafka’s log to real-time aggregate computation), distributed machine learning infrastructure (parameter servers, gradient synchronisation, the consistency models for model training that differ from transactional consistency), service mesh internals (the xDS API, the control plane architecture, the operational model of running Istio or Linkerd at scale), database internals at depth (the storage engine layer, the query planner, the transaction manager — the implementation of the mechanisms this series describes at the architectural level), and the emerging field of serverless distributed systems (the programming model, the cold start problem at provider scale, the consistency guarantees of serverless state management).
The references that will extend this series most productively: Martin Kleppmann’s Designing Data-Intensive Applications for the data systems depth that Parts 2 and 3 introduced at architectural level. Tanenbaum and van Steen’s Distributed Systems: Principles and Paradigms for the theoretical foundations that this series treated pragmatically. Roberto Vitillo’s Understanding Distributed Systems for a complementary practitioner perspective. The Google SRE Book and the Google Site Reliability Workbook for the operational discipline that Part 4 introduced. The papers behind the systems this series cited — the Raft paper, the Dynamo paper, the Spanner paper, the Kafka paper, the Chubby paper — each provides implementation depth that architectural discussion cannot substitute for.
Key Takeaways
- Measure before optimising — bottleneck identification using the four golden signals and distributed tracing is the prerequisite for every scalability decision; adding capacity to an unidentified bottleneck moves it rather than eliminates it
- Stateless services are the horizontal scaling prerequisite — push state to dedicated external stores, keep application tiers stateless, and autoscaling and load balancing work correctly and immediately
- Partition to eliminate coordination — Amdahl’s Law sets a ceiling proportional to the sequential fraction; reducing locks, consensus rounds, and cross-shard queries raises the ceiling more effectively than adding nodes
- Cache at the layer in front of the actual bottleneck — five placement layers each protect a different downstream resource; hit rate is the measure of whether caching is working
- Instrument p99 not average — fan-out amplification makes tail latency the dominant factor in user-facing performance; SLOs must account for fan-out depth and be measured from histogram aggregations
- Implement backpressure before the first overload incident — bounded queues, rate limiting, and adaptive concurrency limits are orders of magnitude cheaper to build proactively than to retrofit during an active incident
- Scale on business metrics — queue depth and consumer lag for async services, RPS for stateless APIs, active sessions for session-intensive services; CPU is the right metric only for CPU-bound services
- Plan for geographic distribution during initial design — data model, residency compliance, and conflict resolution decisions are architectural and must be made before data is in production
- Cost-model every significant decision — unit economics connect engineering decisions to business outcomes; the cost optimisation ladder (right-size, reserve, spot, egress, lifecycle) must be followed in order
- Decouple with asynchronous processing wherever the caller does not need an immediate response — queues with DLQs, the outbox pattern for transactional publishing, and fan-out patterns enable independent scaling of heterogeneous consumers from shared event streams
Frequently Asked Questions (FAQ)
What is the single most important scalability principle for a new distributed system?
Design stateless services. A stateless service — one that reads all necessary state from external stores on every request and carries nothing between requests — can be replicated behind a load balancer without coordination, can be autoscaled immediately in response to load signals, and can tolerate instance failure without state loss. Every other scalability mechanism (partitioning, load balancing, caching, autoscaling) works correctly and simply for stateless services. For stateful services, every mechanism requires explicit state management, conflict resolution, and replication coordination. Start stateless. Add statefulness only when the access pattern requires it and with explicit design for how the state will be replicated and partitioned.
What should I instrument first when building a new distributed service?
Instrument the four golden signals from the start: latency (p50, p99, p99.9 from histogram aggregation), traffic (requests per second per endpoint), errors (error rate and error classification), and saturation (CPU, memory, connection pool utilisation per instance). Add distributed tracing with trace ID propagation through every service call. Instrument business metrics that represent the service’s actual load. With these in place, every performance regression, every capacity issue, and every SLO violation has an immediate diagnosis path rather than requiring post-incident archaeology. Observability retrofitted after the fact is expensive and incomplete — it must be built in from the start to be useful under incident conditions.
When should a system use synchronous vs asynchronous communication?
Use synchronous communication when the caller requires the result of the operation before it can proceed — a user checkout that must confirm payment before completing the order. Use asynchronous communication for everything else: operations the caller does not need to wait for (confirmation emails, analytics recording, inventory reservation after order confirmation, fraud detection, recommendation updates). The test is whether the user’s experience is degraded by waiting for the operation to complete synchronously. If not, the operation should be asynchronous. Asynchronous processing decouples producer and consumer throughput, enables independent scaling of each consumer, and protects the user-facing service from the availability and performance of downstream processing services.
How do I know if my system is ready to scale geographically?
Three readiness criteria. First, the data model must support regional partitioning — data for each user or tenant can be assigned to a home region without requiring cross-region joins for the most common queries. Second, data residency requirements must be mapped to the data model — every field that contains personally identifiable information or regulated data is identified and can be routed to the correct region. Third, the conflict resolution strategy for cross-region writes must be designed — if active-active is required, the conflict resolution approach (last-write-wins, CRDTs, application-level merge) must be explicitly designed for each data type. Systems that meet all three criteria can expand geographically incrementally. Systems that do not meet these criteria require data model changes before geographic expansion — changes that become more expensive after data is in production.
What is the most common scalability mistake at each stage of growth?
At early stage (hundreds of users): premature optimisation — building complex distributed infrastructure before the load justifies it, adding operational complexity that slows development velocity without providing scalability benefit. At growth stage (thousands to hundreds of thousands of users): not measuring bottlenecks before scaling — adding compute when the bottleneck is the database, adding database replicas when the bottleneck is missing indexes, scaling the wrong thing consistently. At scale stage (millions of users): not designing for data scalability early enough — discovering that the data model does not support partitioning after the dataset is too large to migrate easily. At global scale: retrofitting geographic distribution and data residency compliance into a system built with a single-region mental model — the most expensive mistake because it requires the most fundamental architectural change.
What should I read next after completing this series?
Martin Kleppmann’s Designing Data-Intensive Applications provides the data systems depth that this series introduced at architectural level — storage engines, query planners, transaction internals, and distributed database implementation in detail. For the theoretical foundations, Tanenbaum and van Steen’s Distributed Systems: Principles and Paradigms provides the academic grounding. For operational depth, the Google SRE Book and Workbook extend the observability and reliability material from Part 4. For implementation depth, the original papers are irreplaceable: the Raft paper (Ongaro and Ousterhout, 2014) for consensus, the Dynamo paper (DeCandia et al., 2007) for eventually consistent key-value storage, the Spanner paper (Corbett et al., 2012) for globally consistent distributed databases, and the Kafka paper (Kreps et al., 2011) for distributed log-based messaging.
The Complete Series
Series home: Distributed Systems — Concepts, Design & Real-World Engineering
Special Reference
Part 1 — Foundations
- 1.1 — Distributed Systems Explained
- 1.2 — System Models in Distributed Systems
- 1.3 — The Network Model
- 1.4 — The Failure Model
- 1.5 — The Time Model
Part 2 — Communication & Coordination
- 2.0 — From Constraints to Communication
- 2.1 — Communication Fundamentals
- 2.2 — Reliability and Retries
- 2.3 — Naming and Service Discovery
- 2.4 — Coordination and Distributed Locks
- 2.5 — Logical Clocks and Time
- 2.6 — Coordination Services
- 2.7 — Engineering Guidelines: Communication & Coordination
Part 3 — Replication, Consistency & Consensus
- 3.1 — Why Replication Is Necessary
- 3.2 — Replication Models
- 3.3 — Consistency Models
- 3.4 — The CAP Theorem
- 3.5 — Quorums and Voting
- 3.6 — Why Consensus Is Hard
- 3.7 — Paxos vs Raft
- 3.8 — Performance Trade-offs
- 3.9 — Engineering Guidelines: Replication, Consistency & Consensus
Part 4 — Fault Tolerance & High Availability
- 4.1 — Failure Taxonomy
- 4.2 — Fault Tolerance vs High Availability
- 4.3 — Redundancy Patterns
- 4.4 — Failure Detection
- 4.5 — Recovery and Self-Healing
- 4.6 — Designing for High Availability
- 4.7 — Fault Isolation and Bulkheads
- 4.8 — Observability
- 4.9 — Chaos Engineering and Resilience
Part 5 — Scalability & Performance
- 5.1 — What Scalability Really Means in Distributed Systems
- 5.2 — Latency and Tail Latency at Scale
- 5.3 — Partitioning and Sharding in Distributed Systems
- 5.4 — Load Balancing Strategies in Distributed Systems
- 5.5 — Caching Trade-offs in Distributed Systems
- 5.6 — Backpressure and Overload Management
- 5.7 — Indexing and Query Optimisation in Distributed Databases
- 5.8 — Autoscaling Distributed Systems
- 5.9 — Geo-Distribution and Multi-Region Design
- 5.10 — Cost and Capacity Planning at Scale
- 5.11 — Distributed Queues and Async Processing
- 5.12 — Engineering Guidelines for Scalability and Performance