Architecture
Once you have a sense of what your design must do (in the form of requirements or other less formal specifications), the next big problem is one of organization. How will you order all of the different data, algorithms, and control implied by your requirements? With a small program of a few hundred lines, you can get away without much organization, but as programs scale, they quickly become impossible to manage alone, let alone with multiple developers. Much of this challenge occurs because requirements change , and every time they do, code has to change to accommodate. The more code there is and the more entangled it is, the harder it is to change and more likely you are to break things.
This is where architecture comes in. Architecture is a way of organizing code, just like building architecture is a way of organizing space. The idea of software architecture has at its foundation a principle of information hiding : the less a part of a program knows about other parts of a program, the easier it is to change. The most popular information hiding strategy is encapsulation : this is the idea of designing self-contained abstractions with well-defined interfaces that separate different concerns in a program. Programming languages offer encapsulation support through things like functions and classes , which encapsulate data and functionality together. Another programming language encapsulation method is scoping , which hides variables and other names from other parts of program outside a scope. All of these strategies attempt to encourage developers to maximize information hiding and separation of concerns. If you get your encapsulation right, you should be able to easily make changes to a program’s behavior without having to change everything about its implementation.
When encapsulation strategies fail, one can end up with what some affectionately call a “ball of mud” architecture or “spaghetti code”. Ball of mud architectures have no apparent organization, which makes it difficult to comprehend how parts of its implementation interact. A more precise concept that can help explain this disorder is cross-cutting concerns , which are things like features and functionality that span multiple different components of a system, or even an entire system. There is some evidence that cross-cutting concerns can lead to difficulties in program comprehension and long-term design degradation 17 17 Robert J. Walker, Shreya Rawal, and Jonathan Sillito (2012). Do crosscutting concerns cause modularity problems?. ACM SIGSOFT Foundations of Software Engineering (FSE).
Neil A. Ernst, Stephany Bellomo, Ipek Ozkaya, Robert L. Nord, and Ian Gorton (2015). Measure it? Manage it? Ignore it? Software practitioners and technical debt. ACM Joint European Software Engineering Conference and Symposium on the Foundations of Software Engineering (ESEC/FSE).
Ravi Khadka, Belfrit V. Batlajery, Amir M. Saeidi, Slinger Jansen, and Jurriaan Hage (2014). How do professionals perceive legacy systems and software modernization?. ACM/IEEE International Conference on Software Engineering.
The preventative solution to this problems is to try to design architecture up front, mitigating the various risks that come from cross-cutting concerns (defects, low modifiability, etc.) 7 7 George Fairbanks (2010). Just enough software architecture: a risk-driven approach. Marshall & Brainerd.
Marian Petre (2013). UML in practice. ACM/IEEE International Conference on Software Engineering.
Emad Aghajani, Csaba Nagy, Mario Linares-Vásquez, Laura Moreno, Gabriele Bavota, Michele Lanza, David C. Shepherd (2020). Software documentation: the practitioners' perspective. ACM/IEEE International Conference on Software Engineering.
More recent developers have investigated ideas of architectural styles , which are patterns of interactions and information exchange between encapsulated components. Some common architectural styles include:
- Client/server , in which data is transacted in response to requests. This is the basis of the Internet and cloud computing 5 5
Jürgen Cito, Philipp Leitner, Thomas Fritz, and Harald C. Gall (2015). The making of cloud applications: an empirical study on software development for the cloud. ACM Joint European Software Engineering Conference and Symposium on the Foundations of Software Engineering (ESEC/FSE).
. - Pipe and filter , in which data is passed from component to component, and transformed and filtered along the way. Command lines, compilers, and machine learned programs are examples of pipe and filter architectures.
- Model-view-controller (MVC) , in which data is separated from views of the data and from manipulations of data. Nearly all user interface toolkits use MVC, including popular modern frameworks such as React.
- Peer to peer (P2P) , in which components transact data through a distributed standard interface. Examples include Bitcoin, Spotify, and Gnutella.
- Event-driven , in which some components “broadcast” events and others “subscribe” to notifications of these events. Examples include most model-view-controller-based user interface frameworks, which have models broadcast change events to subscribers. For example, views may subscribe to models so they may update themselves to render new model state each time it changes.
Architectural styles come in all shapes and sizes. Some are smaller design patterns of information sharing 4 4 Kent Beck, Ron Crocker, Gerard Meszaros, John Vlissides, James O. Coplien, Lutz Dominick, and Frances Paulisch (1996). Industrial experience with design patterns. ACM/IEEE International Conference on Software Engineering.
Len Bass, Bonnie E. John (2003). Linking usability to software architecture patterns through general scenarios. Journal of Systems and Software.
One fundamental unit of which an architecture is composed is a component . This is basically a word that refers to any abstraction—any code, really—that attempts to encapsulate some well defined functionality or behavior separate from other functionality and behavior. For example, consider the Java class Math : it encapsulates a wide range of related mathematical functions. This class has an interface that decide how it can communicate with other components (sending arguments to a math function and getting a return value). Components can be more than classes though: they might be a data structure, a set of functions, a library, an API, or even something like a web service. All of these are abstractions that encapsulate interrelated computation and state for some well-define purpose.
The second fundamental unit of architecture is connectors . Connectors are code that transmit information between components. They’re brokers that connect components, but do not necessarily have meaningful behaviors or states of their own. Connectors can be things like function calls, web service API calls, events, requests, and so on. None of these mechanisms store state or functionality themselves; instead, they are the things that tie components functionality and state together.
Even with carefully selected architectures, systems can still be difficult to put together, leading to architectural mismatch 8 8 Garlan, D., Allen, R., & Ockerbloom, J (1995). Architectural mismatch or why it's hard to build systems out of existing parts. ACM/IEEE International Conference on Software Engineering.
Dong Qiu, Bixin Li, and Zhendong Su (2013). An empirical analysis of the co-evolution of schema and code in database applications. ACM Joint European Software Engineering Conference and Symposium on the Foundations of Software Engineering (ESEC/FSE).
The most common approach to dealing with both architectural mismatch and the changing of requirements over time is refactoring , which means changing the architecture of an implementation without changing its behavior. Refactoring is something most developers do as part of changing a system 11,16 11 Emerson Murphy-Hill, Chris Parnin, and Andrew P. Black (2009). How we refactor, and how we know it. ACM/IEEE International Conference on Software Engineering.
Danilo Silva, Nikolaos Tsantalis, and Marco Tulio Valente (2016). Why we refactor? Confessions of GitHub contributors. ACM SIGSOFT Foundations of Software Engineering (FSE).
T. H. Ng, S. C. Cheung, W. K. Chan, and Y. T. Yu (2006). Work experience versus refactoring to design patterns: a controlled experiment. ACM SIGSOFT Foundations of Software Engineering (FSE).
Miryung Kim, Thomas Zimmermann, and Nachiappan Nagappan (2012). A field study of refactoring challenges and benefits. ACM SIGSOFT Foundations of Software Engineering (FSE).
Research on the actual activity of software architecture is actually somewhat sparse. One of the more recent syntheses of this work is Petre et al.’s book, Software Design Decoded 14 14 Marian Petre, André van der Hoek (2016). Software design decoded: 66 ways experts think. MIT Press.
Rabe Abdalkareem, Olivier Nourry, Sultan Wehaibi, Suhaib Mujahid, and Emad Shihab (2017). Why do developers use trivial packages? An empirical case study on npm. ACM SIGSOFT Foundations of Software Engineering (FSE).
References
-
Rabe Abdalkareem, Olivier Nourry, Sultan Wehaibi, Suhaib Mujahid, and Emad Shihab (2017). Why do developers use trivial packages? An empirical case study on npm. ACM SIGSOFT Foundations of Software Engineering (FSE).
-
Emad Aghajani, Csaba Nagy, Mario Linares-Vásquez, Laura Moreno, Gabriele Bavota, Michele Lanza, David C. Shepherd (2020). Software documentation: the practitioners' perspective. ACM/IEEE International Conference on Software Engineering.
-
Len Bass, Bonnie E. John (2003). Linking usability to software architecture patterns through general scenarios. Journal of Systems and Software.
-
Kent Beck, Ron Crocker, Gerard Meszaros, John Vlissides, James O. Coplien, Lutz Dominick, and Frances Paulisch (1996). Industrial experience with design patterns. ACM/IEEE International Conference on Software Engineering.
-
Jürgen Cito, Philipp Leitner, Thomas Fritz, and Harald C. Gall (2015). The making of cloud applications: an empirical study on software development for the cloud. ACM Joint European Software Engineering Conference and Symposium on the Foundations of Software Engineering (ESEC/FSE).
-
Neil A. Ernst, Stephany Bellomo, Ipek Ozkaya, Robert L. Nord, and Ian Gorton (2015). Measure it? Manage it? Ignore it? Software practitioners and technical debt. ACM Joint European Software Engineering Conference and Symposium on the Foundations of Software Engineering (ESEC/FSE).
-
George Fairbanks (2010). Just enough software architecture: a risk-driven approach. Marshall & Brainerd.
-
Garlan, D., Allen, R., & Ockerbloom, J (1995). Architectural mismatch or why it's hard to build systems out of existing parts. ACM/IEEE International Conference on Software Engineering.
-
Ravi Khadka, Belfrit V. Batlajery, Amir M. Saeidi, Slinger Jansen, and Jurriaan Hage (2014). How do professionals perceive legacy systems and software modernization?. ACM/IEEE International Conference on Software Engineering.
-
Miryung Kim, Thomas Zimmermann, and Nachiappan Nagappan (2012). A field study of refactoring challenges and benefits. ACM SIGSOFT Foundations of Software Engineering (FSE).
-
Emerson Murphy-Hill, Chris Parnin, and Andrew P. Black (2009). How we refactor, and how we know it. ACM/IEEE International Conference on Software Engineering.
-
T. H. Ng, S. C. Cheung, W. K. Chan, and Y. T. Yu (2006). Work experience versus refactoring to design patterns: a controlled experiment. ACM SIGSOFT Foundations of Software Engineering (FSE).
-
Marian Petre (2013). UML in practice. ACM/IEEE International Conference on Software Engineering.
-
Marian Petre, André van der Hoek (2016). Software design decoded: 66 ways experts think. MIT Press.
-
Dong Qiu, Bixin Li, and Zhendong Su (2013). An empirical analysis of the co-evolution of schema and code in database applications. ACM Joint European Software Engineering Conference and Symposium on the Foundations of Software Engineering (ESEC/FSE).
-
Danilo Silva, Nikolaos Tsantalis, and Marco Tulio Valente (2016). Why we refactor? Confessions of GitHub contributors. ACM SIGSOFT Foundations of Software Engineering (FSE).
-
Robert J. Walker, Shreya Rawal, and Jonathan Sillito (2012). Do crosscutting concerns cause modularity problems?. ACM SIGSOFT Foundations of Software Engineering (FSE).