The first law of thermodynamics states something like this:
**the total energy of the system plus the surroundings is constant**.
Or basically **energy is conserved**; it cannot be created or
destroyed, just moved around. This first law is an important
realization for chemists/physicists, and underpins the way they look
at problems.

I think there is a similar realization that can be made regarding the complexity of software systems, and that it can change the way you look at and design software. It would go something like this:

Matt's first law of software complexity:

**The underlying complexity of a given problem is constant**. It can be
hidden, but it does not go away.

Or:

**Complexity is conserved** by abstractions. In fact, apparent complexity
can be increased by abstractions, but the underlying complexity can
never be reduced.

Or:

Complex problems remain complex, now matter how much good design and
architecture you heap on top. Abstractions *hide* the
complexity. But this is a good thing! A good abstraction divides a problem
in two, putting part of the complexity on either side. A good
abstraction puts most of the complexity where it can be *solved by an
existing solution* to that subproblem.

But complex problems remain complex, and
abstractions
leak.
If you come up with a good design that lets you hide the complexity of
a problem, and then you *forget* about that complexity, rest assured
that it will eventually come back to bite you. Hide complexity, but
don't ignore it.

You may have easily implemented a huge, performant web application
because you used off-the-shelf components for presentation,
persistence, messaging, clustering and fail-over. You applied well
known abstractions like J2EE, RDBMSes, Web Services, MVC... the list
goes on. But abstractions leak. Clustering and fail-over is inherently
hard no matter what the vendors tell you, and the complexity often leaks
through under load. HTTP and TCP do a great job of getting bytes from
one place to another, until a ~~bug~~ feature
in the OS or router shows itself
(like MTU size or sockets closing on DHCP lease re-acquisition).

Use abstractions to move complexity to where it is more easily dealt with. But don't forget that it is still there.

Mathematicians know a great trick that can be used to sidestep my
first law of complexity: redefine the problem. The underlying
complexity of a given problem is constant, so **change the
problem**. This can be the single most useful outcome of a
requirements gathering exercise. Someone may come up and ask
you to solve this problem: "we need an enterprise-bus messaging
system". But if you dig a little bit, you may find out they would be
happy with occasional automated emails, a `.forward`

script
and a CGI.

Use abstractions to move complexity to where it is more easily dealt with. But remember complexity is conserved by abstractions; and abstractions leak.