DEV Community

Cover image for Ability to unlearn in Software: Reactive Programming
Juan Camilo Casas
Juan Camilo Casas

Posted on • Edited on

Ability to unlearn in Software: Reactive Programming

Java 21 released Virtual Threads aka Project Loom as one of the most impactful changes in the language for years. As well as frameworks such as SpringBoot 3.2 giving full support now. That is why the software community needs to rethink the future of reactive programming and its additional complexity in terms of coding, troubleshooting, and maintenance, in contrast with the classic Java imperative programming.

"I quite like reactive programming but appreciate that some people don't want to do it or deal with it."

- Josh Long, presenting Loom in Spring 3.2

Almost two years ago there was this controversial decision among the engineers of my team along with the CTO regarding the technology path we should take for new services on whether or not to use reactive programming.

At that moment, must mention that reactive programming was very popular among the engineer members, and the normal reaction was to continue using it. Those who had experience with it, including me, were already using it for 3 or 4 years with it using Spring Webflux or even before with RxJava.

To give the outcome of the decision, at that moment we were aware that Loom was in its earlier stages, and not yet available to the public. Then we decided to not continue with reactive programming and Spring WebFlux. And looking at it, it turned out to be a good decision! But you may think how controversial was among the team…

First, like everything, architecture is about trade-offs, and not all decisions apply to all cases, I can explain some of the reasons that led to consider that decision:

Non-blocking IO and its promise

We can't deny that the main benefit of the reactive model was the ability to free up threads on expensive IO operations.

But first of all, easy boy, don't get so excited from the beginning. In the system design stage, it is required to know really what the expected qps and load for databases, other services, and so on. If the load is so high that services require autoscaling up and down per minute, and there is a huge amount of instances per pod, that the cloud provider is eating your pocket every month on your bill in cost of on-premise instances, then maybe… maybe it is the right decision, but let's take more considerations into the plate.

Libraries not ready for the non-blocking model

Let's say, the product is in its scale-up moment, the qps expected no longer 10 and the architecture is worth reconsidering and preparing for the huge amount of requests of the next step of the evolution.

Hang on, some libraries don't support Java non-blocking at that moment and using java NIO interface it is not easy to implement, and due to this, many projects had a mixture of non-blocking and blocking calls, and synchronous process that are not ready to be asynchronous, i.e if you have a very expensive like encryption that takes very long and the operation is IO blocking, then there is a bottle-neck on the thread pool that doesn't show the real impact and benefit of the reactive programming code on the rest of operations.

If you say, we don't have this problem with libraries, and it is not foreseen, then there are more things to consider.

Cloud Cost vs Developer Cost

Traditional imperative programming is more understandable among all levels of engineers, from juniors to seniors. In contrast, reactive is different, as its learning curve is steeper, and the paradigm takes its time to understand…

That impacts directly on the deliveries of your team and the business agility. Imagine if there are junior developers, and the test coverage is not the best, then you find that reactive pipelines handling of errors is quite complex, and an error in reactive is like a problem that should appear in your house then it appears in the public street! As pipelines errors appear in other parts of the code, with very small tracks of its origin.

Therefore, if we compare the costs of maintaining line codes in imperative programming vs the cost of reactive may vary, and probably, writing the same code in imperative is 2x, better for business. And if you consider the cost in Cloud to have 2x instances, and the hours of senior developers maintaining that cost, maybe the cloud is cheaper, so let's be conscious about maintenance costs vs blocking IO operations.

What now?

The release of Spring Boot version 3.2 with the support of Virtual Threads, and a simple *spring.threads.virtual.enabled: true *to provide IO non-blocking in Tomcat. That is huge!

The problems right now are different. I Just wanted with this article to show how an analysis of different perspectives can save teams and businesses for better results and ahead of the changes with better stakes in the future.

I just want to finish with an idea of Venkat Subramaniam, in one of his conferences that took in Barcelona, that in the path of software, there are lessons, and there are technology and concepts that we might require to learn and also some that we require to unlearn!

Top comments (1)

Collapse
 
stealthmusic profile image
Jan Wedel

When webflux was launched, we decided, to create a new webflux microservice next to our WebMVC ones.

It was so hard to implement seemingly simple use cases that we also decided to stick with MVC.

I had a longer discussion with Josh Long (the one from your picture) on Twitter some years ago but he was pretty dogmatic back then. I think that has changed, especially also since Loom.