Almost 50 years ago, Mel Conway noticed how modularity in product design mimicked the structure of the organization that created it. More precisely he said, “Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure.” Even though Conway’s Law is not a real law in the sense of a law of physics, there is empirical evidence that supports this observation. A study by MacCormack, Carliss, and Rusnak at Harvard verified that a loosely coupled organization produced a software design whose components were less tightly coupled than designs produced by a more traditional tightly structured organization.
Why do our designs reflect structures that mimic the organization that created it? For now, I will discount the possibility that the organization was structured to reflect the design mainly because organizations usually predate the design. Over time, it is indeed true that design has a deep impact on organizational structure.
Organizational structure often leads to the definition of formal interfaces between components produced by different parts of the organization. A well-defined interface increases productivity by allowing different teams to work independently of each other. When components are produced by the same team, the need for formal communication is reduced, leading to informal interfaces. As a result, component design takes advantage of the knowledge of how other components are built and the resulting design involves informal sharing of state and side effects.
So, how can we create products whose design isn’t biased by the organization that created them?
Overcoming Conway’s Law
One obvious way of overcoming the harmful effect of Conway’s Law is by acceding to it. Just split up the organization along modular boundaries. However, most of the time this isn’t feasible or even desirable. Often we don’t have control over the organization and excessive organizational boundaries can create undesirable management overhead. Sometimes, particularly in the early stages, we don’t know the modular boundaries so it is not possible to know how to split up the design. Sometimes a team is too small to be split up. Sometimes the expertise for multiple components resides within a small group of individuals – making it more practical to keep those components within a single group.
However, there are other reasons besides organizational boundaries for formalizing interfaces. Some are natural to the problem but other reasons can be invented by the team. Here are just a few of those reasons:
- Boundaries natural to the problem: For instance, if you are writing a web application, there is a natural part of the application that runs in the browser and a part that runs on the backend. This suggests a natural split in the functionality.
- Boundaries inherent in the hardware: If you are creating a medical device with multiple processors for different processing, you have a natural boundary for splitting your software into components.
- Boundaries based on run-time processes: Any time you have a process for handling different functionality, that is a natural boundary for modularization. The use of micro-services is one such example.
- Boundaries based on technology/language: For systems that utilize multiple languages such as Java for user interface and C/C++ for compute intensive areas, the parts processed by the different languages lead to a natural split between them.
- Map modularization to deliverables: Even when you have an application running in a single process context, you can split up the deliverable into multiple assemblies such as dlls and JARs. You can add additional rules to prevent cycles between these assemblies in your build process.
- Using tools: You can use tools such as Lattix Architect to define modules of your software system and to enforce the inter-relationship between them.
Desirable as it generally is, modularization has a cost, as well. Remember that the value of modularization is always with respect to the problem. Just as modularization makes it harder to break the architecture, it also makes it harder to change. It is best to be much more fluid when you are uncertain of the problem decomposition (such as at the start of the project) than in a mature solution where the requirements and implementation are understood and agreed upon.