nenadstojkovic.dev

Reviewed 6 min

Connascence

Coupling tells you two components are connected; connascence tells you what kind of connection it is, and Page-Jones's degree, locality and strength axes tell you how expensive that connection will be to change. A working note on the static and dynamic forms and the vocabulary for talking about which couplings in a codebase are actually dangerous.

Let’s talk about connascence. Probably the most important software metric you have never heard of. But even if you haven’t heard the word, you’ve heard of cohesion, coupling, and loose coupling.

Connascence was invented by Meilir Page-Jones in 1992, to allow reasoning about the complexity caused by dependency relationships in object-oriented design, much like coupling did for structured design. Connascence, derived from Latin, means “having been born together.” An undertone to this meaning is “having intertwined destinies in life.”

Coupling is, let’s say, coupled with modularity. Modularity is important, and coupling, cohesion and connascence are some of the software metrics which describe the level of modularity. And we want that “high cohesion and low coupling” thing, right? Low coupling is always a good idea, isn’t it? While low coupling is important for modularity, connascence offers more than simply measuring connections between modules — it helps us understand the relationships between modules.

Understanding connascence helps build maintainable, reusable, flexible, and understandable software. But just to emphasize: our goal should be “low coupling,” not “zero coupling,” because the components we write need to be connected to each other in order to do something meaningful.

Afferent and efferent coupling

We should mention a few definitions.

For two components, being coupled means that if something has changed in one component, we might have to change something in the other component too, because of that change.

There are two types of coupling: afferent (incoming) and efferent (outgoing). Yep, they have unreasonably similar names, and also not very descriptive ones.

Uncle Bob defines them as:

Afferent Coupling (Ca): The number of classes in other packages that depend upon classes within the package is an indicator of the package’s responsibility.

In other words, the afferent coupling metric defines the number of modules that depend on a specific module. A module with a high value is more likely to induce changes in the components that are dependent on it.

Efferent Coupling (Ce): The number of classes inside this package that depend on classes outside this package.

In contrast to afferent coupling, efferent coupling defines the number of components on which a certain component depends. Components with a high value are sensitive to the changes introduced to their dependencies.

Module A, B and C are components that our module depends on, which means that changes in them can cause changes in our module (efferent coupling). Module D, E and F are components that depend on our module, which means that changes in our module can cause changes in them (afferent coupling).

In other words, our module is efferently coupled to A, while A is afferently coupled to our module.

Cool. Not confusing. At all.

The table below shows examples with different afferent and efferent values. Which one do you like the most?

Component Afferent (incoming) Efferent (outgoing)
A 0 0
B 0 3
C 3 0
D 7 7

Think about it.

Yes, I agree with you, it depends. But we can notice that component A makes little sense in our context, and it is quite possible that component A is useless.

For component B we can say that nothing depends on it, but it depends on 3 other components. Is that good?

For component C we can say that three other components depend on it, but it depends on nothing itself — a safer position to change from, since nothing upstream can knock it over, though anything you break here lands on three others at once.

For component D we can say that it is the worst of both worlds: seven components can break it, and it can break seven components in turn. High afferent and high efferent coupling together is usually the strongest signal that a component needs to be pulled apart.

Static and dynamic connascence

Connascence can be static, when it can be assessed from the lexical structure of the code, or dynamic, when it depends on the execution patterns of the code at run time.

There are several types of static connascence:

  • Connascence of Name (CoN): this is when multiple components must agree on the name of an entity. If A were changed to int j, then B should be changed to j := 7.
  • Connascence of Type (CoT): this is when multiple components must agree on the type of an entity. In the (unlikely) situation that A were changed to char i, then B would certainly have to be changed too.
  • Connascence of Meaning (CoM) or Connascence of Convention (CoC): this is when multiple components must agree on the meaning of specific values.
  • Connascence of Position (CoP): this occurs when multiple components must agree on the order of values.
  • Connascence of Algorithm (CoA): this is when multiple components must agree on a particular algorithm.

There are also several types of dynamic connascence:

  • Connascence of Execution / order (CoE): this is when the order of execution of multiple components is important.
  • Connascence of Timing (CoTm): this occurs when the timing of the execution of multiple components is important.
  • Connascence of Value (CoV): this occurs when there are constraints on the possible values some shared elements can take. It’s usually related to invariants.
  • Connascence of Identity (CoI): this happens when multiple components must reference the same entity.

Properties of connascence

Page-Jones talks about two important properties of connascence that help measure its impact on maintainability:

  • Degree of explicitness: the more explicit a connascence form is, the weaker it is.
  • Locality: connascence across encapsulation boundaries is much worse than connascence between elements inside the same encapsulation boundary.

A nice way to reformulate this is using what is called the three axes of connascence:

Degree

The degree of an instance of connascence is related to the size of its impact. For instance, a software element that is connascent with hundreds of elements is likely to become a larger problem than one that is connascent with only a few.

Locality

The locality of an instance of connascence talks about how close the two software elements are to each other. Elements that are close together in the same encapsulation boundary should, typically, tolerate higher forms of connascence than elements that are far apart and in different encapsulation boundaries. In other words, as the distance between software elements increases, the forms of connascence should be weaker.

Strength

Page-Jones states that connascence has a spectrum of explicitness. But what does that mean? The more implicit a form of connascence is, the more time-consuming and costly it is to detect. Also, a stronger form of connascence is usually harder to refactor. Following this idea, we find that stronger forms of connascence are harder to detect and/or refactor.