The Town Crier Pattern is an architectural anti-pattern in which a server broadcasts complete, fully-rendered state updates to all connected clients on every internal state change, regardless of whether any client is watching, whether any client has requested the information, or whether it is 3 AM and the only connected “client” is a browser tab that was left open by someone who went to bed six hours ago.
The pattern takes its name from the medieval practice of hiring a person to stand in the town square and shout the news, except in this case the town crier delivers the full municipal gazette — weather, livestock prices, shipping manifests, and judicial proceedings — every time a single sheep enters the market.
“The server broadcast 114 HTML fragments to a browser tab that was either asleep or didn’t exist. It’s screaming into the void.”
— riclib, 1:10 AM, reading server logs in pajamas, The Hundred-Event Buffer, or The Night the Server Screamed into the Void
The Architecture
A classic Town Crier implementation exhibits the following characteristics:
-
Eagerness. Every internal event triggers a full broadcast. A file is downloaded. The server renders three HTML fragments — a running panel, a notification badge, a tab badge — serializes them, and pushes them via SSE to every connected session.
-
Completeness. The broadcast contains the full state, not a delta. The running panel includes all active jobs, not just the one that changed. The badge includes the current count, recomputed from scratch. The tab badge includes the same count, formatted differently.
-
Universality. The broadcast goes to every session, not just sessions that are displaying the relevant UI. A user viewing a configuration page receives running-panel HTML they will never render. A browser tab in the background receives events it cannot process.
-
Frequency. The crier does not batch. A pipeline with twenty-five file downloads produces twenty-five announcements. Each announcement triggers three broadcasts. Seventy-five HTML renders for twenty-five files for an audience that may not exist.
The mathematics of the April 2026 incident:
Progress callbacks per job run: 38
SSE events per callback: 3
Total events per run: 114
Channel buffer capacity: 100
Surplus: 14 events
Buffer overflow: guaranteed
Audience at 1 AM: 0
“That’s the server doing pushups in an empty gym.”
— riclib, summarizing the architecture in one sentence
The Void
The defining characteristic of the Town Crier Pattern is the void — the empty town square, the sleeping browser, the SSE channel pushing events into a buffered channel that nobody is draining.
The void is patient. The void does not complain. The void simply lets the buffer fill, and when the buffer is full, the events are dropped, and the only evidence is a log line:
WRN SSE event dropped (buffer full) tab=1fb2c4ba824f8bee event=notification-badge
The developer sees this log. The developer does not think “we are broadcasting too much.” The developer thinks “the buffer is too small” or “the consumer is too slow” or “there’s a stale session holding a reference.” The developer theorizes about TCP timeouts and EventSource reconnection races.
The developer does not count the events.
The developer should count the events.
The Doorbell
The cure for the Town Crier Pattern is the doorbell: a notification system that announces that something happened, not everything about what happened.
The April 2026 redesign replaced the town crier with two events per job run. One when it starts. One when it finishes. Both update a badge count. Everything else — the running panel, the job details, the progress percentages — is fetched by the client when the client wants it, via polling, at a frequency determined by whether the user is actually looking.
Events per job run (before): 114
Events per job run (after): 2
Reduction factor: 57×
Architecture name (before): push-everything
Architecture name (after): tell me when, I'll ask what
The doorbell rings when someone arrives. The town crier broadcasts the municipal gazette at 3 AM to an empty town square. Both convey the same information. One of them overflows a buffer.
“THE SERVER THAT SPEAKS / WHEN NOBODY LISTENS / IS NOT GENEROUS / IT IS LOUD”
— The Lizard, The Hundred-Event Buffer, or The Night the Server Screamed into the Void
Diagnostic Criteria
A system suffers from the Town Crier Pattern if:
- The number of SSE/WebSocket events per operation exceeds the number of user-visible state changes by a factor of 10 or more
- The server renders HTML that no connected client will display
- A scheduled job at 1 AM produces the same event volume as a user-initiated job at 2 PM
- The first question the developer asks about a buffer overflow is “why is the consumer slow?” instead of “how many events are we sending?”
- Increasing the buffer size is proposed as a fix
See Also
- Self-Destructing Polling — The replacement architecture: pull-based, lifecycle-aware, zero events when nobody is watching
- YAGNI — The Town Crier often starts as “real-time updates” that nobody asked to be real-time
- The Second System Effect — Push architectures are a common second-system indulgence
- The Wrong Question — The diagnostic failure that keeps the Town Crier employed
