A PriceTracker subject publishes price ticks for stock tickers. Observers (RetailInvestor, StopLossAlert, AuditLogger) each register with a custom ObservationHook that declares their interest condition — e.g. "only notify me when INFY drops below ₹1300". The subject evaluates each hook before dispatching, so observers only fire when their predicate is satisfied. This avoids polling and keeps each observer fully decoupled from the others.
Every frontend framework (React, Vue, Flutter) is built on Observer. A component subscribes to a state store; when state changes, the framework calls each subscriber to re-render:
type StateStore struct {
observers []func(newState State)
state State
}
func (s *StateStore) Subscribe(fn func(State)) {
s.observers = append(s.observers, fn)
}
func (s *StateStore) Dispatch(newState State) {
s.state = newState
for _, fn := range s.observers {
fn(newState)
}
}Components never poll — they react. Adding a new component that depends on shared state is one Subscribe call.
Kafka's consumer groups are Observer at scale. A topic (subject) retains messages; consumer groups (observers) each read the stream independently. Adding a new consumer — analytics pipeline, fraud detector, audit log — requires zero changes to the producer. This is Observer with a broker decoupling the two sides and enabling async, persistent fan-out.
Databases like PostgreSQL emit row-level change events (insert, update, delete) via logical replication slots. Downstream services (search indexer, cache invalidator, audit trail) subscribe to the stream. Each reacts to changes without coupling to the service that wrote the row — a classic Observer topology with the WAL as the subject.
A server maintains a registry of active WebSocket connections. When a backend event fires (new chat message, order status update, live score), the event handler iterates the registry and pushes to each connection:
type Hub struct {
clients map[*Client]bool
}
func (h *Hub) Broadcast(msg []byte) {
for client := range h.clients {
client.send <- msg
}
}Clients connect and disconnect dynamically — the hub never knows what they do with the message, only that they implement the subscriber contract.
A build system (GitHub Actions, Jenkins) notifies registered webhooks when a pipeline completes. Slack bots, deployment triggers, JIRA ticket updaters, and test reporters all subscribe to the same build event. Each webhook is an observer; the build system is the subject. Adding Datadog monitoring is one more webhook registration — no pipeline code changes.
Prometheus's scrape model inverts Observer (pull instead of push), but application-side metric collection is Observer: a metric registry (subject) emits snapshots; exporters (observers) format and forward them to different backends (Prometheus, Datadog, CloudWatch). Each exporter reacts to the same snapshot independently.
A game engine publishes collision, damage, and input events. Entities subscribe to the events they care about — the health system listens for damage events, the sound system listens for collision events, the UI listens for score events. No entity knows about the others; the event bus is the subject:
type EventBus struct {
handlers map[EventType][]Handler
}
func (eb *EventBus) Publish(e Event) {
for _, h := range eb.handlers[e.Type] {
h.Handle(e)
}
}Decoupling entity systems this way makes it trivial to add or remove features (e.g. achievements) without touching existing systems.
Tools like fsnotify implement Observer over the OS inode event stream. A watcher (subject) monitors a directory; subscribers (observers) react to create, modify, and delete events:
watcher.OnChange(func(path string, op Op) {
if op == Write {
reloadConfig(path)
}
})Hot-reloading config, triggering test runs on save, and invalidating build caches are all observers on the same file-change subject.
A ride-sharing platform streams GPS location updates from a driver. Multiple observers react to each update: the passenger app displays the moving pin, the ETA service recalculates arrival time, the operations dashboard logs the trace, and the geofence service checks for zone entry/exit. Each observer is independent; the location stream is the shared subject. Adding surge-pricing zone detection is one new observer — nothing else changes.