
The unintentional side-effects of a bad concurrency model - davidw
http://joearms.github.io/2016/01/26/The-Unintentional-Side-Effects-of-a-Bad-Concurrency-Model.html
======
Matthias247
After ten years in the automotive industry I can confirm that some
observations from the article also happens there: If you look at the system
architecture (parts, components, connections) of a car you can also very
certainly identify the companies organizational structure. You would also see
there that the architecture is optimized for solving the problem with least
amount of effort and redundancy. But nevertheless, working in such a gigantic
environment I also concluded that this still makes sense. You simply need a
well defined boundary up to which you and your team are responsible and a well
defined interface against which your are developing. In the electrical
engineering world these are often interfaces described in form of messages
that are defined on classical bus systems (e.g. CAN), which worked quite
decent. I worked a lot on the introduction of IP networking based interfaces,
which (besides allowing other network layers and higher speeds) also can be
used for describing interfaces between different software parts which might
still be deployed in the same hardware. This yielded the advantages that Joe
Armstrong described here: Different parties can work very independently, using
different technologies and you can compose the results to a complete system.

However I can't say that I would use the approach for everything, because I
comes with a certain overhead - in performance as well as in the development
process. E.g. to have a large number of developers (could easily be > 100) the
same understanding of an interface and develop against it you really need a
perfect static definition of the interface, for at least some parts a dynamic
description (state and sequence charts) and test tools that work against both
sides. The interface definition must be maintained by someone (probably
neither the groups that implement the server or the client side) and changes
will be much much slower than a change somewhere in a monolithic codebase (can
be month from the change of an interface description until all sides have
adapted the new version). I would also heavily recomment the use of some
interface definition language (anything from custom, protobuf, thrift,
swagger, etc.) and the use of a framework and code generations tools and
client and server side. Manually processing JSON or even worse writing bytes
manually to a socket won't work at scale.

------
jondubois
I think it's not really a ideal thing for client-side work and server side
work to be separated (this is more a talent constraint). The most efficient
organizations I have worked for are the ones that split themselves up into
feature teams instead of strict front-end and backend teams. Otherwise, you
end up with people waiting around for each other to make changes and that
forces them to context-switch between different tasks (and everyone loses
focus). Not to mention that the backend devs often dont get the interface 100%
perfect the first time and you end up having to iterate over those changes -
Which means more waiting around and more context switching (except if the
required changes are minor in which case they can pair together).

With feature teams, you give people ownership over a feature of the software
and they are responsible for bringing it to completion. The great advantage of
fullstack developers is that a single developer can work on a feature end-to-
end - It gets rid of communication problems, greatly speeds up development and
reduces the backend-frontend "blame game" factor that you often see in large
companies.

I like the way the article portrays people as concurrent processes but I think
there is a fundamental difference between people and processes which affects
things: People don't have perfect memory. When someone offloads a task to a
different person and starts working on some new tasks asynchronously, when
they come back to the previous task, they may not be able to resurrect all the
old thoughts that they had the last time they looked at the same code (not the
same level of depth). So they lose a bit of time getting back in the mindset.

~~~
justinhj
Some backends are fairly sophisticated and require extensive domain knowledge
from designing databases that can be scaled, caching systems, security issues
and so on.

On the client side you may need someone who is an expert in C#, in 3d
rendering and geometry and in audio. Finding someone great at both is going to
be very hard, and frankly not worth the benefits unless you have a very small
team.

That said I believe front and backend should work closely together. There is
no reason that the frontend should be blocked because the API design can be
figured out before the backend is written and the frontend can mock out the
missing features until they are available.

~~~
bitsai
"That said I believe front and backend should work closely together. There is
no reason that the frontend should be blocked because the API design can be
figured out before the backend is written and the frontend can mock out the
missing features until they are available."

Came here to say exactly this. At my current workplace, one of the very first
steps in any project that involves both FE and BE work is for FE and BE
engineers to get together and design the API. Once that's done, work can
proceed on both sides in a largely asynchronous and independent manner. And
one of the first things to be implemented on the BE is API endpoints with
multiple mocks that can be requested by the FE, so the FE can start
integrating with the endpoints as early as possible.

------
platz
> Large Erlang applications have a flat “bus like” structure. They are
> structured as independent parallel applications hanging off a common
> communication bus. This leads to architectures that are easy to understand
> and debug and collaborations which are easy to program.

How is it easier to debug a message-passing architecture than a monolith?
Observe the JS world is slowly moving away from using Pub/Sub for connecting
components together. Each individual piece is easy to understand, but the
chain of causality is very hard to determine with all those messages flying
around.

~~~
kasey_junk
Message passing vs monolith's are orthogonal. Being a monolith has to do with
deployment & life cycle boundaries. Message passing has to do with
communicating state transitions between multiple logical components, possibly
running at the same time (regardless of whether they are in a monolith or
not).

If you want to compare something with message-passing architectures a better
comparison would be to a shared register. Both are abstractions around
communicating a shared piece of state. The argument that debugging a message
passing abstraction is easier hinges on the idea that the message passing
coordination system decouples the sender and receivers state transitions from
each other, where as in a shared register the state can be viewed as the state
of both. In systems where there are more than a couple pieces of shared state
it becomes untenable to put every component that _can_ share state into the
precise stateful configuration that you need to debug an particular case.

It turns out in practice, that generally it is easier to break up monolithic
applications that are message based than it is to break up ones that are using
shared registers, but that is a side benefit of the abstraction not the real
difference between the 2.

I'm a huge advocate of log based architectures, which take message passing
architectures to another level. That is, message passing architectures are
good at limiting the things that _can_ happen, log based architectures do that
and add the benefit of capturing what _did_ happen.

~~~
kitd
> I'm a huge advocate of log based architectures, which take message passing
> architectures to another level. That is, message passing architectures are
> good at limiting the things that can happen, log based architectures do that
> and add the benefit of capturing what did happen.

AKA Event-based programming. Events are what _has_ happened. Components define
what events to respond to and how.

In practice you need a combination of events and messages (or 'commands').
Which one to use depends on NFRs like dependency, complexity, performance,
maintainability, etc.

~~~
kasey_junk
I don't know if there is a formal taxonomy of these things, but I tend break
down messaging passing architectures along the what/how axes.

So you can have events or commands being sent in your messaging based
architecture as the what.

Log based architectures really speak to the how. That is, the medium for
message exchange is a durable log.

But like you said, in the real world they tend to be hybrids across all these
lines.

------
lmm
The best projects I find are small libraries that mostly use other small
libraries that mostly... Less a flat bus than a pyramid.

Within-language interfaces can be incredibly rich, conveying a huge amount of
information. When you split into separate components you throw all that away,
and end up spending more time parsing and marshalling than actually doing your
business logic. The right data structure can make the problem look trivial,
and is far easier to understand than the behaviour of a system of loosely
coupled pieces.

~~~
it
Erlang doesn't require any programming effort to parse and marshal messages.
You just send them and receive them.

    
    
      Pid ! {hello, world}
    
      receive
         {hello, world} ->
            io:format("Received hello.~n", [])
      end

~~~
_yosefk
There's a lot of machine effort though, if the data structures are big. If
you're doing computer graphics or vision and you have to send images or 3D
assets around, it won't perform very well. I think Armstrong consistently
underestimates the cost of messaging compared to shared memory - or rather the
fact that systems where this cost is prohibitive exist, just as much as
systems where his approach works great exist.

------
dschiptsov
Living organisms as a whole of various subsystems around semi-independent
organs, which has been made out of tissues, which in turn made out of
specialized cells, etc is the right model.

Cells are agents communicating with messages over different channels (slow
blood vessels, and fast neural pathways).

There, at least according to theory of evolution, is no better way to
structure a complex systems.

And the cell machinery could be modeled as a pure-functional LISP (because
code and data uses exactly same sequential structure). This, presumably, was
the intuition behind MIT Scheme.

Pattern matching on messages in Erlang along with the "become" universal
server is beautiful way to express general agents and is close to that
uniformity, but code cannot be a message.

Procedures are enzimes, data structures are proteins, asynchronous messages-
passing is the way to communicate, and there are protocols to follow.

One cannot possibly design better than that.)

------
discreteevent
He seems to be describing what Alan Kay intended objects to be.

~~~
Jtsummers
[http://www.infoq.com/interviews/johnson-armstrong-
oop](http://www.infoq.com/interviews/johnson-armstrong-oop)

I swear I had another source, but yes. He has said that he views Erlang as OO
in the Alan Kay sense. Which, honestly, was my first thought when I tried
Erlang. Each process is, essentially, a concurrent closure, and closures (in
the functional language world) are the poor man's objects [1].

[1]
[http://c2.com/cgi/wiki?ClosuresAndObjectsAreEquivalent](http://c2.com/cgi/wiki?ClosuresAndObjectsAreEquivalent)

