Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Why-Not Handbook

Overview

Most engineering material explains why an approach works. This handbook answers the opposite, harder question: why not the alternative? Each page takes a pair — or a small set — of options that interviewers, code reviewers, and designers routinely contrast (BFS vs DFS, hashing vs binary search, mutex vs semaphore, B-tree vs LSM, caching vs recomputation) and explains the mechanism behind each option deeply enough that the trade-offs fall out as consequences rather than memorized slogans.

The collection deliberately overlaps with the Trade-offs section, which stores quick-reference comparison tables. There, a one-line table row says “hash: O(1) average, no ordering”. Here, the same comparison is re-derived from first principles: how the hash bucket is probed, what the cache does to that probe, what happens under adversarial keys, and the specific workloads where the binary search that “loses” on paper is the correct engineering answer. If you want a fast answer, read trade-offs; if you want to defend that answer under three rounds of “why?”, read here.

The Six-Step Meta-Framework

Every page in this handbook applies the same six steps, in the same order. The order is the point: each step narrows the space so the final recommendation is a consequence of evidence, not taste.

  1. Define the problem and its constraints. A comparison without a workload is a slogan. Before comparing, the pages pin down: what the input looks like (size, distribution, mutation rate), what guarantees the caller needs (exact vs approximate, ordering, freshness, worst-case bounds), and what resources are fixed (memory budget, latency budget, hardware). Binary search vs hashing is undecidable until someone says whether range queries are needed and whether keys are adversarial.
  2. Explain the mechanism of each option. Not “hash tables are O(1)” but why: hash the key, index into a bucket array, resolve collisions by chaining or probing, resize when load factor exceeds a threshold. Mechanism-level explanations expose the constants, the memory behavior, and the failure modes that asymptotic notation hides. Two operations with the same big-O can differ by 50× in practice, and the mechanism tells you which one.
  3. State complexity, guarantees, and failure modes. For each option: worst-case, average-case, and amortized complexity where they differ; the formal guarantees it gives (ordering, stability, linearizability, bounded latency); and the characteristic ways it breaks (rehash storms, deadlocks, frontier blowups, write skew). Guarantees are contracts — the pages state exactly which contract each option signs.
  4. Name the conditions where each option wins. Not a generic ranking, but explicit trigger conditions: “DFS wins when the graph is deep and memory is bounded”, “LSM wins when write throughput dominates and reads tolerate milliseconds of staleness”. Every option in every page is the right choice somewhere — the handbook has no strawmen.
  5. Find the edge cases and counterexamples. This is where most material stops and this handbook starts. Each page includes at least one non-obvious case where the “obvious” choice fails: BFS frontier blowup on lattice graphs, hash-flooding attacks, lost wakeups in condition variables, write skew under snapshot isolation, cache stampedes after an invalidation. These cases are what interviewers use to separate rote answers from real understanding.
  6. Write the decision framework. Each page ends with a table or ordered checklist mapping workload properties to a choice. The framework is falsifiable: if your workload matches the left column, the right column is the recommendation — and the page has already told you what you are giving up.

The framework generalizes beyond the forty comparisons here. When you meet a fresh “X vs Y” question, running these six steps produces an answer that is specific to your constraints and honest about costs.

File Inventory

PageCore questionFlagship edge case covered
bfs-vs-dfsSame O(V+E) complexity — why does traversal order change what you can guarantee?BFS frontier blowup on grid/lattice graphs; recursion depth overflow in DFS
binary-search-vs-hashingO(log n) guaranteed vs O(1) average — which contract do you need?Hash-flooding adversarial keys; cache-friendly sorted arrays beating hash tables at small n
arrays-vs-linked-listsWhen is O(1) insertion real, and when is cache locality worth more?“O(1) insertion” that requires an O(n) search first; reallocation invalidating iterators
sorting-algorithmsFive n-log-n-class algorithms — why do libraries ship hybrids?Naive quicksort’s O(n²) collapse on sorted input; radix sort’s hidden per-pass costs
concurrency-primitivesMutex vs semaphore vs condvar; processes vs threads; polling vs event-driven I/OLost wakeup; crash blast radius; busy-polling trading CPU for tail latency
database-choicesNormalization vs denormalization; B-tree vs hash vs LSM vs GiST/GIN; optimistic vs pessimisticWrite skew under snapshot isolation; LSM read amplification without bloom filters
caching-distributedCaching vs recomputation; consistency vs availability vs latencyCache stampede after invalidation; per-operation CAP choices inside one system
crash-recoveryWAL vs in-place vs append-only/COW: where does atomicity live on disk?Torn pages defeat redo deltas — full-page writes and doublewrite exist; append-only’s compaction as slow-motion recovery
push-vs-pullWho owns the pacing — consumer or producer — and what failure does each import?Retry storm after receiver recovery; slow consumer under push turns broker memory into the failure
replication-modesWhat exactly does a commit OK promise about the second copy?MySQL semi-sync silently downgrades to async during the failure it exists for; PG sync stalls indefinitely
scaling-up-vs-outWhich part of the workload is serial, and what makes the serial remainder irrelevant?The global counter hot row sharding cannot fix; Amdahl crossover when coordination grows with node count
stateless-vs-statefulEvery system has state — which placement’s failure mode can you afford?JWT logout that logs out nothing until expiry; per-node rate limiter undercounting by the fleet size
static-vs-dynamic-typingWhen is type information checked, and which error class does each choice relocate?TypeScript’s erased boundary — the typed lie at unvalidated data edges; the orthogonal strong/weak axis (Python vs JS vs Java)
monolith-vs-microservicesWhat failure layer do you import when the network becomes a design layer?The 27× retry cascade through three layers of 3× retries; five engineers operating a distributed monolith
rest-vs-grpcWhere does the API contract live — social convention or compiled artifact — and who is the client?gRPC-Web exists because browsers cannot read trailers; long-lived HTTP/2 channels unbalancing L4 load balancers
orm-vs-raw-sqlWho owns the query text, and what does each ownership model hide?ORM-generated predicates that defeat indexes (function-wrapped columns, implicit casts); identity-map staleness
tcp-vs-udpWhere does reliability live — transport or application — and who owns congestion?TCP’s loss-based congestion control misdiagnosing wireless loss; UDP MTU fragmentation black holes
sync-vs-async-ioWhat does the program do while waiting for the kernel, and who hides the wait?The async file I/O myth on Linux (epoll reports regular files always-ready); one blocking call freezing an event loop
snowflake-vs-uuidWho generates the ID, what does the B-tree experience, and what do strangers learn?UUIDv4 index fragmentation multiplied across every secondary index; sharded auto-increment steps that resharding breaks
row-vs-column-storesWhich questions is the physical layout cheap at — few rows/all columns or all rows/few columns?HTAP’s async staleness seam; compaction starvation under chatty updates; “Cassandra is a column store” (data-model vs storage axis)
password-hashing-choicesHow is verification made deliberately expensive, and what does each hardness buy?bcrypt’s silent 72-byte truncation (and the pre-hashing zero-byte bug it spawns); the login endpoint as a DoS amplifier
graphql-vs-restWho shapes the response — and who pays for shape?Introspection hands attackers the schema map; the request-scoped DataLoader that leaks; one GraphQL round trip slower than three cached REST calls
batching-vs-streamingWhen does processing start — after a window or on arrival?The lambda architecture’s dual-truth drift; the micro-batch interval that is three dials in one; the backfill a pure streaming system cannot perform
etl-vs-eltWhich engine owns transformation, and does raw data land?The raw-zone PII masking race; silent schema drift landing loud downstream; streaming CDC that fits neither letter
monorepo-vs-multirepoIs coordination tooling or process — where do contracts break?The shared package that never reached 2.0 (polyrepo upgrade deadlock); monorepo CI collapse without affected-target computation; access control fragmenting the monorepo anyway
canary-vs-blue-greenHow is the new version exposed over time, and who notices failure?Dual-write doubling side effects in the overlap window; sticky sessions poisoning canary statistics; the migration that broke the rollback target
virtual-dom-vs-signalsWhere is change detection computed — diff, track, or compile?The memoization treadmill (hand-written dependency graphs); torn reads in naive signal graphs; the 10,000-row list that signals do not save
normalize-vs-denormalizeWhere does complexity live — the read path or the sync path?JSON columns as transactionally-synced denormalization (and the planner-statistics trap); the index as the denormalization you already accepted; the join that outgrew a node
rpc-vs-message-queueWho waits for the answer, and who owns the failure — in-line errors vs deferred lagRequest-reply over queues: waiting re-added while every RPC guarantee is removed; the six-hour consumer outage a lag graph failed to page
docker-compose-vs-kubernetesOne host’s daemon vs a cluster’s reconciling control plane — what “restart” even meansThe scale: 5 that validated horizontal scaling on a single CPU; SIGTERM-and-readiness choreography compose never taught the app
app-events-vs-cdcWho declares what happened — application intent outside the transaction, or the log’s committed truthThe outbox pattern as the bridge (and its retention/ordering edges); the 3 a.m. SQL fix CDC caught and application events missed
serverless-vs-containersWho owns the idle time — per-invocation billing vs provisioned capacityThe websocket gateway billed 100% utilization per connection; cold starts that only fire during incidents; the cron job that crossed the execution ceiling
regex-vs-parserPattern matching vs grammar parsing — where the nesting boundary sitsRecursive descent triggered at “one more level of nesting”; the silent wrong-match that passes validation; the engine guarantee split (backtracking vs automata)
kafka-vs-rabbitmqWhat does the broker promise — a replayable log or an ack-and-delete queue?The prefetch window that flips at-least-once into at-most-once on crash; retention expiry silently deleting un-consumed records
elasticsearch-vs-db-fulltextWho owns the inverted index, and what does that ownership cost?The GIN pending-list merge cliff; BM25’s shard-local idf skewing relevance on small sharded indexes
jwt-vs-sessionsWhere does authentication state live, and what does revocation cost?The denylist that converges back into a session table; clock drift expiring a fleet’s tokens at once
vms-vs-containersWhich boundary draws the isolation line — hypervisor or shared kernel?The shared-kernel fate that turns one syscall 0-day into cross-tenant escape; the pids controller nobody set
logical-clocks-lamport-vs-vectorTotal order or concurrency detection — what can each clock even see?The concurrent update Lamport timestamps silently order into a fabricated “last write”; vector pruning that deletes exactly the concurrency it exists to catch
optimistic-vs-pessimistic-lockingWhen does contention pay for coordination up front versus at commit?The lock held across an HTTP call exhausting the connection pool; the 100-writer commit stampede without backoff
sagas-vs-two-phase-commitWhere does uncertainty live while a transaction crosses services?The coordinator crash that froze the ledger’s in-doubt locks; the compensation that raced an already-shipped order

Coverage Ledger

The same concepts appear across several research projects from deliberately different angles — the coverage plan requires linking, not re-explaining. The ledger below records which angle each project takes so you know where to go next. (Paths are relative to pages in this directory.)

ConceptThis handbook’s angleElsewhere
Hash tablesVs binary search: ordering, guarantees, adversarial keysFollow-up Tree (escalation ladder), Misconceptions (the false O(1) claim), Assumptions Hunt (adversarial inputs)
DB index structuresB-tree vs hash vs LSM vs GiST/GIN by workloadConnection Atlas (dependency web), Indexing strategy (practical SQL guide)
Mutexes and lockingOwnership semantics and signaling vs exclusionFollow-up Tree (escalation to lock-free), Misconceptions (threads-equal-speed myth)
Isolation and concurrency controlOptimistic vs pessimistic decision ruleAssumptions Hunt (implicit isolation assumptions)
Caching and backpressureCaching vs recomputation; staleness vs availabilityConnection Atlas (queue-full path), Cache patterns (operational playbooks)
High-level comparison tablesDeep mechanisms hereTrade-offs (quick-reference tables)
Crash recovery and WALWAL vs in-place vs append-only by failure mechanismWAL Internals (engine mechanics), Storage Durability (device-level assumptions)
Push vs pullPacing ownership and where each mode relocates failureBackpressure: Atlas (the queue-full path), Kafka and Pulsar (pull vs push brokers)
Replication modesWhat the commit acknowledgment promisesPrimary-Backup (topology), GitHub October 2018 (async failover incident)
Scaling directionAmdahl crossover; contention is not partitioningAmdahl’s Law (the arithmetic), Relational DBs Don’t Scale (the anti-slogan)
State placementFailure-mode and revocation-latency relocation per state typeLeases (ownership primitive), Consensus & Leases: Atlas (safety under partitions)
Type systemsChecking time, erasure, and the orthogonal strong/weak axisPython Typing and TypeScript (the gradual middle path)
Architecture styleThe network as a failure layer; Conway-aligned team topologyArchitecture Trade-offs (quick tables), Microservices (operational catalog)
API style (REST vs gRPC)Contract location, reachability, caching, deadline propagationREST and gRPC (protocol mechanics), API Contracts (contract assumptions)
Query ownership (ORM vs SQL)Who writes the text the planner seesQuery Planner (plan mechanics), Query Planner Dataflow (text-to-execution web)
Transport choice (TCP vs UDP)Where reliability lives; QUIC as the synthesisTCP vs UDP (quick tables), QUIC (reliability rebuilt on UDP)
I/O model (sync vs async)Who hides the wait: scheduler, readiness, or completion queuesI/O Multiplexing (syscall mechanics), io_uring (completion-based design)
Identifier design (auto-increment vs UUID vs Snowflake)Generation authority, index locality, leakageB+Tree (locality mechanics), URL Shortener (ID design as a design interview)
Physical layout (row vs column)Access-shape economics; HTAP as the hybrid answerColumn Stores (layout depth), HTAP (the hybrid architecture)
Password hashing (SHA-2 vs bcrypt/scrypt/Argon2)Cost-per-guess engineering and memory hardnessSecurity & Crypto (deployment assumptions), Cryptography (the primitives)
API style (GraphQL vs REST)Who shapes the response; caching, query cost, introspectionGraphQL (the query language), REST (the resource model), REST vs gRPC (the contract-location fork)
Pipeline pacing (batch vs streaming)When processing starts: windows vs continuousBatch Processing and Stream Processing (mechanics), Exactly-Once Semantics (the stream tax)
Transform placement (ETL vs ELT)Which engine transforms; whether raw landsSnowflake (the elasticity that flipped ELT), Debezium (streaming CDC)
Repository strategy (monorepo vs multirepo)Coordination as tooling vs processGit Workflows (branching inside either shape), Git Workflow Assumptions (what history promises)
Deployment strategy (canary vs blue-green)Exposure over time; detection vs rollbackCanary Releases (the playbook), Feature Flags (software-layer exposure)
UI update model (VDOM vs signals)Where change detection is computedReact Internals (diff/scheduler mechanics), Svelte (the compiler-driven middle)
Physical schema (normalize vs denormalize)Copies and their sync strategies, uniformlyDenormalization (read-optimization patterns), Incremental View Maintenance (engine-maintained copies)
Communication style (RPC vs queues)Who waits; where failures surface; delivery semantics per sideMessage Queues: Follow-up Tree (escalation ladder), Queues and Kafka (broker mechanics)
Orchestration level (Compose vs Kubernetes)Scheduling unit: host daemon vs fleet control planeKubernetes Pod Lifecycle: Follow-up Tree (reconciliation mechanics), Deployments (rolling updates)
Event declaration (app events vs CDC)Who owns truth; the dual-write hole and the outbox bridgeEvent Sourcing & WAL: Atlas (the log kinship), Debezium (CDC connector mechanics)
Execution model (FaaS vs containers)Who owns idle time; cost curves, cold starts, ceilingsServerless internals (scale-to-zero mechanics), Firecracker (the microVM substrate)
Text structure (regex vs parser)Where nesting breaks patterns; engine guarantee classesRegex Engines: Follow-up Tree (engine ladder), Regex Input Assumptions (pattern-encoded assumptions)
Message broker choice (Kafka vs RabbitMQ)What the log-versus-queue mechanism decides about replay, delivery state, and failureMessage Queues: Follow-up Tree (escalation ladder), Kafka and RabbitMQ (broker internals)
Full-text search (database vs search engine)Who owns the inverted index and pays for analysisSearch & Inverted Index: Atlas (dependency web), Full-Text Search (SQL-side mechanics)
Authentication state (JWT vs sessions)Where identity truth lives and what revocation latency costsAuth, Identity, Session: Atlas (the auth web), JWT is Encrypted (the signature confusion)
Isolation boundary (VMs vs containers)What the hypervisor isolates versus what the kernel partitionsContainers Are VMs (the myth), VM vs Container (quick tables)
Time and ordering (Lamport vs vector clocks)Causality capture versus total order — detection vs serializationClocks and Time (physical-time assumptions), Hybrid Logical Clocks (the synthesis)
Row concurrency control (optimistic vs pessimistic)Contention economics: waiting versus wasted workDatabase Isolation (isolation assumptions), Transactions & Concurrency Control: Atlas (dependency web)
Distributed commit (sagas vs 2PC)Blocking coordination versus compensating workflowsACID Transactions: Follow-up Tree (escalation ladder), Event Sourcing & WAL: Atlas (the outbox/log kinship)

Duplication boundary (from the project plan): trade-offs pages hold the scannable tables; this handbook holds mechanisms, complexity derivations, guarantees, and edge cases. If you find yourself copying a table between the two, link instead.

How to Use This in an Interview

A strong “why not X?” answer has three moves. First, name the mechanism of the rejected option (“a hash table probes one bucket after computing a hash”). Second, state the guarantee you actually need (“I need predecessor queries and ordered iteration”). Third, conclude conditionally, not absolutely (“so for this workload the hash table cannot substitute; for pure point lookups it would be faster”). Pages here give you all three moves pre-assembled, plus the edge case that turns a good answer into a memorable one.

Interview Questions

  1. Why does the handbook insist on mechanisms before trade-offs? Because every trade-off line item (“O(1) average”, “stable”, “no ordering”) is a consequence of a mechanism. Deriving consequences from mechanisms lets you answer variants you never memorized — new workload, new failure mode, new hardware — while a memorized table breaks the moment the question changes.
  2. What does “conditions where each wins” prevent? Strawman comparisons. If your explanation of an option cannot name a workload where it is the best choice, you do not understand the option — you have only memorized its weaknesses.
  3. Give an example of an edge case changing the default choice. On paper, hashing beats binary search for lookups. Under adversarial keys and a known hash function, an attacker forces every key into one bucket and turns O(1) into O(n) — a denial of service. Hardened hash functions (keyed seeds, SipHash) restore the guarantee; alternatively, a sorted array plus binary search gives a hard O(log n) that no input can break. The workload detail — are keys attacker-controlled? — flips the decision.
  4. What belongs in a decision framework that does not belong in a trade-off table? An ordering. A table lists dimensions; a framework says which question to ask first (“Do you need ordering? If yes, the hash family is out regardless of everything else”). Ordered checklists mirror how real designs narrow down.
  5. How do the coverage boundaries keep the book maintainable? Same concept, different angles, explicit links. The Why-Not Handbook derives from mechanisms; trade-offs summarizes; Follow-up Tree escalates through interview question chains; Misconceptions attacks false claims. A reader landing on any page gets pointers to the others instead of duplicated prose.

Key Takeaways

  • Every comparison here runs the same six steps: constraints → mechanisms → complexity/guarantees/failure modes → winning conditions → edge cases → decision framework. The order matters; the last step is a consequence of the first five.
  • Mechanisms expose what asymptotic notation hides: cache behavior, constants, amortization spikes, and the exact contract each data structure or system signs.
  • No option is a strawman. Each page states the explicit conditions under which the “losing” option is the correct engineering choice.
  • Edge cases — adversarial inputs, frontier blowups, write skew, stampedes — are where defaults fail and where interview depth is proven.
  • This collection complements, and links to, the quick-reference tables in Trade-offs rather than duplicating them.

Cross-References

  • Engineering Trade-offs — the quick-reference comparison tables this handbook goes deeper than.
  • Database Trade-offs — high-level SQL/NoSQL, consistency, and indexing tables referenced by the database pages here.
  • DSA — the main DSA track whose chapters (sorting, hashing, graphs) provide the tutorial-level background for the mechanism discussions here.
  • Follow-up Tree — question-escalation chains for concepts compared in these pages.
  • Assumptions Hunt — the hidden assumptions behind the complexity claims this handbook quotes.
  • Connection Atlas — dependency-web view of the same database concepts.
  • Misconceptions — the false beliefs this handbook’s edge cases are designed to disarm.