Skip to main content
CleanCodeMastery

Chain of Responsibility Pattern: Pass the Request Until Someone Handles It

Learn the Chain of Responsibility pattern with a simple school leave application story, easy TypeScript and C# code, diagrams, tables, and practice tasks.

25 min read Updated June 11, 2026beginner
design-patternsbehavioral-patternschain-of-responsibilitytypescriptcsharpmiddlewareoop

๐Ÿ“ Priya's leave application

Meet Priya. She studies in Class 7B at Sunrise Public School in Kolkata. Her cousin Anushka is getting married in Jaipur next week. The whole family is going. Priya needs three days of leave from school.

On Monday morning, Priya writes a neat leave application. She folds it carefully and gives it to her class teacher, Mrs. Sen, before the first bell.

Now watch what happens to that little piece of paper.

Mrs. Sen reads it during the lunch break. School rules say a class teacher can approve leave of one day only. Three days is above her limit. Does she reject it? No. Does she call Priya and scold her? No. She simply writes "Forwarded" in the corner and sends it to the vice principal, Mr. Iyer.

Mr. Iyer can approve up to two days. Three days is still too much for him. So he also writes "Forwarded" and sends the letter up to the principal, Mrs. Rao.

Mrs. Rao can approve any reasonable leave. She reads the application, remembers her own cousin's wedding years ago, smiles, and signs it. Approved! The letter travels back down, and on Tuesday Priya finds out she is going to Jaipur.

Notice some important things in this small story:

  • Priya gave the application to only one person โ€” Mrs. Sen.
  • Priya did not know, and did not care, who would finally approve it.
  • Each person in the line had a simple choice: handle it myself, or pass it ahead.
  • The application travelled along a chain: Mrs. Sen โ†’ Mr. Iyer โ†’ Mrs. Rao.
  • Nobody in the chain needed to know the full school rule book. Each one knew only their own limit.

This is exactly the Chain of Responsibility design pattern. You hand a request to the first link of a chain. The request travels link by link until someone takes responsibility for it. Keep Priya's letter in your mind โ€” every section of this lesson follows that same letter.

Here is the letter's path as a flow:

Figure 1: Priya's leave letter climbing the school chain

And here is the same trip told as a journey, with how each person feels along the way:

Figure 2: The leave letter's trip from Priya's desk to the principal's office

๐ŸŽฏ What is the Chain of Responsibility pattern?

Chain of Responsibility (also called CoR or Chain of Command) is a behavioral design pattern. Behavioral patterns are about how objects talk to each other and share work.

Here is a simple definition you can remember for exams:

Chain of Responsibility lets you pass a request along a line of handler objects. Each handler decides either to process the request or to pass it to the next handler in the line. The sender does not know which handler will finally process it.

The pattern was first described in the famous Gang of Four (GoF) book, Design Patterns: Elements of Reusable Object-Oriented Software (1994). It is one of the eleven behavioral patterns in that book.

Three small ideas make this pattern work:

  1. One common interface. Every handler looks the same from outside. It has one main method, usually called handle(request). Mrs. Sen, Mr. Iyer, and Mrs. Rao all "look the same" to the letter โ€” each one reads it and decides.
  2. A pointer to the next handler. Each handler keeps a reference to the next link. Mrs. Sen knows the letter goes to Mr. Iyer next. That pointer is what makes them a chain instead of three separate people.
  3. A choice at every link. Handle and stop, or pass it ahead. Sometimes a handler does a bit of work and still passes it ahead โ€” like Mrs. Sen noting "attendance is good" on the letter before forwarding it.

Match the pattern's roles with the school story:

Pattern roleWhat it doesSchool version
Handler interfaceDeclares setNext() and handle()The idea of "a person who can receive a leave letter"
Base handlerStores the next link, forwards by defaultThe school habit of writing "Forwarded"
Concrete handlersThe real checking logic, one per ruleMrs. Sen, Mr. Iyer, Mrs. Rao
ClientWires the chain and sends the requestPriya (and the school office that decided the order)
RequestThe thing travelling through the chainThe leave application itself
๐Ÿ’ก

Easy memory trick: think of the school line โ€” "Teacher can't? Pass it up!" The sender talks to only the first link. The chain decides the rest. If you can retell Priya's story, you already understand this pattern.

Here is the whole pattern as one mind map you can redraw in your notebook before an exam:

Figure 3: Chain of Responsibility at a glance

๐Ÿ˜– The problem it solves

Why do we even need this pattern? Let us see the pain first.

Suppose the school hires you to write its leave approval software, and you write it the quick way โ€” one big function:

// โŒ The "one giant function" approach โ€” works today, hurts tomorrow
function approveLeave(days: number, studentName: string): string {
  if (days <= 1) {
    return `Class Teacher approved ${days} day leave for ${studentName}`;
  } else if (days <= 2) {
    return `Vice Principal approved ${days} day leave for ${studentName}`;
  } else if (days <= 5) {
    return `Principal approved ${days} day leave for ${studentName}`;
  } else {
    return `Leave rejected. Too many days!`;
  }
}

This looks fine for a small case. But real software grows the way real schools grow. Within one term, the school adds new rules:

  • Sports leave must go to the Sports Teacher first.
  • Medical leave above five days needs the School Board.
  • During exam week, all leave must pass an extra "exam check".
  • Day scholars and hostel students follow different first steps.

Now your one function becomes a jungle of if, else if, and nested conditions. Every new rule means editing this same function. Editing it risks breaking the old rules. Testing one rule forces you to think about every other rule. Worst of all, the order of checks is frozen inside the function. You cannot reorder or reuse a check without surgery.

The deepest problem has a name: tight coupling between the sender and the receivers. The code that sends the request is glued directly to the full list of approvers and their exact order. Priya, in real life, never needed to know that list โ€” she just handed the letter to Mrs. Sen. Your code should enjoy the same comfort.

Chain of Responsibility fixes this by giving each rule its own small handler class. Each handler does one job. You connect them in any order you like, at runtime, like joining train coaches. New rule? New coach. New order? Re-couple the coaches. The engine (the client) never changes.

โš™๏ธ How it works, step by step

Let us build the pattern slowly, piece by piece, following the letter.

Step 1: Make a common Handler interface. It declares setNext(handler) to join the chain and handle(request) to process a request.

Step 2: Make a base handler class (optional but very helpful). It stores the next handler and gives default behavior: "if I have a next, forward to it; otherwise stop." Concrete handlers then only write their own special logic. This base class is the school habit of writing "Forwarded" โ€” nobody has to re-learn it.

Step 3: Write one concrete handler per rule. ClassTeacher, VicePrincipal, Principal. Each handler checks the request. If it can handle it, it does so and the chain stops. If not, it calls the next handler.

Step 4: Wire the chain in the client. classTeacher.setNext(vicePrincipal).setNext(principal). This is just one line. Want a new order? Change the wiring, not the handlers.

Step 5: Fire the request at the head. The client calls classTeacher.handle(request) and waits for the result.

Here is the journey of Priya's three-day application through the chain, message by message:

Figure 4: Priya's application travelling through the chain of handlers

See how Priya only talks to Mrs. Sen. The rest happens inside the chain. If tomorrow the school adds a Head of Department between the vice principal and the principal, Priya's part of the story does not change at all. Only the wiring changes.

It also helps to see the states the application passes through. At any moment, the letter is sitting on exactly one desk:

Figure 5: The life of one leave application as states

One more important point: a request can end in three ways.

  1. Handled mid-chain. Someone approves or rejects it. The chain stops.
  2. Falls off the end. Nobody handled it. The client must plan for this (return null, a default answer, or an error).
  3. Processed by many links. In some versions, every handler does a small piece of work and passes it on anyway. Middleware works like this โ€” more on that in the College corner below.

College corner: this third style is exactly how a middleware pipeline works in web frameworks. In ASP.NET Core or Express.js, an HTTP request enters a chain where every link usually runs: logging middleware notes the request and passes it on, authentication middleware attaches the user and passes it on, and the final handler produces the response. The chain only short-circuits when something is wrong โ€” an auth middleware can return 401 Unauthorized without calling the next link, killing the request right there. So middleware is Chain of Responsibility tuned for "everyone does a bit" with an emergency brake at every link. When you configure middleware order in Program.cs or with app.use(...), you are literally wiring a chain.

๐Ÿ’ป Real-life code example

Now let us write Priya's school in TypeScript. Read the comments โ€” they tell the story.

// The request object โ€” the leave application itself
interface LeaveRequest {
  studentName: string;
  days: number;
  reason: string;
}
 
// Step 1: Common interface for every approver in the chain
interface LeaveHandler {
  setNext(handler: LeaveHandler): LeaveHandler;
  handle(request: LeaveRequest): string | null;
}
 
// Step 2: Base class โ€” stores "next" and forwards by default
abstract class BaseApprover implements LeaveHandler {
  private next: LeaveHandler | null = null;
 
  setNext(handler: LeaveHandler): LeaveHandler {
    this.next = handler;
    return handler; // returning next lets us chain calls fluently
  }
 
  handle(request: LeaveRequest): string | null {
    if (this.next) {
      return this.next.handle(request); // forward to the next link
    }
    return null; // end of chain โ€” nobody handled it
  }
}
 
// Step 3: Concrete handlers โ€” one small class per approver
class ClassTeacher extends BaseApprover {
  handle(request: LeaveRequest): string | null {
    if (request.days <= 1) {
      return `Mrs. Sen approved ${request.days}-day leave for ${request.studentName}.`;
    }
    console.log("Mrs. Sen: above my limit, passing up...");
    return super.handle(request); // pass to the next approver
  }
}
 
class VicePrincipal extends BaseApprover {
  handle(request: LeaveRequest): string | null {
    if (request.days <= 2) {
      return `Mr. Iyer approved ${request.days}-day leave for ${request.studentName}.`;
    }
    console.log("Mr. Iyer: above my limit, passing up...");
    return super.handle(request);
  }
}
 
class Principal extends BaseApprover {
  handle(request: LeaveRequest): string | null {
    if (request.days <= 5) {
      return `Mrs. Rao approved ${request.days}-day leave for ${request.studentName}.`;
    }
    console.log("Mrs. Rao: even I cannot approve this much!");
    return super.handle(request);
  }
}
 
// Step 4: Client wires the chain โ€” order is just the wiring order
const classTeacher = new ClassTeacher();
const vicePrincipal = new VicePrincipal();
const principal = new Principal();
 
classTeacher.setNext(vicePrincipal).setNext(principal);
 
// Step 5: Fire requests at the head of the chain
const requests: LeaveRequest[] = [
  { studentName: "Riya", days: 1, reason: "Doctor visit" },
  { studentName: "Priya", days: 3, reason: "Cousin Anushka's wedding" },
  { studentName: "Meera", days: 10, reason: "Family trip" },
];
 
for (const req of requests) {
  console.log(`\n--- ${req.studentName} asks for ${req.days} day(s) ---`);
  const result = classTeacher.handle(req);
  console.log(result ?? "Nobody could approve. Please meet the school board.");
}

Output:

--- Riya asks for 1 day(s) ---
Mrs. Sen approved 1-day leave for Riya.
 
--- Priya asks for 3 day(s) ---
Mrs. Sen: above my limit, passing up...
Mr. Iyer: above my limit, passing up...
Mrs. Rao approved 3-day leave for Priya.
 
--- Meera asks for 10 day(s) ---
Mrs. Sen: above my limit, passing up...
Mr. Iyer: above my limit, passing up...
Mrs. Rao: even I cannot approve this much!
Nobody could approve. Please meet the school board.

Look at Meera's case. Her request fell off the end of the chain, and our client handled that politely with a default message. Always plan for the "nobody handled it" case.

Also notice how easy change becomes. Want a Sports Teacher to check sports leave first? Write one new class and change one wiring line:

sportsTeacher.setNext(classTeacher).setNext(vicePrincipal).setNext(principal);

No old handler was touched. This is the Open/Closed Principle in action: open for extension, closed for modification.

Here is the class structure you just built:

Figure 6: The class structure of the school leave chain

๐ŸŸฆ The same idea in C#

The pattern looks almost the same in C#. Here is a shorter version with the same school story:

public record LeaveRequest(string StudentName, int Days);
 
public abstract class Approver
{
    private Approver? _next;
 
    public Approver SetNext(Approver next)
    {
        _next = next;
        return next;
    }
 
    public virtual string? Handle(LeaveRequest request)
        => _next?.Handle(request); // default: forward, or null at chain end
}
 
public class ClassTeacher : Approver
{
    public override string? Handle(LeaveRequest request)
        => request.Days <= 1
            ? $"Class Teacher approved {request.Days}-day leave."
            : base.Handle(request);
}
 
public class VicePrincipal : Approver
{
    public override string? Handle(LeaveRequest request)
        => request.Days <= 2
            ? $"Vice Principal approved {request.Days}-day leave."
            : base.Handle(request);
}
 
public class Principal : Approver
{
    public override string? Handle(LeaveRequest request)
        => request.Days <= 5
            ? $"Principal approved {request.Days}-day leave."
            : base.Handle(request);
}
 
// Client
var teacher = new ClassTeacher();
teacher.SetNext(new VicePrincipal()).SetNext(new Principal());
 
Console.WriteLine(teacher.Handle(new LeaveRequest("Priya", 3)));
// Output: Principal approved 3-day leave.

And because Python students should not feel left out, here is the tiniest possible Python chain โ€” same story, duck typing instead of interfaces:

class Approver:
    def __init__(self, limit: int, title: str):
        self.limit = limit
        self.title = title
        self.next: "Approver | None" = None
 
    def set_next(self, handler: "Approver") -> "Approver":
        self.next = handler
        return handler
 
    def handle(self, days: int) -> str:
        if days <= self.limit:
            return f"{self.title} approved {days}-day leave."
        if self.next:
            return self.next.handle(days)
        return "Nobody could approve. Meet the school board."
 
 
teacher = Approver(1, "Mrs. Sen")
teacher.set_next(Approver(2, "Mr. Iyer")).set_next(Approver(5, "Mrs. Rao"))
 
print(teacher.handle(3))   # Mrs. Rao approved 3-day leave.
print(teacher.handle(10))  # Nobody could approve. Meet the school board.

College corner: C# developers meet this pattern every single day without noticing, because the ASP.NET Core middleware pipeline is built on the same idea. Each middleware receives a RequestDelegate named next. Calling await next(context) forwards the request; skipping that call short-circuits the pipeline. The order you register middleware (UseAuthentication before UseAuthorization, exception handling first) is exactly the wiring step from our pattern โ€” and getting that order wrong is one of the most common real-world ASP.NET bugs. Once you see middleware as a chain of handlers, the official docs suddenly read like this lesson.

๐ŸŒ Where you see it in real software

Chain of Responsibility is not a textbook-only pattern. It quietly runs huge parts of the software world.

1. ASP.NET Core middleware. Every HTTP request entering an ASP.NET Core app passes through a pipeline of middleware components. Each middleware can do work before and after calling next, or it can short-circuit the pipeline by not calling next at all โ€” for example, an authentication middleware returning 401 immediately.

2. Express.js middleware. In Node.js, Express passes each request through functions of the shape (req, res, next). Calling next() forwards the request along the chain. Sending a response without calling next() stops the chain. Logging, body parsing, sessions, and routing are all links in this chain.

3. Java servlet filters. The Java Servlet specification defines Filter objects with a doFilter(request, response, chain) method. Each filter decides whether to call chain.doFilter(...) to continue. This design is decades old and still everywhere in Java web apps.

4. DOM event bubbling. When you click a button inside a div inside body, the click event "bubbles" upward: button โ†’ div โ†’ body โ†’ document. Each element along the path gets a chance to handle the event, and any handler can call stopPropagation() to break the chain. The browser itself is running a Chain of Responsibility for you.

5. Logging frameworks. Loggers often pass a log record along handlers (console handler, file handler, email-on-error handler). Each handler checks the log level and decides what to do.

6. Support and escalation systems. Helpdesk software escalates tickets: bot โ†’ level-1 agent โ†’ level-2 engineer โ†’ manager. Sound familiar? It is Priya's letter wearing a headset.

Real softwareWhat is the request?Who are the handlers?Can a link stop the chain?
ASP.NET Core pipelineHTTP requestMiddleware componentsYes โ€” skip calling next
Express.jsHTTP requestMiddleware functionsYes โ€” respond without next()
Java servlet filtersHTTP requestFilter objectsYes โ€” skip chain.doFilter
DOM event bubblingUI event (click, key)Elements on the pathYes โ€” stopPropagation()
Logging frameworksLog recordLog handlers/appendersYes โ€” filter by level
Helpdesk escalationSupport ticketAgents by levelYes โ€” resolve at any level

Interesting side note: in a healthy chain, most requests stop early. In Priya's school, most leave letters are one-day letters that Mrs. Sen approves herself. If the principal's desk received most letters, the limits would be badly designed. The same is true in software โ€” your first handlers should absorb the common cases.

Figure 7: Where leave letters typically stop in the school chain

And here is how far a letter travels depending on how big the ask is โ€” the bigger the request, the deeper it goes into the chain:

Figure 8: Handlers visited before an answer, by leave size

Notice the 10-day bar: it also visits all three handlers, but ends unhandled. Travelling the full chain does not guarantee an answer โ€” that is why the fallback message exists.

๐Ÿค” When to use it and when not to

Like every pattern, CoR is a tool, not a rule. Use this table to decide:

SituationUse it?Why
Many objects may handle a request, and you do not know which one in advanceโœ…The chain finds the right handler at runtime
You want to add, remove, or reorder processing steps without editing old codeโœ…Wiring is separate from handler logic
A long if / else if ladder decides "who processes what"โœ…Each branch becomes one clean handler
Several checks must run one after another (auth, validation, rate limit)โœ…Classic middleware-style chain
Exactly one fixed object always handles the requestโŒA direct method call is simpler and clearer
You only have two or three stable conditions that never changeโŒA small if/else is honest and readable
Every receiver must always get the request, no stopping allowedโŒThat is closer to Observer / events
You need a guaranteed answer for every requestโš ๏ธPossible, but you must add a default end-of-chain handler

If you like pictures more than tables, place your own situation on this map. The further you are towards "many possible handlers" and "flow changes often", the stronger the case for a chain:

Figure 9: A quick map for deciding whether the chain fits your problem

โš ๏ธ Common mistakes students make

โš ๏ธ

Mistake 1: Forgetting the unhandled case. If no handler claims the request, it silently falls off the end. Beginners forget this and their program returns undefined or null with no message. Always decide what "nobody handled it" means: a default handler at the end, a clear error, or a polite fallback like our school-board message for Meera.

โš ๏ธ

Mistake 2: Forgetting to call the next handler. Inside a concrete handler, if you forget return super.handle(request) (or next.handle(request)), the chain quietly stops at that link. Requests vanish and you spend hours wondering why. Make the base class do the forwarding, so concrete classes only override what they need.

A few more traps to watch for:

  • Creating a circular chain. If handler A's next is B, and B's next is A, your request loops forever โ€” like a letter bouncing between two offices until the school year ends. Keep wiring in one place so you can see the whole chain at a glance.
  • Putting too much logic in one handler. If a handler checks five different things, split it into five handlers. One job per link โ€” that is the whole point. Mrs. Sen checks days; she does not also verify medical certificates and exam schedules.
  • Depending on chain order secretly. If ValidationHandler only works when AuthHandler ran before it, write that down, or better, make handlers independent.
  • Building very long chains for tiny problems. Ten classes to replace three if statements is over-engineering. Patterns should reduce pain, not add ceremony.
  • Mutating the request carelessly mid-chain. If a handler edits the request before passing it on, later handlers see changed data. Sometimes that is the design (middleware attaching a user). If it is not, keep requests read-only.

College corner: in production middleware pipelines, the debugging version of these mistakes is famous. A middleware that forgets to call next makes every request hang or return empty responses; a middleware registered after the router never runs at all; an exception-handling middleware registered last (instead of first) cannot catch anything upstream. When your framework "mysteriously ignores" a handler, draw the chain on paper first โ€” the wiring is almost always the bug, not the handler.

๐Ÿ‘ช Compare with cousins

Chain of Responsibility has a few look-alike cousins in the behavioral family. Knowing the differences is a favourite exam and interview question.

PatternHow sender and receiver connectWho receives the request?Can the flow be stopped midway?
Chain of ResponsibilitySender โ†’ first link โ†’ next โ†’ nextThe first handler that claims it (usually one)โœ… Yes, any link can stop it
CommandSender wraps the request in an objectOne known receiver, via the commandโŒ Not about stopping โ€” about storing/queuing/undoing
ObserverPublisher keeps a list of subscribersAll subscribers get every eventโŒ Everyone is notified
MediatorEveryone talks to one central hubThe hub decides who actsThe hub controls routing
DecoratorWrappers around one object, like a chainEvery wrapper always runsโŒ Decorators must pass the call on

The trickiest cousin is Decorator, because its structure (objects linked one after another) looks identical. Remember the difference by intent: a Decorator adds behavior and always forwards the call; a CoR handler decides and may refuse to forward. Decorators cannot break the chain. CoR handlers can. In school terms: a Decorator is like each office stamping the letter and always sending it ahead; CoR is like each office having the power to settle the matter and file the letter away.

Against Observer, think of the morning assembly announcement: everyone hears it (Observer). Priya's letter goes to exactly one final approver (CoR). Against Mediator, imagine if all letters went to the school office first, and the office decided who handles what โ€” that central desk is a Mediator, not a chain.

๐Ÿ“ฆ Quick revision box

+=====================================================================+
|        CHAIN OF RESPONSIBILITY โ€” QUICK REVISION                     |
+=====================================================================+
| WHAT     : Pass a request along a line of handlers; each one        |
|            handles it OR passes it to the next.                     |
| STORY    : Priya's leave letter: Mrs. Sen -> Mr. Iyer -> Mrs. Rao.  |
|            Student talks only to the first link.                    |
| ROLES    : Handler (interface) | BaseHandler (stores next)          |
|            ConcreteHandlers (real logic) | Client (wires chain)     |
| KEY CODE : setNext(h)  +  handle(request)                           |
| ENDINGS  : handled mid-chain | falls off the end | many links work  |
| WINS     : single responsibility, easy reorder, open/closed,        |
|            reusable handlers                                        |
| RISKS    : unhandled requests, harder debugging, long chains slow   |
| SEEN IN  : ASP.NET Core / Express middleware, servlet filters,      |
|            DOM event bubbling, logging frameworks                   |
| COUSIN   : Decorator looks same but ALWAYS forwards; CoR may STOP.  |
+=====================================================================+

๐Ÿ‹๏ธ Practice exercise

Try these three tasks yourself. Type the code โ€” do not just read it.

Task 1: Bank complaint escalation. A customer files a complaint with an amount involved. Build a chain: CustomerCareExecutive (handles complaints up to โ‚น1,000) โ†’ BranchManager (up to โ‚น50,000) โ†’ RegionalOfficer (up to โ‚น10,00,000). If even the regional officer cannot handle it, print "Complaint forwarded to the banking ombudsman." Test with amounts โ‚น500, โ‚น20,000, and โ‚น50,00,000.

Task 2: Exam-week rule. Add an ExamWeekHandler at the front of Priya's school leave chain. During exam week (isExamWeek = true on the request), it rejects every leave request except medical ones, without passing them ahead. Show that you did this by changing only the wiring line and adding one new class โ€” no old class should be edited. This proves you understand the Open/Closed Principle.

Task 3 (challenge): Mini middleware. Build a tiny request pipeline like Express: a LoggerHandler that prints the request and always passes it on, an AuthHandler that stops the chain with "401 Unauthorized" if request.token is missing, and a BusinessHandler that returns "200 OK". This shows the "every link does a bit of work" style of the pattern. Bonus: make LoggerHandler also print "request finished" after the rest of the chain returns โ€” that before/after sandwich is exactly how real middleware measures response time.

If you can finish Task 3, you have basically rebuilt the heart of every web framework's middleware system. Well done โ€” now pass this lesson on to the next student in your chain. If they cannot handle it, they know what to do: pass it up! ๐Ÿ˜„

Frequently asked questions

What is the Chain of Responsibility pattern in simple words?
It is a way to pass a request along a line of handler objects. Each handler checks the request and either handles it or passes it to the next handler. The sender never needs to know who will finally handle it.
What happens if no handler in the chain handles the request?
The request falls off the end of the chain unhandled. Good designs plan for this by returning null, a default result, or throwing a clear error so the client knows nobody took responsibility.
How is Chain of Responsibility different from Decorator?
Both link objects in a line, but a Decorator always passes the call onward and adds something extra, while a Chain of Responsibility handler can stop the request completely and not let it go further.
Where is Chain of Responsibility used in real software?
Web framework middleware is the most famous example. Express.js and ASP.NET Core both pass every HTTP request through a chain of middleware handlers. Java servlet filters and DOM event bubbling also work this way.
Does every handler in the chain have to handle the request?
No. Each handler decides for itself. It can handle the request and stop the chain, do some work and still pass it on, or simply pass it on untouched. That freedom is the heart of the pattern.

Further reading

Related Lessons