Imperative, functional, reactive programming – which one to use when and for what?

23
Clap

Imperative programming, functional programming, reactive programming – which one to use when and for what?

If you have read about all these various programming paradigms and are confused about which one to use for your next project or which one to apply while refactoring your existing application design, then it is perfectly normal.

A few years back when I read about all these programming paradigms, it felt to me like all these are existing old patterns and techniques but given proper name & shape. I have used publisher/subscriber, producer/consumer, observer/observable, event processing, mathematical computation & immutable data structures from the start of my career but without segregating them in one of the above programming paradigms. I hope most of us will have the same experience, using some of it well before knowing explicitly by name. If you have written a UI-based application like web UI or standalone UI in java swing (or other frameworks) you may remember all the event & action listeners that we register for a button, key event, and mouse click events. You may also recall the use of message queues & topics for publishing events for the subscriber(s) to consume in a distributed application. More often than not, we call general utility method(s) like min, max, sum or for example call an existing method to return all orders above a particular value, etc. i.e. reusing the method for operating on a collection and not rewriting every time with a “for” to loop through a collection/list/array. Certainly inside the method, it may have a loop but for the caller, it is a mathematical operation.

Although these techniques are pretty old, there are more and more boundaries defined around them now. It is also good to know these are now given proper name & definition. In certain cases, there is specification & guideline built around it which makes it easier to get adopted into any programming language. Moreover, as with any design patterns, it becomes easier to explain & discuss these within a team. For example, there is the “Reactive Streams” specification which is adopted in javascript and in java as “java.util.concurrent.Flow” since java 9. There are also third-party libraries like RxJava, RxJS, Akka, and many more which provide these functionalities out of the box, easing the work of the developer from worrying about handling multiple subscribers, back pressure, asynchronous, concurrent & parallel processing.

As mentioned before based on the business challenges in hand, I have used all of those and in some cases even used all of those within an application. I hope and believe most of you will be in the same boat as I am, where you may have already used most of it within your application even before you formally came to know all these programming paradigms. It is also quite natural to question which of these we should use for our application.  Is one better than the other?

Definition

Imperative programming is fundamental to any programming language and even machine language is imperative. Internally other programming paradigms in some way or the other use imperative style internally but abstracts this out for the caller. Imperative style is mostly sequential and follows a natural flow, hence it is easy to write, debug and troubleshoot. In most cases, this offers better raw performance than others.

Functional programming is treating computation as “mathematical functions”, using pure function without side effect which does not change state. The state is immutable in functional programming.

Reactive programming is typically asynchronous events data stream over time and its propagation of change. It is built on observable (publish/subscribe) & iterator patterns. Generally, there are also functional programming and reactive programming done together for the transformation of one stream to another.

Reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change.
– Wikipedia

In my view, these are equals and there is no one better than the other. All of these will excel in particular scenarios and will function at a sub-optimal level in certain scenarios. As an architect, I am also faced with the challenge (in a positive way) to help my developers understand these and to make a better choice for themselves. Below are some guidance I share within my team,

[Disclaimer Note: These are guidelines and not hard rules]

  • Always start with imperative programming for synchronous request/response. As much as possible, it is better to let containers (tomcat, jetty or JBoss in case of a java web application) to handle threads & concurrency rather than handling within an application.
  • While handling streaming and data manipulation like count, sum, transformation use functional programming, i.e. instead of iterating every time, call a method (or function) on the collections to do it for you.
  • If there is an asynchronous flow or when one or more components have to know about other component changes (create, edit & delete) then use reactive programming (event-based publisher/subscriber model).
  • If it is asynchronous processing that needs to go through several transformations and has processing then use functional reactive programming.
  • If you are unsure, then fall back to imperative programming by default. This will help us change when we have a better understanding and walk through the code later.

Most probably we will end up using all the above in a single application, especially if we are building a monolith application with multiple components running within a single web application. If you are building an application based on microservice architecture then the same model can be applied in a distributed environment & vice versa. In this case, each microservice application is treated as a “distributed component”.

  • Use the Event-based publisher/subscriber model as a primary way of integrating microservices. As the services are distributed this needs to be done with a messaging queuing platform like RabbitMQ or can use topic-based streaming platforms like Kafka. This way the load on the HTTP REST/RPC based API will be reduced.
  • Use API (REST/RPC request-response over HTTP) based integration when absolutely needed especially when business transaction spans multiple services. Use the Saga pattern instead of the two-phase commit. More on https://microservices.io/patterns/data/saga.html
  • For ETL*, OLAP*, data analytics, streaming prefer a “Streaming Platform*” like Apache Kafka and an analytical platform like Apache Spark 2.

*ETL – Extract-Transform-Load – Mostly used for migrating data from one form to another.

*OLAP – Online Analytical Processing.

Instead of using the queuing system for communication between microservices (1) and a streaming platform for the analytical platform (3), it is also good to use one “Streaming Platform” to achieve both.

It is also vital to note that often in coding & development, efficiency is preferred more than speed.  Certainly what “Efficiency” is, differs from application to application but I define it as an application that is robust, scalable, performant, easy to understand (code), debug and troubleshoot quickly.

There are also debates and discussions on what is reactive and functional reactive programming.  There are also discussions on the boundaries and what it is not. To me as long as we use them and achieve our functionality we don’t have to worry about what programming paradigm it falls under, after all, that is in its name. If it does not falls under any of these then we can define another new name for it.  Patterns help us in understanding, explaining, and discussing the solution easier & faster. It is also good to know the different patterns used in the applications. As you can see, the same pattern can be applied in front-end (UI/UX) development, back-end application development, distributed computing, and in the analytical platform. It is important to note that this enables an engineer to understand the overall application and make a huge difference in the life of engineers (& team) especially full-stack engineers to shift over to different areas of an enterprise application and quickly understand the ecosystem.

In summary, to build an efficient application, it is common to use more than one style of programming paradigm, even if you resists you may be using these in some way or another especially if you are building a medium to large-scale application. However, we don’t have to define boundaries and discuss on a written application whether it is imperative, reactive, or functional. It may be one or mix and match of all. Drawing boundaries and trying to identify your application using one or the other will only limit its capabilities. Rather than identifying those as different styles of programming, treat them as yet another design pattern within an application. Treating them as yet another pattern will help us to realize these can co-exist and complement each other than replacing one style with another.

Happily architecting, designing & coding.

—–WRITING TO LEARN AND LEARNING TO WRITE —–

Clap

ClapsClaps(23)

Also published on Medium.