Making sense of connascence

Every developer out there wants their code to be maintainable and changeable. Right? Well, every application out there changes eventually. Connascence as a methodology helps you to discover the reliability of your code.  
Geschreven door Ben Cavens

At the DDD Europe 2020 conference, I attended a session by Paul Raynard on the topic of connascence. Terminology that was new to me so I'm writing this article to explore the basic concept. If you’re interested in a novel introduction to this subject, this blogpost is made just for you.


What is connascence?

Connascence in its generic meaning refers to the birth of multiple things at the same time. The act of growing together. In the programming world, this translates to the following definition:

Two components are connascent if a change in one would require the other to be modified to maintain the overall correctness of the system.

So basically connascence refers to coupled code. Two objects are connascent if object A cannot be changed without changing object B.


The cost of change

Code that is less coupled will be easier to change. Good code welcomes change. Technical debt is directly related to the cost of change. Protective code has fewer unknowns when it changes.

Every developer out there wants their code to be maintainable and changeable. Right? Well, every application out there changes eventually. One way or another. Change is dictated by every app usage by users, evolving business rules, altered regulations, new features, software and framework updates and so on.

https://dilbert.com/strip/2017-07-20


Make the easy change

To phrase it with a Kent Beck quote: First make the change easy, then make the easy change.

Meilir Page-Jones suggests in his book, Fundamentals of Object-Oriented Design in UML, that to improve maintainability, you should minimise the overall connascence within your application.

That's great but how do we do this? Well, connascence as a programming methodology gives the developer a way to discover and identify code coupling. It provides a common vocabulary. It allows the team to reason about relational complexity.


Static and dynamic connascence

The methodology gives you nine types to work with. Nine common ways of how code logic can be coupled.

All these types are either static or dynamic. The static ones are name, type, meaning, position and algorithm. The dynamic ones are execution, timing, values and identity. A static type can be discovered by visually looking a the codebase. A dynamic type is detected at runtime. The dynamic types are much harder to discover than the static ones.

For a detailed explanation and examples of these types, you can check out https://connascence.io/


Decoupling is hard

The impact of each form of connascence can be determined by: strength, locality and degree.

The strength of a connascence case is measured by the ease with which it can be decoupled. Connascence of name, for example, shows a weak strength because changing a name in your application is a lightweight task. In general, the static coupling is easier to deal with than the dynamic form.

The locality tells you how close two or more coupled elements are to each other. It's about encapsulation. A class, method or domain is expected to have more connascence than code further apart. A connascence that is isolated into one class is far more straightforward to maintain than when it's spread out across the entire codebase. The goal is to reduce the spread of your coupling as much as possible.

The degree metric is about the size of the connascence. Are there two elements involved or fifty? The more elements are involved, the more effort is needed to make a code change.


A basic example

Consider this pseudocode where you have an Article class with a method putOnline and isOnline.

class Article{
    function putOnline(){}
    function isOnline(){}
}

// Testfile
class ArticleTest{
    function test_if_article_can_be_put_online(){
        $article = new Article();
        $article->putOnline();
        $this->assertTrue($article->isOnline());
    }
}

We see here that there is a connascence of name for the putOnline and isOnline methods. You have connascence of name when, by changing a name, you have to change it elsewhere in the code.

The form of connascence here has the following impact profile:
- strength: weak. This type is straightforward in refactoring.
- locality: average. The methods are also present in the test class.- degree: small. There are only two occurrences.

With this small assessment, it's clear that this is an easy case. Now let's say you'll want to change the putOnline method to publish. Well, now the test will fail because it's calling an unknown putOnline method. The method needs to be renamed in the test as well.

Now you're back to working code and the compiler may be satisfied. But we as humans still see some visual improvements possible. It doesn't feel right when the concept online is used next to published. There is a conflict inside the domain language. The language is now confusing. We can fix this by changing the Article::isOnline method to Article::isPublished and renaming the test method from test_if_article_can_be_put_online to test_if_article_can_be_published.

Keep in mind that this refactoring does not at all remove the connascence of name. It merely demonstrates its impact. The connascence of name remains, even after changing the method name.


A dynamic type example

The following code snippet shows a dynamic connascence of execution. This is coupling where the order of execution dictates the way state will be altered.

class SendMail(){
    public function handle(){
        $mailer = new Mailer();
        $mailer->to('ben@example.com');
        $mailer->subject('this is not spam.');
        $mailer->send();

        $mailer->cc('sam@example.com');
        
    }
}

Here we have a potential issue where the $mailer->cc method is called after the mail has been sent. This results in the cc not being included. 

This is known as a connascence of execution. It has the following impact profile:
- strength: average. Refactoring, in this case, is still easy since it's visually clear what to do.
- locality: low. The coupled code is present in the same method.
- degree: small.

This case of connascence is a bug that needs fixing. The fix in this case - placing the cc method before the send method - is rather easy once you've spotted it. The problem is that this kind of coupled behaviour is much harder to discover because it only reveals itself at runtime.


Conclusion

The connascence types and the metrics of strength, locality and degree provide you with a good common lexicon to discuss technical debt. It helps you frame the changeability of the codebase. It can even assist as an assessment tool for refactoring.

Static coupling can be discovered and improved by static analysis tools, your IDE finder, code reviews or by reading through the codebase. Dynamic types don't expose themselves until runtime. They are - hopefully - detected by test coverage or via manual testing. In the other case, your users will let you know :)

Your codebase will always be connascent.

It just depends on whether you as a developer are aware of what kind of connascence is present. Development is about making conscious decisions on whether it is worth the effort of refactoring.


Used resources

https://connascence.io/
https://en.wikipedia.org/wiki/...
https://www.amazon.co.uk/Funda...