Process and Stateness are they object?

Old quote:

  1. Ten Pounds in a Five Pound Sack - Representation Is the Essence of Programming

Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won’t usually need your flowcharts, they’ll be obvious.

Frederick Brooks - Mythical Man Month (1975)

Even today a lot Frederick Brook’s insights still apply - but when it comes to this particular point I personally believe that it has become obsolete. By focusing primarily on representation you’ll always end up with a CRUD system.

These days I tend to side with Sam Newman:

These capabilities may require the interchange of information — shared models — but I have seen too often that thinking about data leads to anemic, CRUD-based (create, read, update, delete) services. So ask first “What does this context do?”, and then “So what data does it need to do that?”

Sam Newman, Building Microservices (2015)

Ultimately the application is supposed support use-cases that will enable the users to meet some set of concrete goals and it’s the use-cases that help identify what data is necessary to achieve these goals - so really what the application is able to do should be the primary concern, the data that enables the “doing” is a necessary but secondary concern. So it makes sense to create some of the logical boundaries around the system’s activities (runtime berhavior) rather than the just focusing on neatly partitioning the data for persistent storage.

Actors and objects have one thing in common - they need to collaborate. However the means of that collaboration is quite different - message passing vs. method invocation. The motivation behind the encapsulation of state are quite different as well.

An actor doesn’t share state so it can maintain run-time autonomy and isolation (a byproduct of which is that there is no need for synchronization) - if it dies it doesn’t take the “rest of the world” with it. OOD heuristics on the other hand focus primarily on design-time autonomy and isolation. OO is more focused on segregating commonalities from variabilities and hiding implementation details that may be subject to change during the entire lifecycle of the application. When OO was conceived computers were still mostly uniprocessors and OO never really concerned itself with runtime isolation as fault tolerance and distributed processing were never stated design goals.

Both actors and objects are essentially “black boxes” that expose an (message or method-based) interface but the criteria that lead to their “optimal boundaries” are quite different. Now that doesn’t mean that the essence behind some of the OO design principles can’t, after some careful re-interpretation, apply to actor-based systems - but thats a separate topic.

To spawn, or not to spawn? is quite specific:

  • Use functions and modules to separate thought concerns.
  • Use processes to separate runtime concerns.
  • Do not use processes (not even agents) to separate thought concerns.

Now initially I would have been tempted to use “design concerns” instead of “thought concerns” - but that would have been a grave mistake because in Erlang/Elixir “runtime concerns” are also “design concerns” - less so because of performance but moreso because of fault-tolerance. So my interpretation of “thought concerns” is the typical compartmentalization of functionality that we engage in to make things “easier to reason about” while simultaneously structuring the application to align itself with the user’s domain. So deck, hand, round align with the domain concepts while round_server and notifier are primarily concerned with orchestrating how the rounds unfold over time.

Now it would be a mistake to try to compare the “size” of processes and objects - some processes can be large, i.e. hold complex state that would typically be divided over a large number of objects but there are also processes that are extremely short-lived and trivial (e.g. completing a (blocking) “call” for the parent process which can’t afford to be blocked).

OO design often obsesses with reuse - ignoring YAGNI in the process. Lately a new criterion for choosing boundaries seems to have emerged - impermanence and replaceability. Actually, it’s not all that new:

Program to an interface, not an implementation.

p.18 Design Patterns: Elements of Reusable Object-Oriented Software (1994)

Now typically this is seen as a means to break the coupling to a concrete implementation but it also has the effect that you can “throw away” the current implementation and replace it with an entirely new (rewritten) one.

So recently some teams have been placing more emphasis on composing systems from parts which are “small enough” to be rewritten in one day to two weeks (depending on the circumstances). If any one part gets “large enough” to become “too large or difficult to replace” it is refactored towards a smaller implementation or broken down into an arrangement of more replaceable parts. So “replaceability” could be another aspect that can potentially help to find better boundaries for something as small as a process or something a large as an OTP application.

9 Likes