The
central problem in software development is managing complexity. This
complexity comes from the sea of details and decisions, large and small,
that need to converge into a stable, working system in order for it to
be successfully used for its intended purpose. That not only includes
the code, but also the analysis, management, documentation, packaging,
distribution, training, support, configuration and operations that
inevitably precede a user being able to utilize some functionality on a
computer.
Unchecked,
rampant complexity growth in any area can swamp a project, making it
impossible to continue substantial progress. About the only way I know
to contain complexity is by breaking it off into manageable chunks and
getting it encapsulated into well-defined sub-problems. This works at a
base level, but new problems emerge. Overlapping sub-problems create
redundancies which add significantly to the complexity. This can be
managed typing and categorizing the sub-problems carefully, so that
overlap is rare or negligible, which gets one past the first hurdle. But
as the number of sub-problems grows, their inherent complexity needs to
checked as well. At this upper level, they just form a slightly higher,
more abstract version of the overall problem, but all of the same
issues apply.
Thus,
at some point in order to manage the complexity another higher layer
becomes necessary and the same issues repeat over again. As the
complexity grows, the number of layers to manage it grows as well. Thus,
in any attempt to manage growing complexity, a steadily increasing
number of layers will need to be created. This is the inevitable
consequence of our existence, we can’t handle unlimited sized problems.
Smart people or well-running teams may have higher limits, but there is
always some limit, and it is always reached earlier than expected.
Layering
occurs in all aspects of software development, but for this post I’d
like to just focus on how it effects the code, data and operations of
software.
The
first and most obvious layer in software is the underlying code itself.
Programmers get presented with a number of domain or technical problems
for which they assemble a large number of instructions for a computer
to follow. It is well understood that failure to organize these
instructions results in ‘spaghetti code’. That is code with a logical
structure so intertwined it resembles the noodles in a plate of
spaghetti. Of course this is bad, because any attempt to alter the code
is hampered by a significant wall of complexity. From this, within the
context of coding, we can draw the conclusion that dis-organization
increases complexity.
As
the code base begins to grow, it becomes necessary to break it off into
more and more sub-problems to manage it. This layer is most often
referred to as system’s architecture. It introduces conceptual groups
like modules, sub-systems, components, etc. Each of these pieces
encapsulates a larger subset of the functionality, and the interactions
between them gets formalized. Although I’ve never seen it mentioned,
dis-organization at this level can produce a spaghetti architecture, one
that impedes both changes and overall stability. Experience coding in
the base layer does not easily translate into understanding the
organizational issues at this layer. They appear similar, but they are
fundamentally different problems.
Once
a system has grown out of its base components, it is often desirable to
widen its functionality to a larger group of developers. This creates
essentially a platform layer where the core abilities are exposed, and
controlled, allowing for the existing work to be leveraged for more
divergent purposes. Once again, failure to organize this layer results
in more pasta, and of course understanding the dynamics of the necessary
organization is not directly related to the layers below.
Continuing
upwards we get to the product or system layer. This layer generally
introduces a new set of problems because it is driven more often by the
desires or needs of non-technical people. Organization at this level
comes more as ‘paths’ that are followed serially or in parallel, but
they still need the same generalized principles of organization.
Experience at this level generally involves far more people and
negotiating skills, than technical ones. A product manager for a
commercial system for instance is balancing the technical issues against
the direction and current of the market place.
The
final layer that I’m going to discuss is the one that is completely
ignored by most technical people. For users to access the system, it has
to be running somewhere. In an effort to contain the complexity of
design and work most systems form vertical silos. That is they handle a
very specific problem and don't easily inter-operate with the other
systems being run in their environment. There are some interconnections,
but standards are weak because they interfere with individual goals or
profit margins. As such, most modern large organizations have a large
number of these silos. Like every other layer, redundancies cause
significant problems. Thus, at an operational level, all large
organizations need an organizational scheme to coordinate the building
or purchasing of these silos into a manageable affair. This drives
concepts such as the common enterprise software ‘categories’ like CRM
and CM, which implicitly contain the primitive building blocks of the
operational environment. Again, dis-organization at this level results
in an enterprise wide plate of pasta, which diminishes that usefulness
of the tools.
No
doubt there are many more layers, since they are dynamic and unlimited
in number. For any given layer, as it gets better organized we can say
it is ‘converging’. By this we mean that in some overall way, its base
complexity matches closely to the underlying ‘necessary’ complexity of
the layer. If it’s not managed then it diverges, generally as a result
of disorganization, over-engineering, over-simplification or any other
amplifier that isn’t fundamentally inherent to solving the problem.
It
should be noted that there is a subjective (human-based) component to
minimal complexity. That is, for reasons based on the way individuals
think, the lines between over, under and minimal complexity tend to move
around. One person’s perfect trade-offs can appear overly complex to
another person. Thus rather than a fixed point, a layer will generally
converge within a range, particularly if there are many people involved.
Thus
in software development, in order to manage complexity, we want to see
the overall system converge on a reasonable number of layers, while each
layer is converging on a reasonable system of organization. Development
is an ongoing progress, and thus so is converging. We simply need to
track all of these ‘paths towards organization’ and continually insure
that they are headed for the correct goal. At the bottom layer that is
accomplished by programmers pushing themselves for elegance, while at
the upper layers is it often architects or managers trying to place some
organizational constraints on the chaos below. Every layer needs to get
close in order to win.