10 Microservices Patterns in Chronological Order
The payment went through. Inventory failed. The customer presses the button again because the page is still waiting. What does this system need: a saga, a circuit breaker, a queue, or simply a sensible timeout?
A list of names cannot answer that. I prefer to start with what broke, follow the decision that addresses it, and finish with the cost of keeping it running. Seen that way, patterns stop being answers to memorize and become tools for making decisions.
I will use one example throughout: an online order that needs payment, inventory reservation, and shipping. This is an illustrative example, not an account of an actual incident. It lets us change a decision and follow its consequences through the rest of the system.
Before We Start: What These Dates Mean
Many patterns used in microservices came from somewhere else. Long transactions, resource isolation, and reliable messaging were problems long before containers.
I am therefore ordering documented milestones, not issuing birth certificates. A paper, a book, or a release gives us a reference point; it does not prove nobody had done it before. Nor is this a compulsory progression in which each pattern must replace the previous one.
| Milestone | Pattern | The problem we will follow |
|---|---|---|
| 1987, the Sagas paper | Saga | Coordinate work that should not remain one long transaction. |
| 2004, Fowler's article | Strangler Fig | Replace a system without a single big cutover. |
| 2005, Fowler's description | Event Sourcing | Preserve the changes that explain the current state. |
| 2007, Release It! | Circuit Breaker | Temporarily stop calling a failing dependency. |
| 2007, the same book | Bulkhead | Limit the damage one dependency can cause. |
| Around 2009–2010, dissemination of CQRS | CQRS | Design different models for updates and queries. |
| 2013, Zuul's public announcement | API Gateway | Give clients a controlled entry point. |
| 2016, Burns and Oppenheimer's paper | Sidecar | Separate a supporting capability from the main process. |
| 2016–2017, Linkerd and Istio | Service Mesh | Govern communication between services. |
| 2018, Microservices Patterns | Transactional Outbox | Atomically persist a change and its intent to publish. |
The original sources are linked throughout and collected at the end. Where a date marks dissemination or cataloguing, I say so explicitly.
1987 · Saga: Finish a Process Without One Giant Transaction
The background. Héctor García-Molina and Kenneth Salem published Sagas in 1987, addressing long database transactions. Microservices later adopted the idea to coordinate work across separate participants.
The problem. Orders records the purchase, Payments charges the customer, and Inventory tries to reserve stock. If that last step fails, a local transaction in Orders cannot undo changes already committed elsewhere. Holding resources while every participant responds is not a free solution either.
The idea. Break the process into local transactions and define what happens after failure. A compensation reverses a business effect: refunding a payment, for example. It does not automatically restore the world to its previous instant. An email may already have been read, and a refund may take time.
With orchestration, a coordinator tracks the process and directs the steps. With choreography, participants react to events. One makes the workflow explicit; the other distributes coordination. Either way, you need to know the order's state and who owns the next attempt.
The cost, and when to skip it. A saga does not provide global isolation by itself. Other processes can see intermediate states. I would start with a local transaction when the entire change belongs inside one boundary. If you need an immediate invariant across participants, reconsider that boundary first. Adding compensations cannot make the invariant hold.
The interview question. What if the refund fails? Compensation needs idempotency, controlled retries, and durable tracking. If recovery still fails, someone needs an operational resolution path. A lost error message is not a strategy.
2004 · Strangler Fig: Replace a System in Pieces
The background. Martin Fowler introduced the metaphor in his post dated 29 June 2004. The useful idea remains gradual displacement: deliver value before the replacement is complete.
The problem. You want to replace the old checkout. While you build its successor, the business keeps changing discounts, tax rules, and delivery options. If you wait until the new system reproduces everything, the finish line keeps moving.
The idea. Introduce an interception point and move capabilities incrementally. You might start with order tracking and leave payment where it is. The new system gains responsibility as you verify results and retire the old implementation.
The cost, and when to skip it. For a while, you maintain two implementations, translations between models, and a coexistence procedure. Data is often the hard part: who writes each fact, and how can it be queried during the transition? Improvised dual writes create two competing truths that someone must reconcile.
I would not conclude that tight coupling makes the pattern impossible. You may need to establish a boundary before migrating. But if a component is small, well tested, and replaceable through one bounded change, prolonged coexistence may cost more than the replacement itself.
The interview question. What would you migrate first? A capability with clear data ownership, manageable risk, and a way to verify equivalence. Before switching traffic, I would define error signals, success criteria, and a route back. Migration percentage means little when incorrect purchases look successful.
2005 · Event Sourcing: Keep the Path to the Present
The background. Fowler published his Event Sourcing description in December 2005. Historical records are much older; this source documents a formulation of the application pattern.
The problem. The order row says CANCELLED, but no longer explains what was confirmed, what was charged, or why it was cancelled. If those transitions matter to the business, keeping only the final result may be insufficient.
The idea. Persist domain events as the source of truth, then reconstruct state by applying them in order. OrderPlaced, ItemAdded, and OrderCancelled describe meaningful facts rather than bare column differences.
The cost, and when to skip it. You need to manage concurrency, schema evolution, reconstruction time, and external effects. Replaying history to rebuild state must not charge the customer again. You also need a plan for the lifecycle of personal data before putting it into durable events.
I would not choose Event Sourcing solely to obtain an audit trail. An audit record can accompany a current-state model without making event replay central to the system. Snapshots have a cost too: you must be able to invalidate or rebuild them when the interpretation of history changes.
The interview question. What happens when an event's shape changes? You can maintain compatible readers, version schemas, and upcast older representations when reading. That preserves the interpretation of historical facts. It does not mean pretending a past fact happened differently.
2007 · Circuit Breaker: Stop Insisting for a While
The background. Michael Nygard's Release It! is a stability reference published in 2007; the author announced shipping that April. I use the book as a dissemination milestone here.
The problem. The payment provider takes too long, and each call holds a connection or a thread. New orders keep arriving. More retries can add pressure to a dependency already unable to serve its callers.
The idea. A closed circuit permits calls and observes outcomes. Once the failure criterion is met, it becomes open, rejecting calls without contacting the provider. After a delay, it becomes half-open, admits a limited number of probes, and decides whether to close or reopen.
The cost, and when to skip it. You must decide which outcomes count as failures and what observation window to use. A card rejected under a business rule does not show that the service is broken. Poor thresholds can block a healthy provider or react too late.
The pattern complements timeouts and retry budgets. It can fail fast without a fallback: an acceptable response may simply explain that the operation could not be completed. For payments, manufacturing a successful response would be worse than surfacing failure.
The interview question. How many probes would you allow? Enough to make a decision under controlled load, according to the provider's behavior. I would also coordinate retries and concurrency limits. Recovery should not trigger another avalanche.
2007 · Bulkhead: Keep One Leak From Sinking Everything
The background. The same Release It! describes resource isolation through a ship's watertight compartments. The point is to contain damage rather than bet that every dependency will stay healthy.
The problem. Payments and recommendations share a pool. Recommendations becomes slow and occupies all available capacity. Now you cannot charge customers either, even though the payment provider is working.
The idea. Allocate limits or compartments to different workloads. These may be concurrency semaphores, connection pools, bounded queues, or separate instances. The implementation should match the resource that is actually running out.
The cost, and when to skip it. A rigid partition may waste available capacity and create new queues. If you isolate by customer, limits also need to account for shared infrastructure underneath the compartments. Otherwise a noisy customer can still consume it all.
I would not give every remote call its own thread pool by habit. With asynchronous execution or virtual threads, connections, memory, and provider capacity are still finite. The mechanism should protect that resource rather than copy a recipe designed for another execution model.
The interview question. How is this different from a circuit breaker? The bulkhead bounds resource consumption. The breaker decides when to stop invoking a dependency based on its outcomes. They can work together: one contains damage while the other detects and interrupts failure. Neither makes the remaining capacity unlimited.
Around 2009–2010 · CQRS: Two Models for Two Jobs
The background. CQRS is associated with Greg Young and Bertrand Meyer's CQS principle. I avoid assigning it a precise 2010 birthday: Udi Dahan published Clarified CQRS on 9 December 2009. Fowler later describes model separation and its variations.
The problem. The model that validates an order has rules and relationships the tracking page does not need. That page, meanwhile, wants a view combining order, payment, and shipment status. Forcing both jobs into one model can make each harder.
The idea. Design commands to express changes and a query model to answer questions. ConfirmOrder protects the rules; GetTracking returns what the page needs. This separation can live inside one application and use one database.
The cost, and when to skip it. You maintain two representations and must decide how the query side stays current. If updates are asynchronous, the page may lag behind a confirmed change. That delay requires a deliberate user experience: pending state, confirmation, or an appropriate read for the operation.
For straightforward CRUD, I would keep the solution straightforward. A slow query may need an index or a specialized query. Not every difference between reading and writing needs a new architecture.
The interview question. Does CQRS imply Event Sourcing? No. You can separate models while storing current state and publishing no events. You can also use events to update a projection without making event history the source of truth.
2013 · API Gateway: A Controlled Front Door
The background. Netflix's public Zuul announcement in 2013 is a recognizable milestone for this tour. It does not mean Netflix invented gateways or that every API needs one.
The problem. The mobile application knows the addresses of Orders, Payments, and Shipping. Each service exposes its own authentication and versioning decisions. Changing internal topology starts requiring client changes.
The idea. Provide an entry point that routes requests and applies shared policies: authentication, request limits, and TLS termination, for example. A BFF tailors an interface to a particular client type. It can be combined with a gateway, but has a different purpose.
The cost, and when to skip it. This is another network hop and an operationally important component. It needs sizing, redundancy, and measurement. One logical entry point does not require one physical instance, but concentrating everything in one instance does create a failure point.
If you have a small API behind an existing proxy, another gateway may add little. I would carefully separate shared policies from business decisions. Whether an order can be cancelled should not become a rule scattered across gateway routes.
The interview question. Gateway or mesh? Their responsibilities complement each other. The gateway handles client access to the system; the mesh governs communication among workloads. Implementations may share proxy technology without becoming the same architectural decision.
2016 · Sidecar: A Companion for the Application
The background. Brendan Burns and David Oppenheimer describe the pattern in Design Patterns for Container-based Distributed Systems, HotCloud 2016. That documents the pattern; it does not mark the beginning of every supporting process.
The problem. Several services need a similar capability, such as log collection or a local proxy. Building it inside each application can duplicate integration and tie it to the service's language or dependencies.
The idea. Run a supporting component alongside the application. In Kubernetes, a common example is another container in the same pod: it shares the pod's network and is managed within that deployment unit. Application and helper remain separate processes.
The cost, and when to skip it. The helper consumes resources and can introduce latency if every request passes through it. Startup, shutdown, and health need coordination too. A separate image does not mean upgrades are independent of the deployment unit: replacing a pod can affect both containers.
I would compare that operational burden with a small library or a shared agent when per-instance cost exceeds the benefit. On managed platforms, you first need to establish which execution model the platform actually supports.
The interview question. Why not a library? A sidecar can serve applications written in different languages and have its own dependencies. In return, it introduces inter-process communication and operations. Implementation independence has a price; it is not always worth paying.
2016–2017 · Service Mesh: Govern Communication
The background. Linkerd launched in 2016; Istio started with sidecars in 2017. I use that period to place the spread of a dedicated infrastructure layer for service communication.
The problem. Teams configure identity, encryption, observability, and failure handling differently. When a policy changes, their various implementations need to behave consistently enough to work together.
The idea. Separate a data plane, through which traffic flows, from a control plane, which configures policies and identities. A sidecar-based mesh places proxies alongside applications. That is one implementation, not the whole definition.
Istio's documentation distinguishes sidecar and ambient modes. Ambient enables a data plane without a sidecar in every pod. That is why I keep Sidecar and Service Mesh as related patterns with different scopes.
The cost, and when to skip it. You add network infrastructure, certificates, configuration, and new failure modes. With a few services and simple policies, existing capabilities may be easier to operate. The observability you gain also needs to help you understand the mesh itself.
The interview question. What moves out of application code? Some transport behavior can be configured in infrastructure. Payment idempotency, response meaning, and business permissions still require application decisions. Mesh retries and client retries also need a shared budget so they do not multiply attempts.
2018 · Transactional Outbox: Close the Dual-Write Gap
The background. Chris Richardson covers the pattern in Microservices Patterns, published in 2018, and in his pattern catalogue. This is a cataloguing milestone; reliable messaging predates the book.
The problem. You commit an order to the database, then publish an event. The process crashes between those operations. The order exists, but Inventory never learns about it. Reversing the sequence does not make the two writes atomic.
The idea. Save the business change and a pending publication record inside the same local transaction. A relay subsequently publishes the record through polling or transaction-log capture. After a restart, the intent to publish is still durable.
The cost, and when to skip it. You operate the relay, monitor lag, clean up records, and preserve the ordering required per aggregate. If the relay publishes and crashes before recording the outcome, it may publish again. Consumers must tolerate this without repeating effects. Recording that an event was seen and applying its effect also need consistency.
If nothing needs publishing outside the transaction, you do not need an outbox. Event Sourcing can persist the fact and provide a publication source, but an event store does not automatically guarantee delivery to another broker. You still need subscriptions, checkpoints, or an equivalent reliable mechanism.
The interview question. Is delivery exactly once? This pattern alone does not promise it. I would design for repetition: a stable identifier, idempotent effects, tracking, and recovery. Retrying must not charge or reserve twice.
How They Connect Without Becoming a Shopping List
A saga coordinates business steps; an outbox helps publish the messages those steps require. CQRS separates models; Event Sourcing changes the source of truth. These can be combined, but a useful relationship is not a mandatory dependency.
For a walkthrough of how some of these decisions arise while building a system, I explored that angle in From DDD to Event Sourcing: The Chain of Problems. Here the question is narrower: which concrete problem justifies each piece?
The Decision I Would Make Before Adding a Piece
| If the problem is… | I would evaluate… | First I would check… |
|---|---|---|
| A process spans several local transactions | Saga | Whether boundaries can be simpler; who resolves pending states. |
| Replacing a system with controlled risk | Strangler Fig | Data ownership, equivalence, and a route back. |
| Reconstructing past domain decisions | Event Sourcing | Whether a simpler audit history would suffice. |
| Remote failures causing cascades | Circuit Breaker | Timeouts, failure classification, and retry budgets. |
| One workload consumes all capacity | Bulkhead | Which resource runs out and what isolation already exists. |
| One model makes updates and queries difficult | CQRS | Whether specialized queries or indexes address the pain. |
| Clients coupled to multiple services | API Gateway | Whether the existing proxy suffices and who operates the entry point. |
| A supporting capability must accompany each instance | Sidecar | Whether a library or shared agent is a better fit. |
| Communication policies are difficult to govern | Service Mesh | Operational capacity and measurable security and traffic needs. |
| A committed change must produce a reliable message | Transactional Outbox | Duplicates, ordering, lag, and relay recovery. |
Takeaway: before asking how to implement a pattern, write down the failure you want to prevent, the guarantee you need, and the cost you can operate.
That is what interests me about their history. It brings you back to the problem before choosing a tool. And it leaves room for another answer that shows judgment: given today's requirements, we do not need it yet.
Which of these problems do you have right now, and what evidence would convince you that you need the pattern?
References for the Journey
- García-Molina and Salem: Sagas, SIGMOD 1987.
- Fowler: Strangler Application, 2004, Event Sourcing, 2005, and CQRS, 2011.
- Nygard: Release It! shipping announcement, 2007.
- Dahan: Clarified CQRS, 2009.
- Netflix: Announcing Zuul, 2013.
- Burns and Oppenheimer: Design Patterns for Container-based Distributed Systems, 2016.
- CNCF: Linkerd 2.0 and the original 2016 launch.
- Istio: data-plane modes.
- Richardson: Microservices Patterns, 2018, Saga, and Transactional Outbox.