Horizontal vs Vertical Scaling — Black Friday DB Ceiling
Database CPU 100%, p99 200ms→15s, checkout 98%→40% — the largest RDS instance still failed on Black Friday.
20+ years shipping large-scale distributed systems. Lessons pulled from things that broke in production.
- ✓Solid grasp of fundamentals
- ✓Comfortable reading code examples
- ✓Basic production concepts
- Vertical scaling (scale up) = bigger machine — more CPU, RAM, disk on a single server
- Horizontal scaling (scale out) = more machines — add identical servers behind a load balancer
- Vertical scaling is simpler but hits a hard ceiling — you cannot buy a machine bigger than the largest cloud instance
- Horizontal scaling has no ceiling but adds complexity — load balancing, data consistency, distributed failures
- The #1 production mistake: scaling vertically until the ceiling, then scrambling to re-architect for horizontal under fire
- Every mature system uses both — scale up first for simplicity, scale out when you hit the ceiling or need fault tolerance
Scaling is the tactical choice between making a single machine more powerful (vertical) or distributing load across many machines (horizontal). Vertical scaling means upgrading CPU, RAM, or storage on one server — simpler to implement but hits a hard ceiling when the largest available instance can't handle peak traffic.
Horizontal scaling adds more nodes to a cluster, spreading queries and writes across replicas or shards, which offers near-linear capacity growth but introduces consistency, coordination, and network latency problems. The Black Friday scenario exposes this trade-off brutally: a vertically scaled database might handle 10x normal load until it hits memory or I/O limits, while a horizontally scaled one can theoretically absorb 100x but risks partition failures or stale reads under pressure.
Real-world systems like Amazon's DynamoDB and Google's Spanner were built for horizontal scaling from day one, while most PostgreSQL deployments start vertical and only shard when forced. The hidden failure mode is that horizontal scaling demands application-level changes — connection pooling, distributed transactions, and eventual consistency handling — that vertical scaling never requires.
Your bottleneck isn't hardware; it's the scaling strategy you chose when you designed the schema.
Imagine you run a lemonade stand that's getting swamped with customers. Vertical scaling is like buying a bigger, faster blender — same stand, more power. Horizontal scaling is like opening five more lemonade stands on the same street — same blender, more copies. Both serve more customers, but the way you manage them, and the problems you run into, are completely different. That tension — one big machine vs many smaller ones — is exactly what engineers wrestle with every time a system grows.
Every successful product eventually hits the same wall: the system that worked beautifully for 100 users starts groaning under 100,000. Databases time out. API responses slow to a crawl. This is a scaling problem, and how you solve it shapes every architectural decision that follows. The wrong choice costs months of re-engineering while competitors pull ahead.
The core question: do we make our existing machines stronger (vertical), or do we add more machines (horizontal)? That single decision cascades into choices about your database, networking, deployment pipeline, cost structure, and team organization.
The production reality: most teams scale vertically first because it is simpler — upgrade the instance size, done. But vertical scaling has a hard ceiling: the largest available cloud instance. When you hit it, you must re-architect for horizontal scaling, which is orders of magnitude more complex. The teams that plan for horizontal scaling early avoid the painful re-architecture fire drill later. I have watched three separate companies go through that fire drill. It always takes longer than estimated and always ships bugs that the original architecture never had.
Why Your Database Bottleneck Is a Scaling Decision, Not a Hardware Problem
Horizontal scaling (scale-out) adds more machines to a pool; vertical scaling (scale-up) adds more power to a single machine. The core mechanic: horizontal distributes load across nodes, requiring a load balancer and often a distributed data layer; vertical increases CPU, RAM, or I/O capacity on one node, hitting a physical ceiling. For databases, horizontal means sharding or read replicas; vertical means bigger instances.
In practice, vertical scaling is simple to implement — no code changes, just a bigger box — but it's bounded by the largest instance a cloud provider offers (e.g., 24 TB RAM on AWS x2iedn). Horizontal scaling is architecturally complex: you must handle data partitioning, eventual consistency, and network latency. The trade-off is linear cost vs. linear complexity. A single PostgreSQL instance can handle ~10k writes/sec; beyond that, you need read replicas or sharding.
Use vertical scaling when your workload is CPU- or memory-bound with predictable growth, and you can afford the price premium for large instances. Use horizontal scaling when you need fault tolerance, geographic distribution, or write throughput exceeding a single node's capacity. On Black Friday, a vertically scaled monolith will hit a hard ceiling; a horizontally scaled cluster degrades gracefully under load.
Vertical Scaling (Scale Up) — Bigger Machine, Same Architecture
Vertical scaling means increasing the resources of a single server — more CPU cores, more RAM, faster NVMe storage, more network bandwidth. You upgrade the instance type, for example from m5.large to m5.4xlarge, and the application runs on a more powerful machine. Nothing else changes.
The appeal is real: zero code changes. Your application, database, and deployment pipeline all stay exactly the same. You change one variable in a Terraform file or one dropdown in a cloud console, wait for the instance to resize, and you are done. This is why every team starts here — it is the path of least resistance and the correct path at early scale.
The ceiling is also real: every cloud provider has a maximum instance size. AWS's largest general-purpose EC2 instance tops out at 192 vCPUs and 1.5TB of RAM. The largest memory-optimized instance (u-24tb1.metal) has 24TB of RAM and 448 vCPUs — which sounds enormous until you consider a sufficiently large in-memory dataset or a sufficiently high write rate. When you hit the ceiling, you have no choice but to re-architect for horizontal scaling, and that re-architecture often takes three to six months in a codebase that was never designed for distribution.
The single point of failure problem is separate from the ceiling problem and is arguably more dangerous. A vertically scaled system is exactly as available as its one machine. When that machine fails — and it will fail — everything fails with it. This is acceptable at small scale with tolerable downtime. It is not acceptable at any scale where the business depends on uptime.
# io.thecodeforge: Vertical scaling via Terraform — change instance type # This is the simplest scaling intervention: one variable change, no architecture change resource "aws_instance" "forge_api_server" { ami = "ami-0c55b159cbfafe1f0" instance_type = var.instance_type # The only line that changes # BEFORE: instance_type = "m5.large" (2 vCPU, 8 GB RAM) — $0.096/hr # AFTER: instance_type = "m5.4xlarge" (16 vCPU, 64 GB RAM) — $0.768/hr # NOTE: instance_type = "m5.16xlarge" (64 vCPU, 256 GB RAM) — $3.072/hr # Cost grows faster than resource ratio — 8x resources, 32x cost at the top end tags = { Name = "forge-api-server" Environment = "production" Team = "platform" } } variable "instance_type" { description = "EC2 instance type for the API server — change this to scale vertically" type = string default = "m5.large" # Vertical scaling progression for reference: # m5.large → 2 vCPU, 8 GB RAM → $0.096/hr # m5.xlarge → 4 vCPU, 16 GB RAM → $0.192/hr (2x resources, 2x cost) # m5.2xlarge → 8 vCPU, 32 GB RAM → $0.384/hr (4x resources, 4x cost) # m5.4xlarge → 16 vCPU, 64 GB RAM → $0.768/hr (8x resources, 8x cost — still linear here) # m5.16xlarge → 64 vCPU, 256 GB RAM → $3.072/hr (32x resources, 32x cost) # m5.24xlarge → 96 vCPU, 384 GB RAM → $4.608/hr (48x resources — ceiling for this family) }
- Zero code changes — upgrade the instance type, restart, done
- Simpler operations — no load balancers, no data partitioning, no distributed consensus to reason about
- Hard ceiling — every cloud provider has a maximum instance size; when you hit it, re-architecture is mandatory
- Single point of failure — one machine fails, everything on it fails with it; acceptable early, unacceptable at production scale
- Cost grows super-linearly at the top end — a 4x instance often costs 5-6x the smaller one; large instances carry a premium for the privilege of simplicity
Horizontal Scaling (Scale Out) — More Machines, Distributed Load
Horizontal scaling means adding more servers and distributing the load across them. A load balancer sits in front of the fleet and routes each incoming request to any available server. Each server runs the same application, is independently deployable, and can be added or removed without coordinating with the others.
The appeal: no ceiling. You can run 10, 100, or 10,000 servers behind a load balancer. If one server dies, the load balancer stops routing traffic to it and the others absorb its share. This is how Netflix, Amazon, and Google handle billions of requests per day — not by buying progressively larger machines, but by running massive fleets of commodity instances. The machines themselves are unremarkable. The architecture is not.
The complexity cost is real and should not be underestimated. Horizontal scaling requires your application to be stateless — no local session data, no in-memory caches that differ between instances, no files written to local disk. Your data must be replicated or partitioned across servers. Your deployment must handle rolling updates across a fleet without downtime. Load balancing, service discovery, distributed caching, health checking, and graceful shutdown all become mandatory concerns. None of these are hard individually, but together they represent a qualitative shift in operational complexity. This is the real reason teams start with vertical scaling — not because they do not know about horizontal, but because they correctly assess that the complexity is not worth it at small scale.
# io.thecodeforge: Horizontal scaling via Kubernetes HPA # Automatically adds/removes pods based on CPU and memory utilization # The HPA controller evaluates metrics every 15 seconds by default apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: forge-api-hpa namespace: production spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: forge-api minReplicas: 3 # Never drop below 3 — maintains fault tolerance across AZs maxReplicas: 50 # Hard cap — prevents runaway scaling from a traffic spike or bug metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 # Scale up when average CPU across all pods exceeds 70% - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 # Scale up when average memory exceeds 80% behavior: scaleUp: stabilizationWindowSeconds: 60 # Wait 60s before scaling up — prevents thrashing policies: - type: Pods value: 4 # Add at most 4 pods per scaling event periodSeconds: 60 scaleDown: stabilizationWindowSeconds: 300 # Wait 5 minutes before scaling down — conservative policies: - type: Pods value: 2 # Remove at most 2 pods per scaling event periodSeconds: 60 # Gradual scale-down prevents traffic drops
The Hybrid Approach — Scale Up First, Then Out
In practice, every mature system uses both strategies. The question is never purely vertical versus horizontal — it is which strategy applies to which tier, at which point in the system's growth, and for which reason.
The pattern that works: start with a single server and scale vertically until the gains diminish or you approach the ceiling. Then add a second server behind a load balancer — now you have horizontal scaling with two vertically sized instances. As traffic grows further, upgrade the instance type within the fleet (vertical scaling within the horizontal fleet) and add more instances (horizontal growth). When the single primary database becomes the bottleneck, add read replicas for read traffic (horizontal for reads). When read replicas are not enough and the primary write load is the constraint, shard the database (horizontal for writes — the hardest step).
The database is where this gets genuinely difficult. Application servers are easy to scale horizontally because they are stateless and interchangeable. Databases are the opposite — they maintain state, enforce consistency, and are hard to partition correctly. Most teams scale the database vertically as far as possible (large instance, more IOPS, more RAM for buffer pool), then add read replicas, then add PgBouncer, then add a caching layer — and only reach for database sharding when all of those options are exhausted. Sharding is not a first step. It is the step you take when every other option has been tried.
The decision framework is simpler than it looks: scale vertically when the bottleneck is on a single server and you have headroom. Scale horizontally when you need fault tolerance, when traffic is unpredictable, or when you have hit the vertical ceiling. Scale the database vertically longer than you scale the application tier — reads are easy to distribute, writes are hard.
# io.thecodeforge: Hybrid scaling — vertically sized instances in a horizontal auto-scaling fleet # This is the standard production architecture: each instance is large (vertical), # and there are many of them behind a load balancer (horizontal) resource "aws_launch_template" "forge_api" { name_prefix = "forge-api-" image_id = "ami-0c55b159cbfafe1f0" instance_type = "m5.2xlarge" # Vertical: each instance is purposefully large # 8 vCPU, 32 GB RAM per instance # This reduces the number of instances needed # and simplifies connection pool math vpc_security_group_ids = [aws_security_group.api.id] user_data = base64encode(<<-EOF #!/bin/bash # Health check endpoint must respond before instance joins the load balancer systemctl start forge-api EOF ) tag_specifications { resource_type = "instance" tags = { Name = "forge-api" Environment = "production" } } } resource "aws_autoscaling_group" "forge_api" { name = "forge-api-asg" vpc_zone_identifier = var.private_subnet_ids # Spread across 3 AZs for fault tolerance min_size = 3 # Horizontal: minimum 3 instances — one per AZ max_size = 20 # Horizontal: scale out to 20 instances under load desired_capacity = 3 health_check_type = "ELB" # Use load balancer health checks, not EC2 status checks health_check_grace_period = 60 # Give new instances 60s to start before health checking launch_template { id = aws_launch_template.forge_api.id version = "$Latest" } target_group_arns = [aws_lb_target_group.api.arn] } resource "aws_lb" "forge_api" { name = "forge-api-alb" internal = false load_balancer_type = "application" subnets = var.public_subnet_ids # ALB spans public subnets, instances in private } # Read replica for the database — horizontal scaling for reads # The application routes SELECT queries here, writes go to the primary resource "aws_db_instance" "forge_db_replica" { identifier = "forge-db-replica-1" replicate_source_db = aws_db_instance.forge_db_primary.identifier instance_class = "db.r5.2xlarge" # Vertical: replica sized for read workload publicly_accessible = false skip_final_snapshot = false }
Hidden Costs and Failure Modes — What Shows Up After the Decision
Both strategies have hidden costs that only surface at scale, and both have failure modes that are not obvious until you have experienced them.
Vertical scaling costs grow super-linearly at the top end. A 4x instance does not cost 4x — at the upper end of instance families, it often costs 5-6x because cloud providers charge a premium for large instances. A db.r5.24xlarge at $13.34 per hour costs more than twice what 24 db.r5.large instances cost at $0.27 per hour each ($6.48/hour total). You are paying a 2x premium for the operational simplicity of a single machine. At small scale, that premium is worth it. At scale, it is not.
Horizontal scaling has operational costs that do not appear on the infrastructure bill. Load balancers add 1-5ms of latency per request. Distributed caching with Redis adds a network round trip on every cache miss. Data partitioning adds query planning complexity and eliminates cross-shard joins. Rolling deployments across 50 servers take 10-15 minutes instead of 2 minutes for one. Distributed failure modes — where 30% of your servers are healthy, 50% are degraded, and 20% are failing — are orders of magnitude harder to diagnose than a single server that is clearly down.
The production trap that I have seen teams fall into more than any other: scale vertically until you cannot, then panic-architect for horizontal under live incident pressure. The re-architecture takes 3-6 months, is done by an exhausted team operating in crisis mode, and reliably introduces new categories of bugs that the original single-server codebase never had — race conditions, cache consistency bugs, connection pool exhaustion after adding instances. The teams that plan for horizontal scaling from day one — even if they only run one server — avoid this entirely. You can run one stateless server behind a load balancer from the start. There is no penalty for being ready.
# io.thecodeforge: Real AWS cost comparison — Vertical vs Horizontal # Data as of 2026 (us-east-1, on-demand pricing, RDS PostgreSQL) ## Option A: Vertical Scaling — Single Large Instance # db.r5.24xlarge: 96 vCPU, 768 GB RAM # Cost: $13.338/hour = $9,803/month # Fault tolerance: NONE — this single instance is your entire database # Scaling ceiling: you are at it # Recovery time: 15-30 minutes for RDS failover to a standby (if configured) ## Option B: Horizontal Scaling — Fleet of Medium Instances # 24x db.r5.large: 2 vCPU, 16 GB RAM each # Total resources: 48 vCPU, 384 GB RAM (half the vertical option) # Cost: 24 × $0.270/hour = $6.48/hour = $4,739/month # Fault tolerance: 23 of 24 instances can fail and reads continue # Savings: 52% cheaper with better fault tolerance ## Option C: Practical Hybrid — Primary + Read Replicas + Cache # 1x db.r5.4xlarge primary (writes): $1.112/hour # 3x db.r5.2xlarge replicas (reads): 3 × $0.556/hour = $1.668/hour # 1x ElastiCache r6g.xlarge (Redis): $0.226/hour # Total: $3.006/hour = $2,200/month # Handles 80% of the read volume of Option A at 22% of the cost # This is the architecture most teams should be running ## Hidden Costs of Horizontal (not on the compute bill): # - Application Load Balancer: ~$20/month + data processing fees # - Engineering time for deployment: rolling updates take 5-10x longer # - Monitoring and alerting: 24 instances vs 1 — dashboard complexity grows # - On-call cognitive load: distributed failure modes are harder to diagnose ## Rule of Thumb (2026 pricing): # Monthly infra cost < $500: single server, vertical scaling — simplicity wins # Monthly cost $500–$5,000: evaluate hybrid — read replicas + cache before sharding # Monthly cost > $5,000: horizontal fleet is almost always cheaper and more resilient # Any production system: always have at least one read replica — fault tolerance is not optional
The Real Cost of Scaling: Session State and You
Nobody talks about session state until the second outage. With vertical scaling, your session lives in the same machine's memory. Simple. Fast. Fragile. The moment you scale out horizontally, that session state becomes a distributed systems problem. Sticky sessions? They defeat the purpose of horizontal scaling. Centralized Redis? Great, but now Redis is your single point of failure unless you cluster it. I've seen teams burn three sprints migrating from in-memory sessions to a distributed cache after a 'simple' scale-out. The lesson: design your application to be stateless from day one. Use JWT tokens or externalize session storage before you ever need to scale. If you can't, vertical scaling might be the safer bet until you can refactor. Statelessness is the hidden tax on horizontal scaling.
# io.thecodeforge.session_trap # Bad: Sticky sessions force traffic to one node # This defeats horizontal scaling import random nodes = ['app-1', 'app-2', 'app-3'] user_id = 42 # Sticky session: always route to the same node sticky_node = nodes[hash(str(user_id)) % len(nodes)] print(f"Routing user {user_id} to {sticky_node} (sticky)") # Output: Routing user 42 to app-2 (sticky) # Good: Stateless with JWT import jwt token = jwt.encode({'user_id': user_id}, 'secret', algorithm='HS256') # Any node can handle this request print(f"Token: {token}") # Output: Token: eyJ0eXAi... (decoded by any node)
Read vs. Write Scaling: They Are Not the Same Problem
Every scaling conversation starts with 'more traffic.' That's wrong. You need to ask: read traffic or write traffic? Vertical scaling handles both equally, because it's one big box. Horizontal scaling forces a choice. Reads are easy: add more read replicas, put a cache in front, use CDNs. Writes are brutal. Each new node means more coordination, more conflict, more complexity. I've seen teams add five read replicas and wonder why their write latency tripled. It's because they didn't think about the write path. For write-heavy workloads, vertical scaling often wins until you absolutely must split. The CAP theorem is real. If you need horizontal writes, you're choosing between consistency and availability. Make that choice explicit before you touch a config file.
# io.thecodeforge.read_vs_write # Simulating read vs write scaling behavior import time class ReadOnlyReplica: def query(self): return "Data from replica (fast)" class WritePrimary: def __init__(self): self.lock = False def write(self): self.lock = True time.sleep(0.5) # Simulate write overhead self.lock = False return "Written to primary (slow)" # Scenario: 3 read replicas, 1 write primary replicas = [ReadOnlyReplica() for _ in range(3)] primary = WritePrimary() # Reads scale fine for r in replicas: print(r.query()) # Output: Data from replica (fast) x3 # Write still hits the single bottleneck print(primary.write()) # Output: Written to primary (slow)
Black Friday traffic spike — vertically scaled database hits ceiling, entire checkout pipeline fails
- Vertical scaling has a hard ceiling — plan for horizontal scaling before you hit it, not the morning you discover you already have
- A single database instance with no read replicas is a single point of failure and a scaling dead end simultaneously
- PgBouncer connection pooling is the lowest-effort, highest-impact emergency scaling intervention available — it requires no application code changes and reduces connection count by 70-90%
- The cost of re-architecting under fire is 10x the cost of planning ahead — the team spent 3 months post-incident doing work they could have done in 3 weeks if they had not been racing against a live outage
top -bn1 | head -20 # snapshot CPU and memory per processiostat -x 1 5 # check disk I/O wait — high %iowait means disk is the bottleneckpsql -c "SELECT count(*) FROM pg_stat_activity;"psql -c "SELECT setting::int AS max_conn FROM pg_settings WHERE name='max_connections';"kubectl logs <new-pod> --tail=50 # check for connection refused or timeout errors at startupkubectl exec <new-pod> -- curl -sv http://database-host:5432 # verify network reachability from the new instance| Aspect | Vertical (Scale Up) | Horizontal (Scale Out) |
|---|---|---|
| Definition | Add more resources to one server — bigger CPU, more RAM, faster disk | Add more servers to the fleet — identical instances behind a load balancer |
| Code changes required | None — same application, same deployment, same everything | Often required — stateless design, externalized sessions, data partitioning |
| Ceiling | Maximum instance size from the cloud provider — finite and knowable in advance | No theoretical limit — add as many instances as the workload requires |
| Fault tolerance | Single point of failure — one machine fails, everything fails | Survives individual server failures — load balancer routes around unhealthy instances |
| Operational complexity | Low — one server to monitor, one deployment to manage, one failure domain | High — load balancing, distributed state, health checking, rolling deployments, distributed failure modes |
| Cost at scale | Super-linear — large instances carry a per-unit cost premium that compounds | Linear — commodity instances at consistent per-unit pricing; cheaper per resource unit at scale |
| Implementation time | Minutes — change instance type in Terraform or cloud console | Weeks to months — stateless re-architecture, load balancer configuration, distributed data layer |
| Auto-scaling | Not possible — fixed instance size; must schedule downtime to resize | Native — add or remove instances dynamically based on load with zero downtime |
| Typical use case | Early-stage products, small teams, databases (harder to shard), internal tools | High-traffic production systems, fault-tolerant APIs, globally distributed services |
| File | Command / Code | Purpose |
|---|---|---|
| io | resource "aws_instance" "forge_api_server" { | Vertical Scaling (Scale Up) |
| io | apiVersion: autoscaling/v2 | Horizontal Scaling (Scale Out) |
| io | resource "aws_launch_template" "forge_api" { | The Hybrid Approach |
| session_trap.py | nodes = ['app-1', 'app-2', 'app-3'] | The Real Cost of Scaling |
| read_vs_write.py | class ReadOnlyReplica: | Read vs. Write Scaling |
Key takeaways
Common mistakes to avoid
5 patternsScaling vertically until the ceiling, then panic-architecting for horizontal under live incident pressure
Adding more application servers when the database is the actual bottleneck
Scaling horizontally without making the application stateless first
Not accounting for connection pool exhaustion when scaling the application tier horizontally
Ignoring the cost super-linearity of vertical scaling at the top of the instance family
Interview Questions on This Topic
Explain the difference between horizontal and vertical scaling. When would you choose one over the other?
You have a monolithic application running on the largest available EC2 instance. Traffic is growing 20% month-over-month. What is your scaling strategy?
How would you design a system that needs to handle 1 million requests per second?
What is the 'shared-nothing' architecture and how does it relate to horizontal scaling?
What is the difference between scaling for reads vs scaling for writes in a database?
Frequently Asked Questions
Vertical scaling is buying a bigger machine — more CPU, more RAM, same server, same architecture. Horizontal scaling is buying more machines — same size, more copies behind a load balancer. Vertical is simpler to operate but has a ceiling and a single point of failure. Horizontal has no ceiling and survives individual server failures, but requires the application to be stateless and adds distributed system complexity.
At small scale, vertical is cheaper because you avoid the operational complexity and tooling costs of a distributed system. At large scale, horizontal is cheaper because large instances cost disproportionately more per unit of resource than small ones — a 4x instance often costs 5-6x the price, while 4 small instances cost exactly 4x. The crossover point is roughly $500-$5,000 per month in infrastructure spend, depending on your cloud provider's pricing for your specific instance family.
Yes — and every mature production system does. The standard architecture is a fleet of medium-to-large instances behind a load balancer: each instance is vertically sized for efficiency (not the smallest possible), and the fleet scales horizontally based on load. You tune instance size (vertical) and fleet size (horizontal) independently. This gives you the operational simplicity of predictable per-instance behavior and the elasticity of horizontal auto-scaling.
For reads, yes — add read replicas and route SELECT traffic to them. The read path scales linearly with the number of replicas, constrained only by replication lag on the write side. For writes, it is fundamentally harder — you need to shard the data across multiple primary databases, each owning writes for a specific data subset determined by a shard key. Sharding adds query complexity, eliminates cross-shard joins, complicates transactions, and requires careful shard key selection to avoid write hotspots. The practical path: exhaust read replicas and a caching layer before attempting write sharding. Most teams that think they need to shard actually need a Redis cache in front of their database.
20+ years shipping large-scale distributed systems. Lessons pulled from things that broke in production.
That's Fundamentals. Mark it forged?
6 min read · try the examples if you haven't