So why have I changed my tune? Well, the short of it is that I have had to write a lot of asynchronous code in the last year. In different languages. In different code bases.

Now, you might be thinking I have Stockholm syndrome. And I might. But I have tried other paradigms. I have written a lot of JavaScript promises, and I have played around with Kotlin coroutines.

Every tool has a time and place

And what I have learned is that Rx is great for modeling streams of data or event-driven apps. Thinking about a flow of data as a stream makes sense. And once you learn the operators, you can express complex ideas in very little code.

Promises are great for modeling purely async work, i.e. do some work and come back when you are done. This makes a lot of sense when you want to perform a transaction of some kind, like saving to database. You don’t want to lock up the app on a costly write that might be slow and you want to wait for the result, to know if the save was successful.

And sure, Rx is a lot to learn. It is hard to wrap your head around. Marble diagrams are weird. And while getting the hang of promises is easier, there are a lot of subtleties to it too. But I say, these toolsets aren’t the problem.

The bottom line is:

Asynchronous Programming is hard

It is hard to read. It is hard to write. Especially at scale. Do you think your async code works? Scale it to a million threads. Put millions of emissions through your chain. Does it work the same? Your small system or development environment probably works great. But once you have many chains and many threads, things get hard to reason about.

I mean, can you fully explain the shared counter variable problem? The problem detailed here. I can’t off the top of my head. Even if you can, I am sure it did not come naturally the first time you saw the example. It just doesn’t make a lot of inherent sense. And this is the tip of the iceberg of the subtleties of async programming. (Schedulers? Managing your own threads? Is your system using real threading or faking it?)

Side projects don’t make money.

The other problem is that there are very few large synchronous systems. It’s fine to write your to-do list app with no Rx or Promises, but most systems that generate money need to support hundreds of thousands of users. Scaling up requires threading, it is an unfortunate fact of life. And working on large, possibly old codebases is every programmer's nightmare. Again, you shouldn’t necessarily blame the tools then. Async programming puts pressure on the engineer.

The crux of the issue is though, is that we as humans think about programs one line at a time. We can write async code that works in small cases and localize the problem to a few lines of code. But we can’t think about how these systems work at scale or how they interact with each other. Our brains only have one thread!

And that’s okay. Writing large, asynchronous codebases is hard work. But I think the hate of the tools is unwarranted. Rx, when learned, is a great spec for modeling streams or events. It really is. But point the energy away from picking apart the tools and point the energy to grasping the difficult concept that async programming is.