There are many patterns, principles, and development methodologies out there that are evangelized.
They can have fancy names, some are old, some are new. But they all boil down to just three “Unities”, which have nothing in particular to do with computing or development, and all to do with those that are doing development and design: humans.
You find those three Unities in practically all crafts.
Without further ado, here they are. The first formalization is attributed to Aristotle for classic drama:
- Unity of Action
- Unity of Place
- Unity of Time
They can be applied at any level, like fractals. They work from high-level design to low-level implementation details, whatever the level, as long as it is one where a human plays a direct role.
Here is how they are translated in terms of development.
Unity of Action
One function, one class, one whatever should have one purpose, perform one action, with minimal side-effects.
The rationale behind Unity of Action is that humans are not good at complex tasks, whenever “something” does, if that’s “many things”, the human will sooner or later lose track.
This is what the S and the D of SOLID are about for instance. It is what KISS is about. It is behind the various factory and plugin patterns, part of the command-query separation, part of the separation of concerns, Don’t Repeat Yourself… It is found in the C of MVC.
Unity of Place
Each function or class should be understandable, modifiable, and designable locally, without looking at anything else.
The rationale behind Unity of Place is that humans cannot look, read or think about multiple things at the same time. Not efficiently so anyway. So for code or design to be manageable by a human, a local point of view should be enough to understand one level or one particular aspect of the whole.
We have a short-term memory of a just a handful concepts, how much exactly is subject of debate, but it’s at most 7 “slots”. A simple function with 1 input, 1 computation and 1 output already eats 3 slots, leaving precious little spare capacity.
This is what the Law of Demeter is dancing around, what the O and I of SOLID try to achieve, it is the core of the Liskov Substitution principle. It is part of the command-query separation. It is also what plugins, Dependency Inversion and Inversion of Control are about. It’s also in the separation of concerns or DRY. It is in all letters of Model-View-Controller.
Unity of Time
Each function or class should have a sequential time flow, or be made of simple sequential time flows.
The rationale behind that one is that humans have trouble conceiving simultaneity, time relativity, and time non-linearity. In other words: our logical brains are sequential machines. Our intuitive thinking mechanism are not sequential, but they are unreliable, and often hard to translate into design or code.
It can be found behind the open/closed principle, DI, Inversion of Control, KISS, and is also the main reason why multi-threaded programming is hard, why asynchronous is harder than synchronous. It is arguably what the MVC approach was primarily designed to deal with.
It is also what low-level compiler optimizations are tackling for you, and what database engines abstract away. What NoSQL would-be-wheel-re-inventers stumble upon, what distributed MapReduce tries to tackle.
Pragmatic Solutions
While I attached some principles to particular Unities, many (if not most), principles try to tackle multiple Unities at a time. They do so with various degrees of success and various degrees of awareness of the problem they’re actually trying to tackle.
Interestingly enough, all the programming principles and frameworks solve it in way that would make Sun Tzu proud:
- Divide and Conquer. You just break down a problem until it becomes an assembly of simple pieces that comply with all three Unities.
- Massive Application of Overwhelming Force. Layers that take care of a whole aspect for you. A compiler and CPU handling the nitty-gritty details of super-scalar code execution, an ORM taking care of the data management complexity, a GUI that streamlines a vast amount of asynchronous activity into palatable events, a global network that allows you to open communication channels, etc.
The major engineering risk there being that of recipes. Methodologies and principles become recipes, a list of things to do, and the human element loses sight of the goal.
It’s not just the how, but also the why.
Beyond the Unities
To wrap up where this article started, Unities are derived from humans limitations.
In programming just like in arts and crafts, those that can go beyond the Unities will stand above the crowd.
Great drama, great movies, great music, great architecture are often ones that break the Unities and achieves something “new” that will dazzle. A successful movie with lots of flashbacks. A literary epic with many subplots. A superb framework. A very complex piece of computational code.
Just keep in mind that for every success at breaking the Unities, it takes at least a trait of genius, a super-human of sorts if you will. Most attempts at breaking the Unities will just fail miserably 🙂
Oh, and let’s not overlook the new elephant in the room. AI’s limitations for coding have no long term requirement to be derived from human limitation. AI generated code has no long term requirement to be constrained by the Unities…
I like it when concepts from such different origins are connected by common points, as you did in this text.
Often, it becomes not only an interesting read but also a very effective way to understand and memorize useful principles.
Also, I misread ‘Oh, and let’s not overlook the new elephant in the room’ as ‘Oh, and let’s not *overclock* the new elephant in the room,’ which gave me chuckles…
Thank you very much.