When building applications, it is fundamental to think about design first.
This process of designing first helps visualize some of the key areas that you are going to tackle.
- How are the different elements or components going to interact with one another?
- What are the various components going to be?
- What are the responsibilities of each of these components?
Software Engineering is a pretty young field, compared to other engineering domains. And over the last 60 years or so, has made tremendous amount of progress. This has led to the rise of several different blueprints for solving common problems, often called design patterns or architectural patterns. These are great tools to use when developing your solution, provided you understand what they solve and when best to use them.
I have, in my personal experience, met engineers who did not understand the fundamentals behind creating good solutions. Two very important aspects that I think architects speak about and focus on when building extensible, solutions are:
Two simple words but when heard for the first time, developers look confused because they are just words after all, how do you apply it in software engineering?
I would like to first take you to the origin of these terms in software engineering.
Larry Constantine and Ed Yourdon, wrote a very influential book on software, Structured Design: Fundamentals of a Discpline of Computer Program and System Design. It was a book about software design and what a system designer must do to build a good software system. It focused on how to document and create a structured system. In essence, they defined it as:
Structured Design is the art of designing the components of a system and the interrelationship between those components in the best possible way.
That is exactly what we were talking about earlier. Wow! The concept had a name after all, Structured Design!
The idea behind the book, mostly revolved around how does a software designer pick a good design. Good software design could be evaluated based on efficiency, maintainability, generality, flexibility and utility of the solution. They conclude that the maintainability and extensibility of the software would be minimized when each part of the system, corresponds to exactly one small, well-defined boundary of the problem and the relationships between the different parts of the system corresponds only to a relationship between the parts of the problem.
So they start from the simple strategy of Divide and Conquer by partitioning and organizing the system into different parts.
In fact it is this book that potentially described monolithic as a system or portion of a system that consists of very highly interrelated parts that they act like a single large component.
The authors went on to explain that their approach was based on the theory of complexity of computer systems and programs. They nailed the problem that we often face these days when inheriting code written by others under pressure. They attempted to define it mathematically: The cost of system development is a function of the problem itself and the program complexity as measured in terms of human error.
Basically they were talking about the importance of designing small, easy to understand modules that can be treated as independent units, which can be later read and understood by humans to make further modifications.
When you think about it, it feels like common sense today, as there are plenty of software engineers and several successful software projects have been done and delivered already and good software engineering principles often focus on building such systems.
The authors of system design explained coupling as: How much of a module must be known in order to understand another module?
In essence, the more one has to know about module X to understand module Y, the more closely X is connected to Y, i.e. X is coupled to Y.
As their approach was to define structured design as a function of human error, this was a very good way to summarize what Coupling is.
In other words, it is the degree of dependence of one module/component in your program on another.
If you have been developing software for a couple of years, you have probably heard your senior engineers or architects, talk about highly coupled or loosely coupled as a means to convey how dependent one is on another. The higher the coupling, the harder it is to extend your software. Usually, any type of coupling is bad as it can lead to problems in one way or the other.
Why though? I think the answer lies in how Constantine and Yourdon explained it.
In a highly coupled design, you would have to understand several parts of the application before you make even a small functional change. The probability of making a mistake is very high. These days we reduce this probability by adding complicated unit or integration tests as safety nets for these complex systems. Although tests are great, when failing integration tests are the only way to find out if your change really worked or not, you know you have a problem - a poorly designed system. It clearly demonstrates how hard your program is to understand in order to even make a simple change.
There definitely is a lot of detail about Coupling, like different types or levels of coupling, from the worst kind to the best kind. But understanding the fundamentals can go a long way. Knowledge of this concept will be a guide to you when you are designing a class or a library.
Constantine and Yourdon, also goes on define Decoupling as a method to make modules more independent.
In English, the word Cohesion can be defined as the action or fact or forming a united whole. This does not clearly define the software concept but it alludes to it.
Constantine and Yourdon clarifies that deciding what becomes a module is not an arbitrary choice. It must be done in relation to the problem structure and the way a system is modularized can significantly affect the complexity of it. Basically, things that perform related functionality must be grouped together. This will make the module independent enough to do its job.
Cohesion is the measure of intra-modular functional relatedness.
It is closely linked to Coupling. A highly cohesive module, will be loosely coupled with other modules.
These are like two sides of the same coin and are powerful tools in the design of modular applications. This is why several other design patterns and principles focus on these two factors: creating high cohesion and low coupling.
I hope that gave you an insight into what these concepts really meant and how they came to be.
The definitions I have given here just touch the surface of the term. There are plenty of examples of different levels of coupling and cohesion that you can find online, hence I will be linking them below.