Virtual threads are free
On JVM 21+, each session runs on its own virtual thread. Blocking on I/O yields the carrier. There's no penalty for a blocking API — and the code is much simpler.
Advertisement
Reactive is now overhead
Adding CompletableFuture everywhere makes the API harder to compose, especially with try-with-resources and stream operations. Virtual threads removed the need.
Advertisement
Streaming is the exception
public Stream generateStream(GenerateRequest req);
// Stream is the natural fit for chunked responses — no need for Flow/Publisher.