On Architecture: From Small to Gargantuan
This article is provided courtesy of" IEEE Software Magazine.
In their innovative book A Pattern Language, Christopher Alexander and his colleagues describe the patterns that breathe life into civil architectures, from intimate spaces to buildings to entire towns. No matter where you look along this spectrum of size, you'll find common structures and common behaviors. At each point in the spectrum, the patterns therein form the architecture of each instance.
Across the software landscape, you'll find a similar spread, from small programs to systems to systems of systems. Each instance has an architecture, be it intentional or accidental, and the forces that work upon each system—from small to gargantuan—vary accordingly.
Software development is ultimately an engineering activity, whose primary activity is to deliver executable artifacts in a manner that balances the forces on that system. The forces that swirl around a software-intensive system include not only its purely functional requirements but also a multitude of nonfunctional ones, such as reliability, portability, and scalability (often called a system's -ilities). Each of these forces is resolved systemically, meaning that no single part of a system can be responsible for responding to a force. Rather, the system as a whole, with its parts working in an architected union, must confront these forces. Indeed, the fact that these forces are dynamic and might change radically over a system's lifetime contributes to making software-intensive systems so complex.
Five forces at work
For these kinds of systems, we can group relevant forces into five basic categories: business, environment, development, operations, and values.
Business forces include cost and schedule. Obviously, it takes time, materials, and people to build and run a system, and the availability of each dictates what can and cannot be built. Ideally, these forces should press in just enough to motivate a maniacal focus on what's really important but not so much that the organization is always starving for resources (and not so little that the organization is sloppy and inefficient).
No interesting software-intensive system lives in isolation; it's shaped by the various forces in its environment. Resources such as computational power, electrical power, physical space, and personnel are limiting factors for some. For most embedded systems, these are unyielding constraints; in contrast, many enterprise systems thirst for more of every such resource. Compatibility with external elements might also warp a system: process and technical standards; demands for system interoperability; economic, historical, and political factors that stipulate using legacy parts; and the necessity for internationalization each represent some dimension of compatibility. A system's complexity also plays a critical role, perhaps the most important one. All interesting software-intensive systems are intrinsically complex, some more so than others. Sometimes, a system will push various software limits, rendering it far more difficult to build, run, and grow.
A system's development encompasses the forces that act during its creation, deployment, and evolution, including
- Feasibility. Is the system novel and does it require technical breakthroughs to build it?
- Manageability. A system whose sole user is also the sole developer is a wee bit easier to manage than one that spans countries, languages, and political systems.
- Accountability (also known as traceability). If you jiggle one branch of a system, can you predict what distant leaves will shake?
Demands for secrecy can also impact development: a system classified for national-security reasons creates physical and social barriers that don't exist in open systems.
The need for resilience is also a critical development force. Resilience encompasses
- Supportability. Is it an open source system or closed and tightly controlled (such as a system in deep space)?
- Maintainability. Can we repair the system easily?
- Adaptability. Can we reconfigure the system to accommodate an unanticipated use?
- Scalability. Can we grow the system to accommodate greater capacity or throughput?
- Portability. Can we move the system from one physical platform to another?
- Extensibility. Can we add a new behavior to the system?
- Churn. Can we modify the system to accommodate new technology?
Whereas developmental forces act during a system's development and growth, operational forces derive from the stakeholders who use the system. Functionality is the most important operational force: a system with hundreds of use cases from many different stakeholders' perspectives is far more complex than a system with only a few use cases and one class of users. As such, functionality involves a system's behavior and usability as seen through the eyes of each set of stakeholders. Performance and quality represent nonfunctional demands on a system. We can measure performance in terms of a system's required typical as well as peak throughput, capacity, and responsiveness. Quality demands such as precision and accuracy will also vary from system to system and in turn shape each system uniquely. A system's dependability reflects the degree to which its stakeholders have confidence in its operation. Each system will have different requirements for safety, reliability, availability, security, survivability, and degradability.
Software-intensive systems that directly or indirectly impinge on environmental, geopolitical, and human values will naturally be subject to more controls and scrutiny. Sometimes, legal issues dictate what we can and can't build: privacy laws; laws for medicine, commerce, and transportation; and intellectual property rights including patents and copyright laws can shape a system. Additionally, ethical and moral factors may weigh in. Building a given system might be technically possible, but a community's values can keep it from happening.
Three example systems
Let's consider some of the forces that weigh on three different software-intensive systems of varying size.
In my first column, I mentioned the Computer History Museum's efforts to preserve the source code and surrounding artifacts of classic, seminal pieces of software. The Museum has a software curator—the only such position I've ever seen in a museum—as well as a vibrant software collections committee. Through a connection with Tim O'Reilly and then with Bill Adkinson and Andy Herzfeld, the committee secured the original source code for Apple's MacPaint. MacPaint wasn't the first graphical image program, but it was the first widely available one, and its presence was important in the personal computer's launch and in the start of digital graphics. MacPaint was written in Object Pascal and consists of only a few thousand lines of code. Architecturally, it's a beautifully simple and elegant object-oriented program. The forces of size and speed (given that the original Mac and all PCs at the time were computationally constrained) and usability (given the general audience) governed the shape of this application.
Jump ahead a few years to the present, and you have Adobe Photoshop, the dominant 2D digital graphics program. (Maya holds the dominant position in 3D digital graphics.) Photoshop is a much larger program, consisting of several million lines of C and C++; it's also a far more capable program, able to conduct complex manipulations on very large images. Usability (for a range of artist skills), speed and capacity (it must be able to handle large images with rich properties and multiple layers of various types), compatibility (with several image format standards), and extensibility (via its plug-in mechanism) shape Photoshop's architecture.
Consider next the system of systems used in producing a contemporary animated motion picture. Artists commonly produce images with Photoshop and Maya and then apply various special effects tools (such as motion capture and lip synchronization programs, mob agents, and plug-ins for hair, clothing, water, fire, air, and particle simulations). Add to that a complex rendering pipeline, and you've got a complex system involving perhaps thousands of machines and hundreds of users. Capacity, speed, reliability, repeatability, and adaptability dominate these architectures.
One person's system is another person's subsystem. Each system has an architecture that serves to hold back the forces weighing in on that system. At each level of system, different kinds of forces dominate, thus lending themselves to different architectural styles.
Grady Booch is an IBM Fellow. He's one of the Unified Modeling Language's original authors. He also developed the Booch method of software development, which he presents in Object Oriented Analysis and Design. Contact him at firstname.lastname@example.org.