Have you ever wondered why a code becomes harder and harder to support and maintain? Or why you received a mind-blowing estimate from a contractor? Or maybe when you asked someone from your team to add a few simple features, you’ve gotten resistance of some sort? That’s probably not because they want to take advantage of you. There’s another reason, namely, the code they have to deal with.
In this article, we will explain why sometimes it is costly to change code, even when you might think the change is minor.
With just a handful of new features may come a significant burden on changing the code, and in most cases that is not a quick or straightforward operation. The code complexity and its ability to improve are two opposites, and both are based on the specific type of code cohesion.
If the code is complex and coupled, then, as a result, it may be harder and more expensive to change. On the other hand, if the code is simple and straightforward, it is based on another type of cohesion and so will be much simpler and cheaper to support, maintain, and add new features.
2. Code Elasticity
At Konstankino we strive to deliver a quality product, that is why we use a specific term — “Code Elasticity.” This term is not new, and we did not invent it, but we do use it frequently. Code is considered elastic only when it is easy to change and improve. Code Elasticity is an elegant and ultimate process for any developer, whether senior or junior. The code can lose its elasticity when anyone on your team stops caring about its quality. That is why we have code review processes and quite a few gatekeepers before we get approval to merge our code into a major branch.
Elasticity is a constant process, and it is not a product. You cannot buy it, but you can gain it! So, let’s see how we can achieve this and make our code more elastic. Following some specific recommendations and principles can help to “elasticify” our code.
Cohesion (or code coupling) has many forms, and we will cover some of them in this article.
Here are the major players:
- Temporal Cohesion.
- Communicational Cohesion.
- Sequential Cohesion.
- Logical Cohesion.
- Coincidental Cohesion.
- Procedural Cohesion.
- Functional Cohesion.
3. Functional Cohesion
Let’s start with Functional Cohesion since it is the most favorable. Functional Cohesion is your goal. That is what Ruby on Rails developers mean when they refer to the notion of Cohesion. Functionally Cohesive code is code that does one thing well and not much of anything else.
In terms of methods or functions it can be `getCustomer()`, `chargeCustomer()`, or `getProductFee()`, for instance. Those are the basic methods that do one specific thing and are named properly. The functions above can be constructed into a chain of calls, such as `chargeCustomer(getCustomer(), getProductFee(ProductA))`. Of course, some frameworks have their own “way” to do things. This specific example shows us what Functional Cohesion is and how you can achieve it. Strive to achieve Functional Cohesion.
4. Procedural Cohesion
Procedural Cohesion is a code smell that you should be aware of. It is a yellow flag for you to notice you might be going a little off course. This type of code cohesion assumes operations in the code are executed in a specific order. This is to say that you have a procedure, and your method has to follow it. This is OK, but what you can do to improve it is extract those operations into specific methods or a class (depends on how that particular operation deals with the data and interacts with other activities) so that each new method will be responsible for the complete execution of one single logical operation.
5. Coincidental Cohesion
Coincidental Cohesion is a huge red flag. This type of Cohesion happens within a method which has a few operations that are not related to each other. An example might be when the code requests a customer’s record from the database, then asks for his or her profile, and then does something with that data. In this case, the only person who reads this method wholly and thoroughly understands it will be able to change it safely. Operations in this type of arrangement have nothing to do with each other; it is you, a developer, who ties things together and tries to keep all moving parts in your head. There is no simple way to improve this type of Cohesion. The most straightforward way to deal with it is probably to completely rewrite your method or class and redesign it from the ground up.
6. Logical Cohesion
We have another yellow flag: Logical Cohesion. And actually, you can also see it in tutorials on how to use some specific tools. Developers from the React and Redux community will find it familiar, or at least this is what some tutorials about Redux tell you to do. During Logical Cohesion, there are a lot of choices within the method, and a specific logical choice gets chosen based on the flag passed into the method. Recall Reducers in the React world? (Note: ReactJS Reducer functions sometimes return a huge object with the key: value pars) That is Logical Cohesion example, so now you are aware of it. What can you do? The simplest thing might be to create a method for each operation and call those methods within a wrapper class. If there is some code repetition you would like to avoid, then you may extract that code into a lower-rank (of private) methods and wrap higher-rank (or public) methods together with lower-rank methods into a class.
7. Sequential Cohesion
Another yellow flag is a Sequential Cohesion where you have a few operations within your method that must be executed in a specific order and use intermediate data from the previous procedures. All those procedures do not form a coherent functionality. An example might be when the code incrementally shapes an object by passing it as a parameter to the next function, and each subsequent function call mutates the current object state. The same solution can be applied here by splitting this method into a set of smaller methods and then nesting them to form an execution chain.
8. Communicational Cohesion
If you think about global data and how it impacts the understanding of the code, then that is Communicational Cohesion which is yet another yellow flag. An example may be when a few operations within your method use the same data, but none of those operations relate to each other in any other way. To get rid of this antipattern, you may want to split your method into smaller methods or classes, and each method will be responsible for data initialization and accessing data independently via a data source.
9. Temporal Cohesion
Temporal Cohesion is when you have a few operations within your method, and each operation must be executed during one specific interval of time. An example might be when you create a user, and then you do some other “related” operations, such as sending emails, generating default configuration or profiles, etc. Again, the quick way to improve this is to split your method into smaller methods and call them within a high-order method.
In this article, we explained what Cohesion is, how to differentiate between types of Cohesion, and what to do to improve your code. As you can see, sometimes adding more features or new functionality may dramatically impact your code elasticity, budget, agility, and ability to deliver a quality product. As with Coincidental Cohesion, you have no other choice but to completely rewrite the code to improve it which is not feasible in most cases, and your product ends up a hostage with poorly designed code that is prone to errors, expensive to develop, and hard to maintain.