
Deprecating the Observer Pattern (2010) [pdf] - tosh
https://infoscience.epfl.ch/record/148043/files/DeprecatingObserversTR2010.pdf
======
skohan
In my experience the Reactive paradigm rarely serves to solve any of the
problems it sets out to, and the root of the problem is here:

> The first step to simplify event logic in an application is to come up with
> a general event interface so that all event handling code can work with a
> uniform interface.

RX proponents seem to make the leap that the problems of software complexity
can be solved by wrapping your logic into a highly general abstraction layer
which homogenizes everything your code does into streams of events. I'm not
entirely sure what this assumption is based on.

To look at how this assumption breaks down, just look at the ReactiveX API:
what's billed as a general purpose tool consists of a menagerie of observable
subtypes and operators built to patch the holes in real-world problem
complexity left by the "general case" solution described by this article. Or
look at the serpintine call stack in any real-world RX project of any non-
trivial complexity.

The more I code, the more convinced I am that the best practice is to write
software at the lowest level of abstraction possible, as close to your problem
domain as possible. Paradigms like RX which seek to magically reduce
complexity through generic abstraction at best move that complexity into
another layer where it's more obfuscated, and at worst introduce additional
complexity.

~~~
bunderbunder
I was pretty enamored of the reactive paradigm until I inherited my first big
RX project. Whoof. The amount of stuff I needed to know in order to maintain
it was just intense.

Nobody else liked it, either, so we slowly, over the course of a few months,
replaced the reactive bits with more ad-hoc code. The end result was smaller,
easier to read and modify, and more performant.

~~~
hinkley
A failure mode that has become apparent to me as a downside of having too much
team stability: it is very easy to design a system that can only be reasoned
about once it has been _memorized_. You need new people to serve as a mirror
to really understand the health of the system.

There’s a term from compilers that some people have borrowed to describe
thinking about code: Local Reasoning.

Lots of things can kill local reasoning. Global variables, overuse of
delegation, mutation of someone else’s objects, and aggressively event-based
systems.

~~~
jhayward
> _aggressively event-based systems_

I'd be interested in reading about your observations on how event-based
systems kill local reasoning.

Is it simply a bad design that requires the publisher to understand who/what
all the subscribers are doing with an event?

I've found that designing the semantics of individual events, and constraints
on their interpretation, is the hardest part of event based systems so I could
see that becoming an issue.

~~~
hinkley
Simple event systems often aren't too bad. There can be problems with order of
receipt (what order do events fired from events get observed?). and side
effects, and it can be difficult to teach junior devs how to dig themselves
out of a problem.

But when the system is using events as message passing mechanism between
modules, things eventually tip over into Bedlam. You have events that
disappear, or broken events that you can't track back to the source (where is
this shit coming from??).

The worst problem, the sucking wound I've seen happen a number of times, is
when an event starts repeating. Somehow you make an event that causes itself
to fire. In a loop, forever until too many pile up and the app stops
responding.

------
danesparza
If the whole point of this article is to illustrate a new way that reduces
cognitive load to the programmer they have utterly failed.

Observers have staying power because they are easily understood with current
common programming language syntax.

To truly replace the observer pattern, we'll need to come up with a simple way
to describe a better pattern. I'm unconvinced that RX is the better solution
based on this article.

~~~
FLUX-YOU
They're not easily understood though. The upfront knowledge cost is high and
they can be difficult to debug with the zoo of things from libraries like rxjs

------
aaaaaaaaaab
RX looks good on paper, but boy, is it a nightmare in any non-trivial project.

Is this stream hot or cold? Will it fire the request every time I subscribe,
or at most once? On which thread is this stream delivered on? UI thread? Let’s
be safe and dispatch it to the UI thread! Oh, now my UI flickers because this
other stream is producing this event one cycle too early... Let’s add one
millisec delay here and hope it fixes the issue (yuck.). How do I cancel this
stuff?

~~~
mercutio2
I’m in general agreement that Rx is, in practice, not an improvement over most
other approaches.

But:

> How do I cancel this stuff

Everyone I’ve ever heard describe Reactive pipelines describes built-in,
standardized cancel behavior as the raison d'être for Rx!

It’s super common to encounter a one off someone thought would never get
called more than once in a while, which actually piles up a huge serial queue
(or worse, creates dozens of threads while concurrently contending for a
resource that’s serialized in a less than perfectly transparent way).

So in those cases, knowing all your interfaces explicitly include a
cancellation token is pretty appealing.

Of course, in practice, lot’s of people don’t bother to wire together the
cancellation bits, so you’re left thinking you can cancel things but it
doesn’t actually do anything. So maybe that’s what you meant...

~~~
aaaaaaaaaab
Not sure about the cancellation token bit. Last time I’ve used Rx my
experience was that you could never know whether disposing a subscription
would cancel the underlying process or not. Many times I wanted the opposite
behaviour to what the original author had implemented. Not to mention cases
where I wanted to keep a process alive as long as there’s at least one
subscriber, but cancel it when there’s none left...

Maybe things have changed since then (2016-2017) with regards to cancellation;
but I’m off the Rx train for a while.

------
acoye
Something I find mildly amusing; To implement a RX framework in a language
like Swift, one should heavily use observing.

~~~
pixelrevision
[http://reactivex.io/](http://reactivex.io/) "The Observer pattern done right"
:)

------
tabtab
In my opinion, most OOP languages are not powerful to implement "events"
right. The work-arounds often mix up conceptual associations with
implementation. For example, if I want to associate an "onClick" event snippet
with a given button, how that snippet fits into the UI implementation guts
shouldn't normally be the app-coder's concern. Most OOP languages don't have
enough power to wire up the conceptual association independent of the
implementation associations, and the work-arounds are ugly compromises. As an
app coder, I want something like this:

    
    
      define buttonX inherits button {
        buttonAttribute1...
        buttonAttribute2...
        buttonAttributeEtc...
        method onClick() {
          doSomethingWhenButtonPressed(...);
        }
      } // end define
    

I don't want to have to "register" the event with an observer or whatnot;
that's low-level grunt work the framework should normally take care of.

Doing it "right" would require something akin to relational modelling, but
there's no current language/IDE that embeds source code into RDMBS well. It's
an area ripe for research. Large OOP frameworks don't seem to scale multi-
associations well and I find myself looking at RDBMS.

------
romaniv
IMO, most commonplace programs can make do with a non-blocking list of last X
events. Producers add to the end of the list. Consumers have references to
beginning of the list, which are advanced when events are processed. When all
references are above an entry, it gets unlinked. You can put abstractions on
top of this, but most simple programs wouldn't need to.

~~~
itsdrewmiller
Great article on this pattern:

[https://engineering.linkedin.com/distributed-systems/log-
wha...](https://engineering.linkedin.com/distributed-systems/log-what-every-
software-engineer-should-know-about-real-time-datas-unifying)

------
MarkMc
Author: Here is a simple example of how to draw a path based on mouse events -
you are probably familiar with the observer pattern, making the example easy
to understand. Now read the following 17 pages to see why it's wrong.

------
tr1et
What is the differences between this Scala.React and ReactiveX? ReactiveX born
in 2010 too, and is mentioned briefly in Related Works.

