Reactive Programming — Fundamentals, Backpressure, and When It Helps
Fast recall
Quick revision
Core answer
Publisher, Subscriber, Subscription, Processor), implemented by Project Reactor (Spring) and RxJava (Netflix). It helps when you have many concurrent I/O-bound operations, need streaming (SSE / WebSocket), or compose async flows with backpressure. It does not help CPU-bound work. In 2026 with JDK 21 virtual threads, many "we need reactive for scale" arguments soften — pick reactive for streaming and true async pipelines, virtual threads for blocking-code scale.Core reactive understanding; honest perspective on when reactive is worth the complexity vs virtual threads
Using reactive everywhere 'for scale'; blocking inside Mono/Flux; ignoring backpressure
Discuss Reactive Streams spec, backpressure strategies, Project Loom impact, debugging trade-offs, context propagation
Interview-ready guide
Interview answer
Reactive programming is non-blocking, event-driven data flow with explicit backpressure. Instead of one thread per request blocking on I/O, a small event loop handles thousands of concurrent operations via callbacks.
Three core ideas
- Non-blocking I/O — threads don't wait; callback on response
- Async composition — operators chain declaratively (
map,flatMap,zip,filter) - Backpressure — consumers tell producers how much they can handle
Java ecosystem
- Reactive Streams standard —
java.util.concurrent.Flow - Project Reactor (Spring) —
Monofor 0..1,Fluxfor 0..N - RxJava —
Observable,Single,Flowable,Completable,Maybe - Spring WebFlux on Reactor Netty
- R2DBC reactive DB drivers; Spring Data Reactive for MongoDB, Redis, Cassandra
- RSocket for reactive RPC
`Mono` and `Flux` are recipes, not values
- Cold by default; nothing runs until subscribe
- Contrast
CompletableFuturewhich starts immediately
Backpressure differentiator
- Signals —
onSubscribe,request(n),onNext,onComplete,onError - Reactor strategies —
onBackpressureBuffer/Drop/Latest/Error flatMap(fn, N)concurrency parameter is your throttle- Hot vs cold publishers — late subscribers miss hot events
Reactor in practice
flatMapfor async;Mono.zipfor parallel composition- Error handling —
onStatus,timeout,retryWhenwith backoff,onErrorResume - Reactor
Contextis the reactive ThreadLocal - Wrap blocking calls —
Mono.fromCallable(...).subscribeOn(Schedulers.boundedElastic())
When reactive helps
- Thousands of concurrent slow I/O
- Streaming — SSE, WebSocket, chunked HTTP, Kafka
- Reactive downstream all the way
- Memory-constrained environments
When reactive does NOT help
- CPU-bound workload
- Blocking dependency (plain JDBC, blocking SDK)
- Team without reactive experience
- Service already healthy on thread-per-request
Common mistakes
.block()in reactive code — deadlocks or stalls- Ignoring backpressure — memory explosion
- Blocking without
subscribeOn— event-loop stall - Loss of MDC / tracing — use Context Propagation
- Forgetting to subscribe — code does nothing
Project Loom changes the calculus (JDK 21+)
- Virtual threads — 10K-100K cheap threads per JVM
- Tomcat on virtual threads handles 10K+ concurrent with simple blocking code
- For "scale the blocking code" problems, virtual threads often win
2026 recommendation
- Default — Spring MVC + virtual threads
- WebFlux when streaming or reactive-all-the-way is a real requirement
- Don't mix blocking and reactive in one service
Strategic answer
- Reactive is about non-blocking, composition, and backpressure
- Not a universal scaling magic bullet
- Great for streaming and truly async pipelines
- Virtual threads cover the "just scale my blocking code" case in 2026
- Pick reactive when streaming or reactive-all-the-way genuinely fits
Reactive Programming — Fundamentals, Backpressure, and When It Helps
Direct Answer
Reactive programming is non-blocking, event-driven data flow with explicit backpressure. Instead of one thread per request blocking on I/O, a small event loop handles thousands of concurrent operations through callbacks. The Java standard is Reactive Streams (Publisher, Subscriber, Subscription, Processor), implemented by Project Reactor (Spring) and RxJava (Netflix). It helps when you have many concurrent I/O-bound operations, need streaming (SSE / WebSocket), or compose async flows with backpressure. It does not help CPU-bound work. In 2026 with JDK 21 virtual threads, many "we need reactive for scale" arguments soften — pick reactive for streaming and true async pipelines, virtual threads for blocking-code scale.
Common Mistakes & Pitfalls
Using reactive everywhere 'for scale'; blocking inside Mono/Flux; ignoring backpressure
Check your understanding
Answer these without looking back at the article.
- How do virtual threads (Project Loom) change when to choose reactive?
- Explain backpressure strategies in Reactor and when to use each
- How does Reactor Context replace ThreadLocal and MDC across threads?
- When would you mix blocking code into a reactive pipeline and how?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.