As software engineers, we often discuss complex concepts that might lead to nebulous and endless discussions. Coupling is one of these complex concepts that raise common questions. How harmful is coupling? How can we measure coupling? What are the different types of coupling? Connascence introduces a shared vocabulary for determining different types and the severity of the coupling in our systems, answering these questions, and making discussions around coupling less nebulous and goal-oriented.
What is Connascence
Let’s define the concept of Connascence:
Two pieces of software share connascence when a change in one requires a corresponding change in the other
Connascence allows us to go beyond the binary of “coupled” and “not coupled”, serving as a tool to measure coupling and describe how bad it is under different levels and kinds.
How we measure the Connascence
- Strength: Stronger connascences are harder to discover, and/or harder to refactor.
- Degree: Describes how many components are coupled. Components with a large number of connascent entities are a more significant issue than just a few connascent entities.
- Locality: Describes how close the coupled components are. A high locality is better and means coupled components are in the same module.
Connascence = Strength x Degree / Locality
Types of Connascence
Connascence comes in many forms. Below are three of the most common that appear in Rails applications and how to avoid them. They are listed from weakest connascence to strongest, so it is worthwhile to refactor from one of the later forms on the list to one of the earlier forms.
Connascence of Name (CoN)
Connascence of name happens when multiple components must agree on the name of an entity. Constants and object names are an example of this form of connascence: if the name of an object changes, every reference to that object needs to change. It’s the weakest form of connascence and almost always unavoidable.
class Order
def Order.most_recent
...
end
end
Strength -> πͺ (weakest form of connascence)
Degree -> π (only one component)
Locality -> πππππ (inside same model)
This code has an explicit dependency between our Order
class name
and how we defined our Order.most_recent
class method. Ruby provides
a way to refactor this code and remove the CoN (Connascence of Name).
class Order
def self.most_recent
...
end
end
Strength -> (zero connascence)
Degree -> π (only one component)
Locality -> πππππ (inside same model)
Even though CoN is often harmless and expected to exist, it should be eliminated when possible so this was a nice refactor.
Connascence of Position (CoP)
Connascence of Position happens when multiple components must agree on the position/order of their values. Let’s see an example to explain this type of connascence better.
class Order
# Always add a new status to be the last element of the array
enum status: [:pending, :published, :delivered]
end
Strength -> πͺπͺ (stronger than connascence of name)
Degree -> πππ (model and database are coupled)
Locality -> πππ (models are distant from the database)
This example is one of the most common CoP in Rails codebases and it has a low Locality because it’s coupled to a distant database. Declarations like that often come with a comment, explaining that the position of the enum items matter – but fortunately, it has an easy fix: it is possible to use a hash instead of an array, which would explicitly map the enum values instead of relying on their implicit position within the array. This refactoring changes from CoP to CoN, thus reducing the Strength of the connascence.
class Order
enum status: { pending: 0, published: 1, delivered: 2 }
end
Strength -> πͺ (connascence of name)
Degree -> ππ (model and database are coupled)
Locality -> πππ (models are distant from the database)
Connascence of Meaning (CoM)
Connascence of Meaning happens when multiple components must agree on the meaning of particular values. In its most basic form, it’s all about magic values.
class Order
def deliver!
if self.type == '001'
...
else
...
end
end
end
Strength -> πͺπͺπͺ (connascence of meaning)
Degree -> πππ (multiple models checking this condition or using this value)
Locality -> ππ (multiple models, database)
The meaning of type == '001'
is unclear, and such a conditional tends to be repeated
throughout our codebase, leading to increased Locality. If that value changes in one place,
it must also change in another.
CoM can be improved to CoN by moving magic values to named constants and creating convenient methods to be used externally. However, in doing so, we have increased the amount of CoN but again lowered the Strength of connascence.
class Order
B2C_TYPE = '001'
def b2c?
type == B2C_TYPE
end
def deliver!
if b2c?
...
else
...
end
end
end
Strength -> πͺ (connascence of name)
Degree -> π (single method to check)
Locality -> ππππ
In Summary
Connascence’s arguably most important benefit is to give developers a common vocabulary to talk about different types of coupling and DRY principles in a less abstract way, allowing software engineers to easily share experiences by referring to a standard set of nouns, resulting in productive discussions on code reviews and pull requests.