Observer Pattern Explained: Reacting to State Changes Without Tight Coupling

How the observer pattern works — subjects, observers, event-driven updates, and why it underpins reactive UIs, event buses, and pub/sub systems.

observer-patterndesign-patternsevent-drivenreactive-programmingsoftware-architecture

Observer Pattern

The observer pattern defines a one-to-many dependency between objects so that when one object (the subject) changes state, all its dependents (observers) are notified and updated automatically.

What It Really Means

Imagine a spreadsheet. Cell A1 contains a value. Cell B1 contains a formula that references A1. Cell C1 contains another formula referencing A1. When you change A1, both B1 and C1 update automatically. You did not explicitly tell B1 and C1 to recalculate — they are observing A1 and react to its changes.

This is the observer pattern. The subject (A1) maintains a list of observers (B1, C1) and notifies them whenever its state changes. The subject does not need to know what the observers do with the notification — it just broadcasts the change. This decouples the object that changes from the objects that react to the change.

The observer pattern is one of the most widely used design patterns in software. React's state management, DOM event listeners, RxJS observables, Node.js EventEmitter, and distributed pub/sub systems are all variations of the same idea: register interest in a subject, get notified when something happens.

How It Works in Practice

Classic Structure

Push vs Pull Model

Push model: The subject sends the changed data to observers.

Pro: Observer has all the data it needs. Con: Subject must know what data observers want.

Pull model: The subject notifies observers, who then query the subject for data they need.

Pro: Subject is simpler. Con: Multiple round-trips if observers need different data.

Real System Examples

DOM Event Listeners (Browser):

javascript

React State Updates:

jsx

Implementation

Classic observer pattern (Python):

python

Event emitter pattern (TypeScript):

typescript

Trade-offs

Benefits:

  • Open/Closed Principle: add new observers without modifying the subject
  • Loose coupling: subject knows observers only through an interface
  • Supports broadcast communication: one change triggers multiple reactions
  • Foundation for reactive and event-driven programming

Costs:

  • Memory leaks: if observers are not detached, they persist in the subject's list (common in UI frameworks)
  • Unexpected cascades: Observer A modifies state that triggers Observer B, which triggers Observer C — hard to debug
  • Ordering dependency: if observers must execute in a specific order, the pattern breaks down
  • Performance: notifying hundreds of observers synchronously blocks the subject

When to use:

  • UI state management (React, Vue, Angular)
  • Event-driven architectures within a single process
  • Decoupling components that react to shared state changes
  • Building custom event systems

When to avoid:

  • When you need guaranteed delivery (use a message queue instead)
  • When observer execution order matters (use a pipeline or chain of responsibility)
  • When the number of observers is very large (use a pub/sub system with proper message brokering)

Common Misconceptions

  • "Observer pattern and pub/sub are the same" — The observer pattern is an in-process design pattern where subjects know their observers directly. Pub/sub is a distributed messaging pattern with a broker in between. Pub/sub is the distributed evolution of the observer pattern.
  • "Observers are always notified synchronously" — The classic pattern is synchronous, but many implementations (RxJS, Node.js EventEmitter with setImmediate) support asynchronous notification.
  • "The subject decides what observers do" — The subject only sends a notification. Each observer independently decides how to react. The subject does not know or control the observer's behavior.
  • "Observer pattern causes memory leaks" — The pattern does not inherently cause leaks. The problem is forgetting to call detach() when an observer is no longer needed. Modern frameworks handle this with cleanup functions (React's useEffect return, Angular's OnDestroy).

How This Appears in Interviews

  1. "How does React re-render components when state changes?" — Observer pattern: useState creates a subject, components are observers, setState triggers notification and re-rendering.
  2. "Design a stock price ticker that updates multiple displays" — Classic observer: price feed is the subject, each display widget is an observer.
  3. "What is the difference between observer and pub/sub?" — Observer is in-process, direct references. Pub/sub is distributed, broker-mediated, supports durability and delivery guarantees.
  4. "How do you avoid memory leaks with event listeners?" — Always unsubscribe: removeEventListener, unsubscribe(), useEffect cleanup. Explain WeakRef-based observer lists as an advanced technique.

Related Concepts

GO DEEPER

Learn from senior engineers in our 12-week cohort

Our Advanced System Design cohort covers this and 11 other deep-dive topics with live sessions, assignments, and expert feedback.