RxJS: An object lesson in terrible good software

We recently used RxJS on a large, complex asynchronous project integrated with a big third-party distributed system. We now know more about it than, frankly, anyone would ever want to.

While we loved the approach, we hated the software itself. The reasons for this are a great lesson in how not to do software.

Our biggest issue by far with RxJS is that there are two actively developed, apparently stable versions at two different URLs. RxJS 4 is the top result from google for RxJS and lives at https://github.com/Reactive-Extensions/RxJS, and briefly mentions that there is an unstable version 5 at a different address. RxJS 5 lives at https://github.com/ReactiveX/rxjs and has a completely different API to version 4, completely different, far worse (“WIP”) documentation, doesn’t allude to its level of stability, and is written in typescript, so users will need to learn some typescript before they can understand the codebase.

Which version should new adopters use? I have absolutely no idea. Either way, when you google for advice and documentation, you can be fairly certain that the results you get will be for a version you’re not using.

RxJS goes to great lengths to swallow your errors. We’re pretty united here in thinking that it definitely should not. If an observable fires its “error” callback, it’s reasonable that the emitted error should be picked up by the nearest catch operator. Sadly though RxJS also wraps all of the functions that you pass to it with a try/catch block and any exception raised by those functions will also be shunted to the nearest try/catch block. Promises do this too, and many have complained bitterly about it already.

What this means in practice is that finding the source of an error is extremely difficult. RxJS tries to capture the original stack trace and make it available in the catch block but often fails, resulting in a failed observable and an “undefined” error. When my code breaks I’d like it to break where it broke, not in a completely different place. If I expect an error to occur I can catch it as I would anywhere else in the codebase and emit an observable error of the form that I’d expect in my catch block so that my catch blocks don’t all have to accommodate expected failure modes and any arbitrary exception. Days and days of development were lost to bisecting a long pile of dot-chained functions in order to isolate the one that raised the (usually stupidly trivial) error.

At the very least, it’d be nice to have the choice to use an unsafe observable instead. For this reason alone we are unlikely to use RxJS again.

We picked RxJS 5 as it’s been around for a long time now and seems to be being maintained by Netflix, which is reassuring.

The documentation could be way better. It is incomplete, with some methods not documented at all, partially documented as a mystery-meat web application that can’t be searched like any normal technical documentation. Code examples rarely use real-world use-cases so it’s tough to see the utility of many of the Observable methods. Most of the gotchas that caught out all of our developers weren’t alluded to at any point in any of the documentation (in the end, a youtube talk by the lead developer saved the day, containing the first decent explanation of the error handling mechanism that I’d seen or read). Worst of all, the only documentation that deals with solving actual problems with RxJS (higher-order observables) is in the form of videos on the paywalled egghead.io. I can’t imagine a more effective way to put off new adopters than requiring them to pay $200 just to appreciate how the library is commonly used (though, to be clear, I am a fan of egghead).

Summed up best by this thread, RxJS refuses to accept its heritage and admit that it’s a functional library. Within the javascript community there exists a huge functional programming subcommunity that has managed to put together a widely-adopted specification for writing functional javascript libraries that can interoperate with the rest of the available javascript functional libraries. RxJS chooses not to work to this specification  and a number of design decisions such as introspecting certain contained values and swallowing them drastically reduces the ease with which RxJS can be used in functional javascript codebases.

RxJS makes the same mistake lodash did a number of years ago, regularly opting for variadic arguments to its methods rather than taking arrays (the worst example is merge). Lodash did eventually learn its lesson, I hope RxJS does too.

3 thoughts on “RxJS: An object lesson in terrible good software

  1. Ben Lesh

    Hi I’m the project lead on RxJS. I’m sorry that you had a hard time with our library. Our documentation is particularly a sore spot. And I agree with many of your points. We only have a handful of people working on the project, and we’re all doing it voluntarily in our spare time.

    I would love to have your help on the project, if you have a few hours a week to dedicate to it. In particular we need a lot of help updating our documents and providing good examples. It would be a good opportunity for you to contribute in an ongoing way to an open source project that’s used by many people. I think you could do a lot of good. Writing some good examples in documentation probably would require the same if not less effort than this blog post, and you would be helping a lot of people.

    1. Alex Holmes Post author

      Hi, thanks for responding. I’m sorry this post has been so badly received; I stand by each of the criticisms and generally I don’t think that something being free should prevent people being critical of it. I can’t (and probably shouldn’t) contribute documentation to undocumented methods or functionality when I don’t understand them (due to the lack of documentation), and I’ve since switched to using most.js, as recommended by one of the core rx contributors here https://github.com/ReactiveX/rxjs/issues/34#issuecomment-256925661 .

      I didn’t know this had been posted on reddit until I was cc’d into your twitter discussion but I’ve since responded to Andre on the reddit thread https://www.reddit.com/r/javascript/comments/6ooa2b/rxjs_an_object_lesson_in_terrible_good_software/dkkcxo6/

  2. Dominic Watson

    I find my relationship with RxJS to be that of love/hate. There was a graph floating around showing the learning curve for AngularJS where the guy loved it…. then hated it… then loved it and so on and I feel the same about RxJS. One minute I feel really clever because the Observable chain makes so much sense and I’m composing them and loving how I can see the lifecycle of a variable in a stream. When I need to debug something I absolutely despise it cause there’s just no visibility outside of .do(console.log) statements.

    This site looks like a good step in the right direction for visualising what’s actually happening, but it would be amazing if this was baked into the IDE: https://rxviz.com

Comments are closed.