This page is part of the ForgeSDLC knowledge base — an AI-assisted, human-directed methodology for taking product work from concept to production. For the core operating model and vocabulary, see Forge SDLC overview and What is ForgeSDLC?.
Reactive Programming
Reactive programming models programs as flows of events over time: producers emit values, operators transform and combine streams, and subscribers observe outcomes. It pairs naturally with UIs, sockets, and message brokers where asynchrony, cancellation, and backpressure are first-class concerns.
Parent context: See ../SOFTWARE-ENGINEERING.md § 1. Programming paradigms (Reactive row) and § 6. Concurrency and parallelism for failure modes that reactive stacks must still respect.
Core concepts
Concept
Definition
Why it matters
Observable / stream
Typed sequence of events (next, error, complete)
Unified model for clicks, HTTP, logs, ticks
Subscriber
Consumer that reacts to stream lifecycle
Where side effects usually live
Operators
Pure(ish) transforms on streams (map, switchMap, …)
Composable async logic without nested callbacks
Backpressure
Slow consumer signals producer to slow or buffer
Prevents unbounded memory under burst load
Schedulers
Execution context (thread pool, main thread, …)
Controls where work runs and hop boundaries
Cold observable
New subscription → new producer run (per-subscriber)
File reads, single HTTP calls
Hot observable
Broadcast shared source to many subscribers
Mouse moves, market ticks, Kafka partitions
Cold vs hot — decision cues
Signal
Prefer cold
Prefer hot
Each subscriber needs full history from start
Yes (e.g., replay on subscribe)
Rarely — add replay operator if needed
Shared expensive source (socket, sensor)
Duplicate work risk
Share multicast / publish
Testing determinism
Easier to reason per-subscription
Requires virtual time / shared scheduler discipline
Late subscribers
OK to miss prior events
Must use BehaviorSubject / replay buffer explicitly
Misclassifying cold/hot is a top source of “works in dev, duplicates work in prod” bugs.
Error propagation and termination
Strategy
Meaning
Good for
Propagate onError
Stream ends with failure
Fatal faults, contract violations
retry / backoff
Transient network blips
IO-heavy graphs
onErrorReturn / fallback
Substitute value or alternate stream
Degraded read models
Global handler
Central logging + metrics
Never swallow — always observe
Reactive errors are not exceptions floating through random stack frames: they travel the same channel as values, which is powerful if you do not scatter empty catch blocks in subscribers.
Multicast, subjects, and discipline
Construct
Use carefully because
Subject / BehaviorSubject
Easy to create hot mutable hubs that leak subscriptions
share / publish + connect
Multicast must match expected replay and refcount semantics
Nested subscriptions
Prefer higher-order operators (switchMap, mergeMap) over manual inner subscribe
Code review hint: any manual subscribe inside subscribe deserves a second look — usually an operator already expresses it.
sequenceDiagram
participant Src as Event source
participant Obs as Observable
participant Op as Operators
participant Sub as Subscriber
Sub->>Obs: subscribe(request N)
Obs->>Src: pull / listen
Src-->>Obs: event
Obs->>Op: onNext
Op-->>Sub: transformed onNext
Sub->>Sub: process (slow)
Sub-->>Obs: request more / backpressure signal
Note over Sub,Obs: Consumer governs rate when supported
Src-->>Obs: complete
Op-->>Sub: onComplete
Exact backpressure APIs differ by library (Reactive Streams request(n), Kotlin Flow collectors, etc.), but the contract is the same: don’t outrun the sink unless you explicitly buffer and accept memory risk.
Reactive Manifesto — pillars
Pillar
Meaning for builders
Responsive
Time-bounded reactions; degrade gracefully under load
Resilient
Failure is modeled; recovery paths exist (retry, fallback, circuit break)
Elastic
Scale out/in under demand; no single choke point by design
Message-driven
Async, non-blocking boundaries; explicit messaging between components