RxJS is great. So why have I moved on?

Bobby @ fiskal.app
9 min readDec 16, 2015

--

If you like this article, check out my work to solve personal finance at fiskal.app.

Almost a year ago I started using React for “greenfield” internal projects at work. One of the huge benefits of the web is everyone brings patterns and libraries from other programming language into JavaScript. FRP is one of these libraries to recently be ported to JavaScript. After trying a couple Flux implementations for React I was attracted to FRP (Functional Reactive Programming) libraries like RxJS and Bacon. I spent 4 months learning and building an internal app with FRP and React.

FRP tries to solve the problem of asynchronous work, event propagation and data transforms that happen as a result of events about your code base. Basically you create an observable that reacts when an event is triggered. The trigger could be a UI event, like on click, or a network request or other things. Each trigger returns a stream that you can chain (ala jQuery) to change or combine other streams together. ReactiveX group does a much better job explaining what they do. Their site is really great at explaining FRP http://reactivex.io/.

RxJS and FRP in its prime

RxJS does a lot of things really great. It allows you to create custom event streams that can have multiple listeners on the output. The observables model and chaining transforms are very useful and help keep related pieces of code together. Observables create stream that can be combined with other streams. It takes away the global events and keeps them localized to where your code needs the to react to the events. If you’ve used Mutation Observers or data binding or Backbone events in JS or KVO or NSNotificationCenter in Objective-C, RxJS like that but much more powerful. RxJS elevates data flow to be a first class architectural pattern on the level of MVC.

RxJS elevates data flow to be a first class architectural pattern on the level of MVC.

Barriers

So why did I move on from FRP? I spent 4 months with 8 hours a day of coding with FRP. I’m no expert but I hit some limitations that caused me to look to see what else was out there. It might just be that I have just not used FRP “properly” and I would love to understand it better.

In my app I had streams for network requests that transform server responses to a structure that was more useful for the UI. There were also observers on UI events. FRP has a very unique API that takes a bit to learn, even if you’ve done map/reduces before. The DSL being very unique made it harder to reuse other functions in my code base. I couldn’t reuse code but the Observable and Streams could be pasted around to leverage in other classes.

Merging and combining streams is where it got muddy really fast. I had events going that were hard to track down. I lost the finer grain control when more than one stream was being used. I’m fully aware this could be my misunderstanding about how to use FRP. At the same time, the methods a library exposes guide developers about to best use them. Take for instance Backbone’s vs. Angular’s API. Backbone is every open and easy to do things outside the “intended” use; when in Angular doing things outside the intended patterns are much more difficult. API design guide developers into certain patterns.

In the end, it was harder to reason about everything going on. Debugging FRP processes was stepping through a lot of the internals of the libraries to find where events got twisted. Using FRP in smaller more isolated uses and limiting combining streams was much more beneficial and the file size increase if you’re just using it in place of a single callback function is not very useful. FRP hits the middle ground between these very well for me.

A simpler async and data flow

So looking around and talking to friends I looked into Go lang. It has been getting a lot of press lately for good concurrency, parallelism with and without threads (for good reason). They use a pattern called CSP. You create a channel object that lets data move across it. This is very similar to FRP. Then when you want to push data on the channel or take data out, you just tell it to set the data to a variable and “park” until that data is set. So code that looks like normal is synchronous and walks line by line acutally parks and waits until something comes across the channel before going to the next line. JavaScript will be getting this with `await` in ES2016.

Go lang and CSP is really cool, right? Async work looks like sync work and all the code is together making it easy to rationalize about. But I can’t use that in my web app. That’s where ClojureScript comes in. Their community is known for just using great ideas from other programming languages. ClojureScript’s core.async is fabulous. They basically stole the best parts of Go and FRP but with an API that’s super simple. Core.async feels like programming 5 years in the future. Core async has Go channel, that “park” to make async code look like sync code and on top of that they’ve added transducers. Transducers are these awesome things to take a bunch of abstract functions that do minor task and quickly compose them together and attach them to a channel.

[ClojureScript’s] core.async feels like programming 5 years in the future.

Breaking down ClojureScript

ClojureScript’s Transducers replaced FRP’s the multiple flatMap and take functions. Transducers are just stupid little functions that can be reused in regular map/reduce/filter operations. In contrast to FRP functions used inside streams needed to understand they were in a stream. Functions used in a transducer don’t know they’re in a transducer. Basically I can reuse them with all my current map/reduce/filtering code inside async channel.

ClojureScript’s analogous structure to FRP Observables are Atoms and Atom watchers. In ClojureScript all the data is immutable. When you first hear that is sounds insane but watch Rich Hickey’s Simple made Easy and you’ll see how awesome it is to have immutable data the default option. Atoms are ClojureScript’s mutable data. Using watchers, you can have a single callback function or channel trigger when a piece of data changes.

The last pattern that we heavily use is ClojureScript version of classes. ClojureScript is a functional programming language but they’ve seen the value that OOP languages provide. When it comes to encapsulating dependancies OOP classes are very useful. ClojureScript handles this with Stuart Sierra’s Component library. It really helps control a lot of dependancies in our data flow.

ClojureScript is the Triforce of Prower

I’ve been programming in ClojureScript for the last 6 months. Do I recommend JavaScript developers to use ClojureScript? [N̶o̶] Yes (see how we use cljs & js together at Capital One). To be fair it’s really hard compared to learning another OO language. The learning curve is steep. For the first month I constantly felt like I was on a trip to Japan where I couldn’t read or written or speak and had to rely on grunting and pointing. Debugging core.async code specifically can be rough in Chrome Dev Tools. But for any challenges, learning ClojureScript is the most rewarding thing I’ve learned in the last 10 years.

In the 3 weeks it took to build our ClojureScript app I have never learned much, grew so much as a developer and understood why my OO code had bugs and how to control them. Writing ClojureScript, learning the syntax, patterns, paradigms and ideas from the ClojureScript community has been invaluable. If you’re just using ClojureScript-inspired patterns in another language, there are huge concepts you’ll miss out on. Functional programming is the future (If you look closely in The Martian, their spaceship is coded in a LISP).

http://twitter.com/puppybits

ClojureScript’s Prowess is transparent bridging to other languages

There are tons of great UI developers that can build great work with React and JS. 85% of our development time for our web app at Capital One is building UI. We don’t need a huge barrier for entry to get great developers doing great work in our web app. With ClojureScript they don’t even need to know they are using ClojureScript.

Inside of ClojureScript we use core.async, go channels, transducers, immutable data, components, atoms and atom-watchers. We can take full use of the prowess of ClojureScript. When we want to talk to the JS world we have one single file called export. It puts a JS interface on top everything. When you’re in the JS world you call a function that returns a promise that give you mutable JSON objects. This is our entire code to transform CLJS patterns to JS patterns and allow for interop.

ClojureScript runs in the browser (and Node) for our front-end web app. We’ve done some tests with wrapping it in JSCore. We can build the data synchronization, caching and business logic once and use it to power our React web app (in Node and the browser), expose a Swift/Objective-C interface for our iOS app and possibly even take it to Android. Since our server is in Clojure, we can even take parts of it’s code base and include it in the front-end clients with almost no work. I’m the only front-end developer that needs to know ClojureScript when the rest of the devs just use JavaScript.

ClojureScript is well worth it for anyone who wants learn. The community is great. The patterns I’ve learned give me a deeper understanding about alternate ways to write code. I have less bugs, less refactors and easier refactors of code. Our ClojureScript library has been pretty flawless with only a couple bugs in production. React has had an order of magnitude less bugs compared to my time doing Backbone and iOS apps. ClojureScript had a order of magnitude less bugs than React.

Further Reading

Functional Programming is taking over UIs with Pure Views.
ClojureScript is the Triforce of Power.
Letting People in the Door. How and why to get 2s page loads.

Addendum: 12/18/2015

There was a lot more discussion about this topic than I expected. I wanted to clarify a point about my recommendation of ClojureScript. The only reason I don’t fully recommend ClojureScript for everything is because of the barrier to entry. But for the effort it takes to learn, ClojureScript pays off in spades.

By using vanilla JS for our view layer, we can bring in a junior JavaScript developer (that might not know React) and she/he can start contributing from day one. With in a few weeks, they will be highly effective at creating and iterating on our UI. If we went full ClojureScript, we would have biased ourselves to developers that appreciate the easy of data transforms and data flow in ClojureScript and have also taken the time to learn ClojureScript. For front-end developers what really matter is empathizing with our users, using animations effectively that help the user understand what the app is doing and above all, make features that our users find useful. There’s a lot more of those type of developers using JavaScript right now. OM Next looks fabulous and maybe that will sway me over the fence. But for now, by using ClojureScript for our client-side business logic / data and vanilla JS and React for views, we can leverage the strengths of both with little dilution of either.

There are many in the React community say they’ve been inspired by David Nolen and the wider ClojureScript community. It turns out I’m that guy in the back of the room pushing them to take the next step and really learn ClojureScript. It’s the entirely of ClojureScript can’t be matched by taking parts and converting them to another language. I hope the more people understand CSP, functional programming and transducers the more it inspires them to learn ClojureScript. To make an analogy to 80’s cartoons, you can have the red, black, green, yellow and blue lions all separately but just think what you could do if you started out the episode out with Voltron?

As for FRP, it’s great and is a very useful and practical pattern for data flow management. They have a lot of great ideas and it’s great to see how they make data flow a first class citizen. The ReactiveX group (http://reactivex.io/) has some wonderful documentation I wish I had a year ago. There’s also a insightful response to my post on Hacker New (https://news.ycombinator.com/item?id=10752742) that I recommend you read to get a more nuanced perspective of RxJS.

Addendum: 12/30/2015

There are now two more posts that go more in depth about how we use ClojureScript in our single page web app.

Appendix: ClojureScript resources

--

--