The previous video identified the 2 primary ingredients to a codebase architecture as a “well-structured containment model” and a set of "defined rules". In this video I drill down into what is meant by a “well-structured containment model”
First question, “why do we need containment at all?”
Well containment is a form of abstraction that helps us make sense when the amount of details is overwhelming,
This is something we use all the time to manage complexity…
To divide a big problem into manageable smaller problems…
…we group lines of code into functions, functions into classes or files, into folders, subsystems, etc.
We nest containers as deeply as we need to
So that there isn’t too much stuff in any one place
As a codebase grows, containers get Fat, and we simply divide them some more
When we divide a set of dependent items into containers, we create dependencies between the containers
Here’s a simple example… when we place the blue, green and red items into their own containers, the dependent items create dependencies between the corresponding containers.
We can organize a given set of dependent files in many different ways.
Placing items in different containers
Can create very different containment dependencies. Here the same set of items are grouped in 2 different ways, with one containment structure being much less complex than the other.
The way that we organize items into containers fundamentally defines the nature of the containment structure.
It is dependencies that make a codebase complex. Roger Sessions gives another example of 2 different organizations of the same codebase in his recent article “The Thirteen Laws of Highly Complex IT Systems”
He makes the case that dependencies are the primary driver of complexity
More dependencies means more complexity, which has an undesirable effect on many attributes of an IT system, such as
Alignment with business needs
Efficient use of cloud resources
Ease of maintenance
Scalability, and so forth
John Lakos talks about dependency (and thereby complexity) being cumulative. His Cumulative Component Dependency measure recognizes that when one item depends on a second, it really depends on all the items that the second item depends upon. For instance an item can be impacted when anything in its dependency closure changes. The CCD of a system is the sum of the dependencies of every item in the system. It is a very good indicator of the relative complexity of systems. In this simple example
the items on the bottom row are considered to be dependent only on themselves;
the items on the second row are each dependent on themselves plus 2 items on the bottom row;
the top item is dependent on itself plus the 2 sets of 3;
so the CCD for this system is 17.
The overall number of dependencies impacts the CCD somewhat, but when groups of dependencies create cycles between items, the effect on CCD is explosive.
For example the dependencies for this set of items all point downward, and its CCD
is 164 or less.
By adding a single dependency from the item on the bottom layer to the item at the top, we introduce cycles.
depended on just a few other items in the acyclic system,
it now depends on every other item in the system! And every item likewise depends on every other, so the CCD
has rocketed to over 1,200! In this case we know which is the disruptive dependency because we added it to the acyclic structure on the left, and highlighted it on the diagram on the right, but cyclic dependencies in reality can make a system an order of magnitude harder to understand and maintain.
So to summarize… we’re going to say that a “well-structured containment model” is
enough that no containers contain too many child items,
and the dependencies between containers are acyclic. A containment model with these simple characteristics will give us
a good foundation for the definition of an architecture along with all the associated benefits. We’ll come to architectural definition later on, but next we’ll take a look at how we might create this kind of containment model for an existing codebase…