Skip to content

Pattern Comparison Matrix

Some patterns look alike but serve different purposes. This guide compares commonly confused pairs to help you choose the right tool.

Cache vs Pool

DimensionLRU CacheObject Pool
PurposeSpeed up repeated lookupsAvoid allocation/GC cost
EvictionLeast-recently-used outReturned by user, reused
ContentRead-only cached valuesMutable, reusable objects
Hit rateDepends on access pattern100% (pre-allocated)
ExampleDNS cache, HTTP cacheDB connection pool, thread pool
When confusedIf you're "caching" objects to reuse→ that's a pool

Arena Allocator vs Free List

DimensionArena AllocatorFree List
AllocationO(1) bump pointerO(1) pop from free chain
DeallocationAll-at-once onlyIndividual O(1) return
FragmentationNone (contiguous)Possible (non-contiguous)
LifetimePhase-based (parse, render)Object-by-object
ExampleCompiler per-query arenaGame entity recycling
When confusedIf you need individual free→ use free list

Observer vs Actor Model

DimensionObserverActor Model
CommunicationSynchronous callbacksAsynchronous messages
CouplingShared memory, same threadIsolated state, any location
ScalingSingle processDistributed systems
Failure isolationOne bad observer blocks allActor crashes independently
ExampleDOM events, React stateErlang/OTP, Akka
When confusedIf observers need isolation→ use actors

Skip List vs B+ Tree

DimensionSkip ListB+ Tree
BalancingProbabilistic (random levels)Deterministic (splits/merges)
ConcurrencyLock-free possibleComplex locking
Disk I/OPoor (pointer chasing)Excellent (high fan-out)
ImplementationSimple (~100 lines)Complex (~500+ lines)
ExampleRedis sorted sets, LevelDB memtableMySQL InnoDB, filesystem indexes
When confusedIn-memory sorted data → skip listOn-disk → B+ tree

Ring Buffer vs Queue (FIFO)

DimensionRing BufferStandard Queue
CapacityFixed, pre-allocatedDynamic, grows on demand
OverflowOverwrites oldest (or blocks)Allocates more memory
MemoryO(capacity), no allocO(n), may cause GC
Use caseBounded buffers, audio streamsUnbounded task queues
ExampleLinux io_uring, loggingMessage queues, BFS
When confusedIf capacity is known → ring bufferIf unbounded → queue

Copy-on-Write vs Double Buffering

DimensionCopy-on-WriteDouble Buffering
Buffer count1 (cloned on write)2 (always allocated)
Copy triggerOn mutationNever (swap pointers)
Read costO(1) alwaysO(1) always
Write costO(n) clone on first writeO(write) to back buffer
MemoryO(n) only when writtenO(2n) always
ExampleLinux fork(), Rc<T>GPU rendering, game loops
When confusedIf writes are rare → CoWIf writes are continuous → double buffer

Circuit Breaker vs Rate Limiter

DimensionCircuit BreakerRate Limiter
ProtectsCaller from broken downstreamServer from excess traffic
TriggerFailure count thresholdRequest count threshold
DirectionOutbound (your calls to others)Inbound (others' calls to you)
RecoveryAuto-recover after timeoutRefill tokens over time
ExampleMicroservice → databaseAPI gateway, login attempts
When confusedProtecting yourself → circuit breakerProtecting your server → rate limiter

State Machine vs Strategy/Vtable

DimensionState MachineVtable
Dispatch byCurrent stateObject type
TransitionsExplicit, guardedNot applicable
State countChanges at runtimeFixed at compile time
Validation"Can this transition happen?""Which implementation to call?"
ExampleTCP connection statesTrait objects, interfaces
When confusedIf behavior changes over time → state machineIf it varies by type → vtable

Bloom Filter vs Hash Set

DimensionBloom FilterHash Set
False positivesYes (tunable)No
False negativesNoNo
Memory~10 bits/element~50+ bytes/element
DeleteNot supportedO(1)
Use case"Definitely not in set" fast checkExact membership
ExampleChrome Safe Browsing, LSM read filterDeduplication, visited URLs
When confusedIf 1% false positive is OK + memory matters→ Bloom filter

Write-Ahead Log vs Checkpointing

DimensionWrite-Ahead LogCheckpointing
GranularityEvery operationPeriodic snapshot
RecoveryReplay from last checkpointLoad snapshot + replay remaining WAL
Disk usageGrows unbounded (without truncation)Fixed per snapshot
Write costO(1) append per opO(state size) per snapshot
Together?Yes — WAL provides durabilityCheckpoint limits replay length
ExamplePostgreSQL WAL, Redis AOFPostgreSQL base backup, Redis RDB

Backpressure vs Rate Limiter

DimensionBackpressureRate Limiter
DirectionProducer → Consumer (upstream signal)External → Server (gateway enforcement)
MechanismSlow/pause the producerDrop or queue excess requests
ScopeInternal pipeline flow controlExternal API boundary
AdaptationDynamic (adjusts to consumer speed)Static threshold (tokens/sec)
ExampleNode.js stream .pipe(), Reactive StreamsStripe API 25 req/sec, Nginx limit_req
When confusedIf you control the producer → backpressureIf you can't control the sender → rate limiter

Tombstone vs Dirty Flag

DimensionTombstoneDirty Flag
Marks"This item is deleted""This item needs recomputation"
LifecyclePermanent until compactionCleared after recomputation
PurposeDefer physical deletionDefer expensive recalculation
VisibilityReaders must skip tombstoned itemsReaders see stale-until-recalculated value
ExampleCassandra tombstones, LevelDB delete markersChromium layout invalidation, game transform
When confusedIf marking something as "gone" → tombstoneIf marking "needs update" → dirty flag

Interning vs Flyweight

DimensionInterningFlyweight
SharingIdentical values share one instanceIntrinsic state shared, extrinsic differs
LookupGlobal table (hash map)Factory with cache
IdentityPointer equality replaces value equalityObjects still compared by value
MutabilityImmutable (must be)Intrinsic immutable, extrinsic mutable
ExamplePython sys.intern(), Rust Symbol, Java string poolFont glyphs, game tile sprites, CSS rule objects
When confusedIf all instances are identical → interningIf instances share partial state → flyweight

Event Loop vs Work Stealing

DimensionEvent LoopWork Stealing
ThreadsSingle thread + I/O multiplexingMultiple threads, one deque each
Best forI/O-bound, high-connection workloadsCPU-bound, recursive/parallel workloads
SchedulingCooperative (callbacks/promises)Preemptive steal from other deques
LatencyOne slow callback blocks allIdle threads steal work, stays balanced
ExampleNode.js, Nginx, RedisGo scheduler, Java ForkJoinPool, Tokio
When confusedIf mostly I/O + few CPUs → event loopIf CPU-heavy + many cores → work stealing

Visitor vs Middleware Chain

DimensionVisitorMiddleware Chain
TraversalWalk a tree/graph of typed nodesWalk a linear pipeline
DispatchBy node type (double dispatch)By registration order
Adding opsNew visitor = new operationNew middleware = new layer
Data flowVisitor accumulates resultRequest/response flows through
ExampleLLVM IR passes, AST transformsExpress.js, Django, gRPC interceptors
When confusedIf processing a heterogeneous tree → visitorIf processing a request/response pipeline → middleware

Choosing the Right Pattern: Decision Flowchart

?Need to store/retrieve data?
?Need to manage concurrency?
?Need resilience?
?Need memory efficiency?

Released under the MIT License.