As of this writing, I've been working on the RxJS project for almost 6 years, I think. When I started out, I really had no idea what I was getting into (And I wouldn't have been able to ship those first versions without Paul Taylor and others, for sure). I can remember looking at the number of weekly downloads on npm, and being able to figure out how exactly how many of them were mine. Fast forward to today, RxJS and observables have become extremely popular... Loved by many, hated by a few, and my fear is, misunderstood by most.
Observables vs Reactive Programming vs RxJS
A big problem I see nowadays is how observables have now been intimately linked, for better or worse, to RxJS. Looking back, I sort of wish we had published the Observable
primitive as a separate package, and the operators in another package.
When this effort started, I was naively optimistic that Observable
would land in the ECMAScript standard, and RxJS would just "become a collection of helper functions" as I think I put it. But years passed, and the TC39 proposal stalled. And the way the world came to know Observable
was through RxJS.
Observables are not RxJS. Observables do not require "operators". They are a primitive. The "dual" of the Iterable
. A simple push-based type. Nothing more.
Reactive programming isn't necessarily observables. Reactive programming is a paradigm or a practice. It can be done with functions, Promises, etc. In essence, if you can compartmentalize your code into functions that will "react" to incoming events without knowing anything about the source, congrats, you're "reactive".
RxJS is a library of functions built around observables, not the other way around. Observables can, and do, exist in the wild without RxJS. They show up in other libraries, often times in slightly different shapes, but the overall concept is the same. Facebook's Relay has an internal Observable implementation that is eerily similar to RxJS's implementation. In fact, I've lost count of the number of times I've seen an abstraction that amounts to an interface that accepts a callback to handle multiple values, an error, or a completion, and returns or otherwise uses some sort of cancellation semantic.
Regrets
1. The huge API
RxJS 5 inherited its HUGE API surface area from RxJS 4 and under. RxJS 4 and under, in turn, inherited it's API from RxNET, many, many years ago. So much of the API that some might deem "unnecessary" exists because "it always has been, and always must be". RxJS 5 might have been our only chance in the history of the library to truly ween that down. Which we did a bit, but probably not enough. The large API surface leads to confusion and loathing in the community. All of which is understandable, IMO.
2. RxJS out-shined Observable
Observables never had a chance to shine on their own. The real win, IMO, to RxJS is the Observable
type itself. Not the operators. Those are just fluff that allow you to do some cool things. Having a lazy type with guarantees like Observable
is actually a bigger deal.
With Observable
you're guaranteed:
- Once it's complete, errored, or unsubscribed, you will get no more messages
- Registered teardown WILL occur. If you complete, error, or unsubscribe, you are guaranteed to clean up resources.
- A unified API that can represent a wide variety of things: Events, multiple values, single values, user interactions, streaming data, synchronous values, asynchronous values, etc. etc.
There are other great advantages to its design. But IMO, those are the biggest.
RxJS and all of its operators are inseparable in some people's heads from observables. And that's a real shame. Observable
is a simple thing. A very simple type. RxJS is complicated with it's huge API and odd names.
3. We never really outlined where RxJS would best serve people
Disclaimer: These are MY opinions on RxJS/Observable use, and not really the RxJS core team's. Feel free to use RxJS or whatever library in whatever way you see fit. If it works, you can maintain it, and you can test it, IMO it's good code. The end.
To put it simply, once people get into RxJS, it's an exciting technology. It suddenly gets used for everything. It's fair to say this mentality exists in tech for a lot of libraries and frameworks. But I think with RxJS it becomes insidious to the detriment of the RxJS community.
Examples:
You have a button that, when clicked, fetches the latest data and displays it. Do you need full-on RxJS? No, probably not. "But what about cancellation???" .. You wanted an observable. Not operators. You can use RxJS here for the
Observable
implementation, but I would caution against jumping intoconcatMap
et al. Especially if your team isn't used to RxJS. But that doesn't mean you shouldn't useObservable
. In fact, you probably should.You have streaming data over a web socket, and you need to split it into a couple of different streams and update two parts of your UI. Yes! This is what RxJS is for. You're a
filter
operator away from a solid use case.You have complex async coordination and/or race conditions, even with APIs that return promises? Honestly, you might want to use RxJS here as well, because of guarantees provided by
Observable
, and useful operators likeconcatMap
that can guarantee ordering, etc, and have complete interop withasync/await
andPromise
.
4. We never taught people how to write readable code with RxJS
We handed people powerful tools and let them go at it. No guidance or experienced wisdom provided with how to effectively use the library so that you didn't drive your coworkers crazy. This is sort of like getting a power tool set with no manuals. How do you maintain it? How do you resolve issues? Where do you store the tools? etc.
The result of this is people write code they don't understand when they revisit it. Most amazingly, some engineers, who are usually a rational bunch, then declare RxJS to be "unreadable", as in, no matter what they did, they could never make the code readable. Seems defeatist to me. Like anything else, good practices and strategies around reading and organizing rxjs code can be learned and taught. But I know that I personally haven't done enough to spread this know-how.
Consequences
For the most part, I think the response to RxJS has been overwhelmingly positive. The community has organized a conference. I've seen a lot of discussion about it across many communities (beyond just Angular). And usage has been steadily growing.
But on the back swing, there is a trail of destruction to the reputation of RxJS and Observable that has been wrought by misunderstandings about Observable and RxJS, and misuse of the library in general, IMO. There have been well-known tech personalities who have called out "wishing RxJS didn't exist". And my fear is that sort of thinking, if it spreads, will spell doom for the Observable
type itself. That would be the biggest shame to this, honestly.
The Observable
itself is huge win. It's a primitive that, like I said above, shows up in many forms in many places, and I think it deserves a spot in the language as much as Iterable
and Promise
. People having a distaste for RxJS's API and/or abuse and misuse is completely understandable, IMO.
There are parts of RxJS I don't like, and here I am unable to pivot the library quickly because it's so popular we'd simply break too many people. But the parts I like the most, the Observable
itself, and the guarantees it provides, are in jeopardy of being thrown out with the bath water by some folks. And that's tragic, IMO.
The road forward
For my part, I plan on trying to continue to champion promoting understanding of the when/where/why of RxJS and Observable. And I want to do better to disambiguate Observable from RxJS. I also want to work very hard to simplify the RxJS API: Tighten the API, remove what does not need to be there, improve documentation and readability, add more guidance for folks on how to make their code more maintainable, etc.
Don't get me wrong, I have other regrets with regards to RxJS as it stands, but I'm confident we can remedy all of those things over time. My deepest concern is that there are a huge numbers of people that still don't understand the Observable
primitive and its benefits, because they associate it with RxJS and are standoffish about getting involved there because of the learning curve.
Latest comments (34)
I was that teammate that pushed for RxJS in an Angular codebase and that made some other devs on the team uncomfortable. To my mind, I thought I was absolutely on the right path because the APIs in Angular return Observables, so why not "follow the grain" of the tools we're using everyday?
I get it now. I was trying to treat RxJS as a next generation Lodash and in doing so the code I wrote... Let's just say it was not easy to read.
But the concept of having a single primitive type that that can make guarantees about the completion, error handling, and cleanup of resources in an asynchronous world is, I think, absolutely essential and I am definitely not dropping Observables!
I love this post (1 year later). RxJS is amazingly powerful and terrible. The complexity that can get introduced with pipes, the callback hell (nested switchMaps that need catchError) that that community spent so long getting rid of all of a sudden became ok again in RxJS. Nobody can write good RxJS code unless they've been doing it for a year. The amount of time seniors have spent walking juniors through an RxJS pipe (and then the amount of time juniors have spent trying to make a change in that pipe and understand what they've done) is massive.
But, as you said, Observable itself is golden.
Go for it - split out the Observable into a separate package and make RxJS 7 depend on it!
Thanks. This sums up a lot of my problems when working on Angular app.
I often felt that what I am writing is not how it should be, but finding examples of what are good practises was really hard. When things clicked I usually was really happy with how powerful and expressive RxJS is, although as you mentioned, size of the API was daunting and knowing what and when to use was a herculean task.
Thanks for your insights!
I've noticed the libs that have better documentation & real world examples people can copy/paste do the best.
what Shawn said!!!
Good and honest article ... I think the two things that you mention (large API surface and opaque use case) are exactly what kept me from ever getting "into" RxJS.
Thank you for writing this.
It must be hard to admit things could have been better. But this is the right way to move forward. I wish other community leaders would follow you, and talk openly about their community problems and how they plan to fix them.
Knockoutjs is a great library that extracts the observable without the operators. I find it also lacks the ceremony of Angular which makes it easier to understand and Steve Sanderson has created a Visual Studio template that uses typescript and has a hot reload.
I still find that RXJS (the combination of observances and operators) can do things that as of yet the detractors can not provide alternative solutions to. This means I’ve had a project that I started without RXJS fail but then solve it by using RXJS.
Is it worth mentioning
zen-observable
here? It's pretty bare-bones and Apollo uses it, so it's the observable API I became familiar with and started using in my own projects.thanks for your work on rxjs. I have just begun working with rxjs through angular. I think is such a powerful primitive, but it has links into functional programming which is another thing people (including me) seem to strugle with. That doesn't say that functional programming is bad or Observables are bad. In fact, I would love to master both. I believe I would be able to build much better software if I would master these concepts.
Again, thanks for your awesome work!