In previous articles I discussed about different levels of coupling, and it is true that most of the times when we are discussing about components/modules and coupling we are focusing on the code, but there is also another dimension on the problem. The distance. The “distance” between components can significantly impact maintainability, readability, and overall system architecture. The distance can be the physical location of the components in a codebase and/or the position within the organizational structure, or even outside of it.
Components within the same module or file have the closest proximity (or lowest distance). For example functions in a small script file. This arrangement makes it easy to navigate leading to quick development and testing cycles. The longer the script file though the worst it becomes, so someone can argue to small files next to each other have better proximity than a long script file.
function validateInput(data: string): boolean {
// validation logic
return true;
}
function processData(data: string): string {
if (validateInput(data)) {
// processing logic
return `Processed: ${data}`;
} else {
return "Invalid input";
}
}
const data = "example data";
const result = processData(data);
console.log(result);
Components in different files but within the same directory exhibit slightly more distance. This organization is common in moderately sized projects where modules are logically separated but closely related. The inter-module distance introduces a slight delay in accessing and modifying related components but maintains a clear separation of concerns.
services/
├── user.ts
└── auth.ts
When components reside in different packages, the distance increases. This setup is typical in large applications where functionalities are modularized into packages. Inter-package distance requires developers to navigate between packages, increasing the complexity of managing dependencies and coordinating changes across different parts of the system. In certain cases the different packages belong to different teams, which increases the distance even more.
monorepo/
├── billing/
│ └── src/
│ └── index.ts
├── notifications/
└── src/
└── index.ts
A single team might own multiple related services. This setup allows for streamlined communication and decision-making processes within the team but maybe there is a need too coordinate deployments/releases of these services. For example:
In large organizations, different teams might own related services, necessitating clear APIs and communication channels. This setup can increase coordination complexity but also allows for specialized teams to focus on their areas of expertise. For example:
The greatest distance occurs when components span across different organizations, often seen when integrating third-party APIs or services. This scenario introduces significant complexity in terms of coordination, error handling, and dependency management. The organisations may have to sign certain SLAs and have an establish communication channel (e.g. Slack) for urgent matters.
Understanding the distance/proximity between components/services/modules helps in designing better systems by anticipating the complexity and communication overhead involved. Apart from the code level examination someone has to examine also the ownership of these components and the location they exist. As the distance increases, so does the need for clear contracts,robust error handling, and clear documentation.