SD System Design In Depth
0%
lesson system-designtradeoffsarchitecture

System design tradeoffs

Module 1.1: Foundations & System Invariants Updated Sep 17, 2026
📄 Cheat Sheet
System Design Was HARD - Until You Knew the Trade-Offs
ByteByteGo System Design Was HARD - Until You Knew the Trade-Offs Watch on YouTube ↗

Concise, well-animated walk through the classic trade-off pairs (consistency vs availability, latency vs throughput, normalization vs denormalization, sync vs async) with the reason each cost appears.

0:00 / --:--

System design tradeoffs

A design tradeoff is a choice between useful properties that the available options cannot all provide equally well. A cache may reduce read work while serving older data. A separate service may allow independent releases while adding network failures to handle.

The useful explanation names the benefit, the cost, and the requirement that makes the choice reasonable. We will work through one cache decision, then use the same method on other common choices.

Compare two answers to the same requirement

Suppose a product page repeatedly loads a seller's display name. Reading the database each time keeps the path simple. Caching the name can avoid repeated database work, but an edit may take time to appear wherever the cached value is used.

First ask whether that delay is acceptable. If the product allows a briefly outdated display name, an expiry or invalidation policy can be part of the design. If the requirement says every read must reflect a completed edit, a cache that can serve an old value does not meet it.

Now change the field from display name to “this seller may accept payments.” A stale answer can permit an operation that should have been stopped. Similar data shapes do not imply similar correctness requirements.

Write the decision in a form another engineer can challenge:

Cache seller display names to reduce repeated reads. Accept the agreed update delay, and check current payment eligibility separately before accepting an order.

The explanation makes two boundaries visible: which field may be stale and which operation needs a stronger check. “Use a cache for speed” leaves both unresolved.

Follow the cost to the place it appears

Many costs appear outside the component being optimized:

ChoiceWhat it can improveWhat the design must also handle
Cache a read resultRepeated lookup work and latencyFreshness, invalidation, memory and cache misses
Move work to a queueAbsorb bursts and schedule workers separatelyUser-visible delay, backlog, retries and duplicate work
Coordinate reads or writes across replicasStronger visibility or ordering guaranteesNetwork waits; some operations may stop when required participants cannot communicate
Store a derived copy of dataSimpler or faster readsExtra writes and keeping the copies in agreement
Split a serviceIndependent ownership, scaling or deploymentRemote-call failure, observability and additional operations work
Add detailed metric labelsMore ways to isolate a problemMore time series to store and query
A queue does not create processing capacity. If workers cannot keep up, the waiting time grows. Similarly, adding a metric label containing every user ID can make a small set of measurements expand with the user population. The cost follows the behavior, not the number of boxes in the drawing.

These are possibilities to investigate, not universal verdicts. A cache with few hits may add work. Services with shared release dependencies may still have to deploy together. Test whether the proposed change actually buys the property being claimed.

Record when to reconsider

A useful decision also names the evidence that would change it. Revisit the display-name cache if stale names create support problems, if its hit rate is too low to justify it, or if an indexed database read already meets the target cheaply.

Keep the rejected alternative in the design note. The next engineer should be able to tell why a direct read was insufficient, what freshness delay was accepted, and how that assumption was checked.

Not every improvement requires a sacrifice. Removing an unnecessary query can reduce latency, cost, and failure exposure together. Look for such fixes before negotiating away a product promise.

For your next design choice, name the alternative and trace one failure. If the cost is still only “more complexity,” say which retry, migration, stale result, or on-call task creates that complexity. When not to add infrastructure applies this test before introducing another service to operate.


System design tradeoffs

A design tradeoff is a choice between useful properties that the available options cannot all provide equally well. A cache may reduce read work while serving older data. A separate service may allow independent releases while adding network failures to handle.

The useful explanation names the benefit, the cost, and the requirement that makes the choice reasonable. We will work through one cache decision, then use the same method on other common choices.

Compare two answers to the same requirement

Suppose a product page repeatedly loads a seller's display name. Reading the database each time keeps the path simple. Caching the name can avoid repeated database work, but an edit may take time to appear wherever the cached value is used.

First ask whether that delay is acceptable. If the product allows a briefly outdated display name, an expiry or invalidation policy can be part of the design. If the requirement says every read must reflect a completed edit, a cache that can serve an old value does not meet it.

Now change the field from display name to “this seller may accept payments.” A stale answer can permit an operation that should have been stopped. Similar data shapes do not imply similar correctness requirements.

Write the decision in a form another engineer can challenge:

Cache seller display names to reduce repeated reads. Accept the agreed update delay, and check current payment eligibility separately before accepting an order.

The explanation makes two boundaries visible: which field may be stale and which operation needs a stronger check. “Use a cache for speed” leaves both unresolved.

Follow the cost to the place it appears

Many costs appear outside the component being optimized:

ChoiceWhat it can improveWhat the design must also handle
Cache a read resultRepeated lookup work and latencyFreshness, invalidation, memory and cache misses
Move work to a queueAbsorb bursts and schedule workers separatelyUser-visible delay, backlog, retries and duplicate work
Coordinate reads or writes across replicasStronger visibility or ordering guaranteesNetwork waits; some operations may stop when required participants cannot communicate
Store a derived copy of dataSimpler or faster readsExtra writes and keeping the copies in agreement
Split a serviceIndependent ownership, scaling or deploymentRemote-call failure, observability and additional operations work
Add detailed metric labelsMore ways to isolate a problemMore time series to store and query

A queue does not create processing capacity. If workers cannot keep up, the waiting time grows. Similarly, adding a metric label containing every user ID can make a small set of measurements expand with the user population. The cost follows the behavior, not the number of boxes in the drawing.

These are possibilities to investigate, not universal verdicts. A cache with few hits may add work. Services with shared release dependencies may still have to deploy together. Test whether the proposed change actually buys the property being claimed.

Record when to reconsider

A useful decision also names the evidence that would change it. Revisit the display-name cache if stale names create support problems, if its hit rate is too low to justify it, or if an indexed database read already meets the target cheaply.

Keep the rejected alternative in the design note. The next engineer should be able to tell why a direct read was insufficient, what freshness delay was accepted, and how that assumption was checked.

Not every improvement requires a sacrifice. Removing an unnecessary query can reduce latency, cost, and failure exposure together. Look for such fixes before negotiating away a product promise.

For your next design choice, name the alternative and trace one failure. If the cost is still only “more complexity,” say which retry, migration, stale result, or on-call task creates that complexity. When not to add infrastructure applies this test before introducing another service to operate.

Check yourself Question 1 of 3
A complete trade-off statement recall
From: System design tradeoffs

Which statement is a complete trade-off: it names the benefit, the cost, and the requirement that makes the cost acceptable?

Architecture Challenge: Evaluating Architectural Tradeoffs
Staff Level
Context / Scenario: Financial ledger with multi-currency transfers across international banks.
Problem: When designing a financial transactions ledger (like Stripe or PayPal), which tradeoff priority is strictly mandatory?
A Use single-leader asynchronous replication across continents without idempotency keys.
B Prioritize extreme low latency (< 1ms) by accepting eventual consistency and asynchronous dirty writes.
C Prioritize Strict Serializability and Strong Consistency (ACID) over availability (preferring CP in CAP) to prevent duplicate balances.
D Store account balances in Redis in-memory caches without Write-Ahead Logging to maximize write throughput.

Key Takeaways

Every architectural decision trades one property for another (consistency vs. availability, latency vs. throughput).
Identify which constraints are hard (legal, physical) vs. soft (optimization targets) early in design.
Document tradeoff decisions with rationale so future engineers understand why alternatives were rejected.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue