DEV Community


Discussion on: Observables, Reactive Programming, and Regret

brucou profile image

I find it interesting, as you suggest, to separate the primitive from the operators. However, I don't see Rxjs size, at least in its latest versions, as being a problem. As operators can be imported separately, there is minimal impact bundle-wise. The impact seems to be more of a cognitive nature, with some folks maybe assuming that because there are 100 operators, it must be complicated, probably twice as complicated as if there were 50? This is like saying that npm is unusable because there are million of packages there.

I think at core there is both a teaching and learning problem. Beating up yourself, and reducing the number of operators to make the library artificially more palatable will not automatically solve those problems.

Teaching problem:

  • While you were busy developing, testing and using the library, folks have filled a void that existed with all sorts of explanations about what/why/where to use Rxjs. Those explanations have extremely varying quality, and are sometimes downright incorrect, At some point people produce observables tutorial that are like monad tutorials, i.e. most of the time confusing. By not having your own take at those questions, you did loose the control of the Rxjs narrative.
  • I was perplex when I saw that there were going to be a Rxjs conference. I am glad it was a success, but I did not understand why you need a conference around what is barely more a specific implementation of a pattern. Like, you would not do a tech conference just about lodash, or would you? The signal I received was that Rxjs was so big in the world that it became its own thing.
  • lodash, ramda and others have more methods than Rxjs ever will. Nobody is dissing lodash as being unncessarily complex. That is because they understand each function. If they give some input, they can mentalize what will the output be. This is harder for Rxjs operators because the functional relationship is between stateful objects (explained in the next paragraph).
  • The available documentation is plentiful (that is good) but for a long time (I don't know if that has been solved now, haven't looked at it in a year) it was incomplete. I explain:
    • most operators are well described in the 95% of their operation (which is taking some data, computing something and producing some other data).
    • what I found (back in the days) is not often or well described is the 5% left: i.e. errors and completion. That is fine when you deal with one observable, but quickly gets tricky when you deal with several. If f is your operator, and i_n the input observables for f, with o the output: o = f(i_1, ..., i_n). Because any i_n can be in three possible states, to describe (document) f accurately you have to explain what f does in 3*n cases (worse case). I remember blocking on withLatestFrom of old because one parameter observable had not emitted yet
    • it is actually worse if the f computation depends on timing or ordering of each source input... This is real complexity cropping in, and that is best done by drawings that by natural language (marble diagram are great but should describe more edge cases).
  • subjects are seriously under explained, and yet fairly important. So same problem with confusing, contradictory, poorly argumented advices from the community.
  • schedulers and concurrency should get more love too. Arguably you may remove schedulers from Rxjs and keep 90% of the use cases, but concurrency is not going to disappear from our applications. The problem that schedulers solve if taken out of Rxjs will have to be solved in some other ways.

Learning problem:

  • what I observed is that programmers have trouble understanding the operators when they do not understand the observable type in the first place. Writing an operator from scratch with learners from the primitive may help dispel the magic.
  • developers are pragmatic beasts and not everybody wants to be lectured about concepts. Some folks rather open a playground and play with stuff. Streams in that sense are hard to play with. You can't inspect them easily, debugging is unclear, and time consuming. My worse experience with debugging is not when an observable does not emit the expected, it is when it does not emit at all... So good tracing and debugging are mandatory here. They help debug but they help learn, and acquire.

Last thing, the readability issues that you mention comes from the fact that behaviors and events are not well introduced, yet they are a key part of understanding how to use Rxjs. That is more important and very linked to the hot/cold dichotomy (another confusing metaphor IMO). If you had separate types. you could document some operators as accepting only behaviors, like withLatestFrom (is is sample/audit now?), and save some programmers the debugging.

Alright there are so many more things (structure readable code and so on), but this is getting too long. In any case, while I appreciate the humility and capacity of self-reflection that you show , I would not want you to make the wrong diagnostic here. Observables are an abstraction. Some programmers will always balk at abstraction that they cannot get in 5mn. For the rest of them, I mostly see documentation as an issue. Rxjs design is quite good. But if you could make a better tracing and debugging story, you may not even need to do too much documentation-wise. You could just let people play with it and build their own intuition.

In summary, keep up the good work and don't get demoralized by the critics.