Home

/

The Production-Ready Playbook

/

The Skip List

The Skip List

Appendix B
Appendix
3
min read

A curated set is defined as much by what it excludes as what it keeps. Every pattern below is real, useful, and earns its place in some system somewhere. The question is never whether a pattern is good. It's whether the production-readiness it buys you is worth the capacity it costs a team that has to maintain what it ships. For each one here, the answer is usually no until something specific forces your hand. That something is the skip-if, read in reverse.

A pattern you carry without a reason isn't architecture. It's debt with a fancier name.

Singleton-as-global

The Gang of Four gave us Singleton (GoF, 1994) as a way to guarantee one instance. In practice most teams use it as a respectable-looking global variable, and a global variable is a global variable however you dress it. It hides dependencies and defeats your tests. Worse, it turns object lifetime into something nobody on the team can reason about.

You already have a better tool. A DI container manages a single instance for you when you register it, and does it without the static coupling. This is the Dependency Inversion Principle doing its job (Robert C. Martin, the "D" in SOLID, Agile Software Development: Principles, Patterns, and Practices): depend on an abstraction the container supplies, not on a static you reach for by name. Register the thing as a singleton lifetime and inject it.

// Not this: a static no test can replace.
public static class Clock { public static DateTime Now => DateTime.UtcNow; }

// This: one instance, injected, swappable in a test.
services.AddSingleton<IClock, SystemClock>();

The skip-if reads in reverse: if you find yourself writing Whatever.Instance, you wanted a DI lifetime.

Observer, Iterator, Builder

These three won so completely that C# absorbed them into the language. Observer is what event and IObservable<T> already do. Iterator is IEnumerable<T> and yield return. Builder is the object initialiser and, increasingly, the with expression on a record. The concepts are still correct, all three of them GoF. As hand-rolled patterns they're now redundant.

Writing them out by hand in 2026 reinvents a compiler feature you already paid for, and there's nothing disciplined about that. We teach what the language doesn't give you for free and skip what it does.

Don't hand-roll a pattern your compiler already ships.

Heavy ORM / Entity Framework

This is the worked over-engineering example in the component chapter, so the verdict is short here. An ORM trades hand-written SQL for change-tracking magic, LINQ that compiles to queries you didn't write, and a migration history a junior can't read. The book's stance is SQL-first data access: a thin Data Gateway over stored procedures (Fowler, PoEAA), where the query you ship is the query you wrote.

Skip the ORM until you genuinely have a domain so large that mapping by hand is the bottleneck, and a team big enough to own the framework's behaviour when it surprises you. Most small teams never reach either.

Saga, before you have a distributed transaction

A Saga (Garcia-Molina & Salem, 1987; Richardson) coordinates a multi-step transaction across services using compensating actions, because you can't hold a single database lock across a network. It is the right answer to a real and painful problem. The trouble is that most systems billed as needing one have a transaction that still fits inside a single database.

If your work commits in one transaction against one store, you don't have a distributed transaction, and a Saga is pure overhead: orchestration code, compensation logic, and a state machine to debug at 3am. Reach for it the day a business operation truly spans two systems that can each fail on their own. Not before.

the-pareto-stack-cloud-design-patterns-for-small-teams
the-ladder-of-altitudes
how-to-read-this
object-level-the-patterns-that-earn-their-keep
decorator
state
component-level-structuring-one-service
ports-and-adapters-hexagonal
mediator-the-commandquery-split
data-persistence
optimistic-concurrency
messaging-scale
outbox
resilience-staying-up-when-dependencies-dont
rate-limiting-throttling
timeout-fallback
the-composed-pipeline
observability-diagnostics-seeing-inside-production
metrics-the-four-golden-signals
externalised-configuration
hosting-cloud-agnostic-by-default
sidecar-ambassador
orchestrator-agnostic-deploy
a-reference-service
the-relay-outbox-to-queue
the-payment-saga-charge-pay-out-compensate
the-over-engineering-tax
conclusion-production-ready-deliberately
the-pattern-quick-reference-card
altitude-3-data-persistence
altitude-5-resilience
the-skip-list
full-event-sourcing-for-crud
robert-c-martin-uncle-bob-the-house-authority-for-structure
altitude-2-component
altitude-4-messaging-scale
altitude-6-observability-diagnostics

Download the full PDF for free?

Free download — no account required

Get the PDF
Get the PDF
Related Chapters
Free Download
Get the full PDF
All pages, including all code examples, diagrams, and the appendix reference card.
No spam. Unsubscribe at any time.
Your email won't be shared.
Oops! There's a problem with your request. We're working on fixing it. Please try again later.