Boundary
Much of what we do when building systems and designing software is, in fact, defining boundaries. Functions need clear boundaries with other functions, modules with other modules, services with other services, applications with other applications. Clear boundaries make our code logically rigorous and well organized. Inside a boundary, the interior is a black box to the outside world: all exchanges, material and immaterial, can occur only at the boundary through known interfaces; at the same time, impure data from the outside world is validated, filtered, and transformed here into pure data that fits the way the internal world operates.
In the prehistoric era of computer software, software resembled a prokaryote: utterly simple—just lines of machine code piled onto punched paper. Later, the assembly era introduced the concept of the subroutine/procedure, which further evolved into the function. A function’s boundary is its signature; a function’s signature consists of its name, input parameters and their types, and return value and its type. External callers need not understand the implementation; they execute the function’s code by calling it. Functions are like cells in an organism.

As software grew more complex, groups of functions were organized into classes or modules to accomplish a particular functionality; from then on, the concept of scope became deeply ingrained. Some functions serve as external interfaces and are public; others perform internal work and are private. The boundary of a class or module consists of its file name, class name (module name), and public methods. Classes or modules are like tissues in an organism.

Further up, several classes/modules aggregate to provide a particular service, forming a component. A component is often embodied as a directory (or subdirectory) with its own abstract interface (start / stop / process_message / …). A component’s boundary consists of the directory it occupies, its abstract interface, and its public classes/modules. Components are like organs in an organism.

One thing organisms and software share is that they too reject a single point of failure. The image above shows the human kidney—a classic active-active cluster setup. One reason those who sold a kidney to buy an iPhone 4 or iPhone 6 Plus are still alive and kicking is that even if one kidney is gone, the other takes on all the traffic, keeping the circulation of bodily fluids unobstructed (though its maximum load capacity is of course reduced).
Like LEGO bricks, we can further aggregate several components to achieve a particular purpose, forming an application or subsystem. An application is embodied as a project made up of several directories. Its boundary consists of its name, the resources it uses (such as ports), and the application’s entry point (such as the main function). An application is like a system in an organism—for example, the circulatory system shown below:

Further up, several applications form a solution that safeguards a particular business objective. A solution is like a complete organism; its boundary can be as large as a complex business blueprint or as small as a utility app on iOS. By analogy with a complete organism, when we build a solution, we must define its subsystems—which of them should be:
- dummy system — like our respiratory system, it works rain or shine, operating automatically even while we sleep. Examples that come to mind include logging systems, monitoring systems, and error-reporting systems.
- intelligent system — like our immune system, it responds according to external changes. Such systems are generally used in behavior-based reaction systems, such as firewalls and dynamic user classification.
- C&C system — command and control, like our brain, issuing orders and controlling the behavior of the whole organism. The “S” in SDN, which was all the rage a few years ago, is exactly this thing.
Let us review the boundaries we have just discussed and their impact on architecture and design:

All the boundaries discussed so far are merely those we design during development. Boundaries at runtime differ somewhat:

As we can see, even if we organize code into components or services, the operating system—or the runtime (VM) of most software—does not provide us with a similar organizing mechanism. So even when we separate components clearly within a project, at runtime they are all mixed together. In most cases, we can only organize them ourselves based on experience. nginx, for example, uses multiple processes to delineate the control plane and data plane—a classic approach.
The Erlang VM has a good design that solves the problem of component independence at runtime, giving them clear boundaries even while running. This is its concept of an application.

In this diagram, a process in the OS sense consists at runtime of several applications (components). Each application has its own supervision tree and its own heap, and can be started, stopped, restarted, or even upgraded independently.