Embodied in Its Components
The fundamental organization of a system isn’t easy to change. The implied decisions regarding technology, communication, structure, and so on become embodied in the components that conform to that organization. After all, it’s the implementation of this organization in the components that gives the whole system form.
It is all too easy to underestimate how deep these decisions run. As desktop computing gave way to mobile- and cloud-based solutions, companies with deep investments in desktop code looked for options to bring their massive investments in these code bases into these new forms.
Initially, that challenge might have looked like a porting problem. Maybe the code needed to run on a new CPU architecture or adapt to a different operating system. Those changes are not necessarily easy to make, but they’re not impossible. Furthermore, many of these code bases had already been through at least one similar port in the past, such as from Windows to macOS.
But the fundamental organization of most desktop applications includes much more than just a CPU instruction set or even an operating system. For example, most of these desktop applications are organized around the assumption that they have access to a fast, reliable, local disk. This point is so foundational that many desktop application architects wouldn’t have even called it out. There was no other option to consider; it could go unstated.
As a result, in many of these applications, the assumption of fast and reliable access to a disk is embodied not just in every component but in every line of code in those components. Need to read some configuration data? User preferences? Save progress? No problem! A couple of calls to the filesystem API, problem solved.
Moving this data to the cloud breaks this assumption, and every line of code that depends on it. The data might exist, but retrieving it might be slow (because it’s over the network) and unreliable (because it’s over the network). Or it might be impossible right now (because the network is down), although later it will be possible again.
Figure 1.2 illustrates how these assumptions become embodied in a system’s components. On the left, components in a desktop application connect directly and independently to a file system; they assume and depend on fast and immediate access to their data.
Figure 1.2 The fundamental organization of a system is embodied in its components. On the left, the organization of the system around a file system is embodied in every component. On the right, that’s been shifted to a cache that mediates access to data.
Components can be written to deal with this uncertainty, but they must embody a different fundamental organization. They must be aware that data has a source that may be slow to access or even inaccessible. As a result, they tend to be organized around a local cache instead, and a good deal of time and attention go into managing how data moves between the cache and storage.
On the right side of Figure 1.2, an alternative organization binds components to a cache that, in turn, mediates between local and cloud storage. In this architecture, components also embody the assumption that their data may or may not be in the cache. When a cache miss occurs, accessing their data will be either slow (it’s retrieved over the network) or impossible (if the network is down).
Organizing around a filesystem abstraction doesn’t really help resolve this problem. It’s not too hard to build a filesystem abstraction that can bridge the differences between different desktop operating systems, or even between mobile and desktop operating systems. But it’s the wrong abstraction for data in the cloud because it continues to assume fast, local access. These foundational assumptions can be just as easily embodied in interfaces and abstractions as code.
To be clear, how an architecture’s foundational organization is embodied in its components is about much more than storage and file systems. This is one example of how an architecture lays down assumptions, and those assumptions can become wired into each line of code in each component. We’ll return to this point more than once, as it’s a key aspect of what makes architecture valuable—and difficult.