In Born Together: Evolution to a Bundle, chapter 4 of the Wasteful Applicative Evolution series, we’ve modeled an application’s development process as an Event Stream. An Engineer performs the action of Change to an application, due to a Cause. First there is a Cause only afterwards a Change, so Change is a reaction to Cause.
In Avoidance: Eventualism & Evolution, chapter 3 of The Inevitable series, we’ve seen that the very same action of Change is the fuel that drives the four Wasteful Application Evolutions we’ve defined, towards a Monolith, a Bundle, Legacy and Refactor. Some of this Change is the one we engineers do to our own applications. Some of this Change is done by other engineers in other companies, for example those who continuously Change and evolve the packages we depend on or SaaS services we consume.
In the very same chapter, we’ve said that we can not stop ourselves from making a Change. Causes will never cease because we can not and should not stop the business from moving forward. Alas, this is exactly why we can also not stop the erosive Wasteful Application Evolutions. As such, we’ve concluded that all we can do is to delay or hasten them.
In this series we are going to give in to it and find a beneficial way to handle Change. Stopping Change is impossible and doing nothing about it is non-beneficial. It sounds like we are giving up to this Cause–Change cycle, which is a trait of our binary thinking minds. Instead, we’re going to focus on these Cause–Change cycles, and how they affect our applications. Through it, we will come up with methods to better design our applications, which will slow down some of the evolutionary processes.
Impact of Change
Let’s look at the world from an application’s point of view. Needless but important to recall that an application has no awareness of anything. A code has no mind of its own. But they do have a relationship with the Engineer, the one who makes a Change to the application. He does have a mind of his own. He makes not one Change, he makes a Stream of Changes.
From its point of view, it seems like a constant Stream of Changes is hitting the application’s surface. But it only seems like it because an application has many fronts. Internally, it’s physically divided into entities such as folders, files, classes, etc.
But when we make a Change, we apply it to a cluster of physical entities. A committed code is a Change to many physical entities, who are also virtually grouped into Modules by a criteria, one that should adhere to SOLID’s single-responsibility principle (each Module has only one reason to Change). In this series, we’ll also examine what makes a beneficial criteria.
In the following example, we’ll work with 5 different Modules. For simplicity, we can presume each Module does only one action to be consumed by 5 different and independent clients.
These Modules share things in common, such as physical code or business logic (whatever that is). In this series, we’ll investigate what these relationships are. For the time being, we’ll call them intersections.
Some of the Changes will hit Module A alone. Some of them will hit Module B alone. Some of them will hit the intersection between A and B and would affect both Modules. Some of them will hit the E and would affect both A, B, C and D because Module E is also an intersection. Let’s better define what these intersections have to do with Change.
In diagram (1) Modules A and B pretty much cover one another. The intersection’s size is large between the two. Meaning that any Change would most likely affect both. Whether with Intention to do so or without.
In diagram (3) A and B barely cover one another. The intersection’s size is small between the two. Meaning that any Change would most likely affect only one. If we have the Intention Change both, we most likely need to make a Change to A and then another Change to B.
In diagram (2) A and B cover one another, only somewhat. They have an intersection of fair size, whatever fair is. Meaning that some Changes to A would affect B and vice-versa. And if we have the Intention to Change both, we’d need only to make one Change only to the intersection.
That’s not news, that’s one of the basics of Software Engineering in theory. To see its results in practice, let’s add time to it.
Evolution of Change
Only the application at the top-right one is a dreadful Monolith which (1) evolves to because its Modules intersect and cover each other a lot. Almost any Change will have unforeseen and unintended consequences. And in case an Instability (bug) was discovered in Module A, it would be much harder to trace because it may not have been a Change done to A at all. Both reasons are exactly why Monoliths cause Inefficiencies.
[Note: throughout this series, we’ll be working only with a single application. Multiple applications will be dealt with in the follow up series Breaking Change]
At the bottom-right corner, is an application that even with our best efforts, you can not Change both A and D in a single Change. In the middle, what looks like an application with carefully planned intersections, and still Modules B and C can not be Changed together. The question of which one is better can only be answered by the Change’s Intentions.
Intention of Change
When we read another engineer’s code, most of what we see is the outcome. Mostly what we do is reverse-engineer the Intention, what was in our engineer’s mind and his reasoning. If we’re lucky, it’s documented somewhere. Could be in a comment somewhere in the code, in the git commit message or in the design schematics. His Intention itself is not in the code.
Applications have no mind so they can not parse or understand it. Only us living beings have that capability. At best the code might reflect an Intention, but it is not in it. Intention is a part of the Change an Engineer makes.
As such, Intentions are within the Change Stream. It is indeed a single Stream of Changes and so far we’ve visualized it only as a single tunnel. Let’s try to split it into several tunnels, each one with its own Intention.
Changes going through tunnel (I) would always have the Intention to affect Modules A, B and C together.
Changes going through tunnel (II) would always have the Intention to affect Modules D, F and E together.
Changes going through tunnel (III) would always have the Intention to affect Modules A and D.
Unlike in the left one (1), in the right diagram (2) we made sure there would be no intersection between Modules A and D. As such, Changes going through tunnel (I) would not have the potential to cause unintentional effects on Modules D,F and E. Similarly with tunnel (II) and the potential of unintentional effects on Modules A,B, and C.
Penalty of Change
To decide whether it was a good idea to remove the intersection between Modules A and D, we should examine the third tunnel in the Change Stream. Whenever a Change comes through it, it would require our Engineer to make two Changes instead of just one. Seems like a penalty of sorts. To answer if it’s worth it or not, we need to look not at a single Change but on multiple Changes coming through the Change Stream.
Let’s presume that a Change goes through tunnel (III) once a year while through the other tunnels Changes comes on an hourly basis. It is clear that removing the intersection between A and D would be beneficial. For a very small cumulative penalty, it would lead to avoid unintentional consequences, the very same Instabilities caused by a Change. Which results in big penalties when they occur.
Let’s presume vice-versa, that a Change goes through tunnel (III) on an hourly basis while the rest comes once a year. It is clear that the lack of intersection between A and D is non-beneficial because the accumulated penalty would be a waste of time.
There are plenty of scenarios in between the two above. One of them is where the Changes come through equally through all tunnels and also on an hourly basis. If that is the case, we’d need to ask ourselves how we’ve ended up with either diagram (1) or diagram (2).
So it seems it is a question of each tunnel’s independent frequency of Change because there is no penalty when there is no Change. It’s also a question of the relative frequency between the different tunnels. Both are properties of the Change Stream itself.
There also seems to be some correlation between our application’s intersections and the Change Stream’s tunnels. And as it is supposedly beyond our control, it is for our application to adhere to the Change Stream. The more our Modules and our intersections are disregarded, or out of sync with the Change Stream, the more potential our application carries to be non-beneficial. So maybe it should be the other way around. These tunnels should be the primary criteria to group our Modules and define our intersections, or at least be taken into account. To do so, we need to dig deeper into the Change Stream, which we’ll further do in the next chapter.