TechHui

Hawaiʻi's Technology Community

connascence

= con (e.g. together) + nascence (e.g. birth or coming into existence)

= to come into existence together as well as to change together, to die together

= a useful concept for writing maintainable code!

=~ coupling, similar to this well-known concept in software engineering

 

A few years ago I attended a conference where Jim Weirich gave a talk on "connascence" as applied to writing maintainable software, a concept he came across in Meiler Page-Jones's "What Every Programmer Should Know About Object-Oriented Design". I still remember that talk and found it quite useful in designing my own software and evaluating others. As most experienced programmers will agree, one of the most challenging aspects of writing maintainable code is dealing with change. How can we write a system where change is isolated and minimizes re-work when we inevitably receive a new requirement or have a better understanding of how to do something?
 

It turns out the trick is in carefully managing coupling or more specifically, connascence, in our code. Jim talked about varying types and degrees of connascence of which I will enumerate below. In general, the less of it, or at least the lower degree we have of it, the more maintainable we are.

Connascence of Name - Let's say you have a method defined as "my_method". Everywhere you call this method you reference it as "my_method". You've introduced a name dependency. If you change the name of your method, you change it everywhere it is called. This is the lowest degree of connascence, and arguably the most innocuous.

Connascence of Position - Let's say your method has the following signature my_method(start_date, end_date, name). Everywhere you call it you must pass in first a start date, then an end date, and finally a name. There is a dependency on the position of your parameters. This too is a relatively low degree of connascence. BTW, using a parameter hash list (where order doesn't matter) is one way of removing this type of connascence.

Connascence of Meaning - You introduce this when you arbitrarily specify special values to mean something. Different modules then need to have the same implicit understanding of how to interpret this data. Disseminating this understanding is a pain, not to mention what happens when somebody's understanding changes. For example, what's the difference between these two lines?

my_record.save(true)

my_record.save(false)

How about we lower the degree of connascence to connascence of name...

my_record.save

my_record.save_without_validation

Connascence of Algorithm - This happens when two modules have to agree on a particular algorithm to work. What if my signing function requires a hash function, and I chose to use MD5? Everybody checking my signature should hash with MD5, but what if I decide to use SHA1 tomorrow? There's no easy answer to this, but maybe I should have written my own library and distributed it from day 1 to users. That way we're just updating the library when the algorithm changes, isolating disruption, and mitigating connascence of algorithm.

Connascence of Type - This happens when two modules have to agree on the same data type. In statically typed languages, this is explicitly declared and enforced at compile time. In dynamically typed languages, this type of connascence is lessened. We still have to agree on the methods that can be performed on a piece of data, but they don't necessarily have to implement ALL methods specified by a particular data type defined elsewhere. By weakening this type of connascence, we arguably achieve more maintainability, but certainly fewer key strokes.

It gets trickier from now on, since this next batch deals not so much with maintainability in the face of change, but maintainability in terms of preventing issues at runtime. These are harder to spot, and harder to rectify.

Connascence of Execution - When the order of execution matters. Imagine you had a message queue, and your system must process these messages in the order they were received and place them in a database. You have a worker process dequeue-ing messages correctly and processing them to the database in order. What if you decide to scale horizontally and add more worker processes? You are no longer guaranteed in-order processing as each worker might finish in different times, unless you've implemented the proper control mechanisms first.

 

Connascence of Timing - When timing of execution matters. The classic example here is in multi-threaded programming when separate threads are writing to shared state and trample on each others work, or make assumptions that writes only occur simultaneously. Please use semaphores, lock, or synchronize your state in this case.

 

Connascence of Value - This occurs when values of two modules are related and must be to work. If you have a replicated database architecture you are dealing with connascence of value. In this case, the relationship between the the value in one versus the other is that they must equal! In a perfect world, we wouldn't need replicated databases, but that is a necessary evil in this world. However, in other cases, we can do a better job of mitigating the effects of value connascence. For example:

class Queue {

  private int head;

  private int tail;

  public def isEmpty() {

    head == tail;

  }

}

We have value connascence here since there is a relationship between the values of head and tail. By supplying the isEmpty method we have clearly explained the relationship between those values, and even made a useful function out of it for others to use.

Connascence of Identity - This occurs when two modules must refer to the same instance of object (not a copy of the object!). This often times is a hard requirement. If you are working with a specific record in the database, you need to be assured you are not working with a copy of it which may differ from another copy you have, and that the instance you have is definitive. Those who came to Rails from another platform probably noticed they had to call 'reload' on an ActiveRecord object in some unexpected places. Just be aware that you have this required relationship, and if you are not guaranteed it, know what the workarounds are.

Hopefully you've found these concepts as useful as I have in writing maintainable software. Ultimately, less maintenance work saves customers time and money, and frees us up to create new, wonderful things.

Views: 733

Comment

You need to be a member of TechHui to add comments!

Join TechHui

Sponsors

web design, web development, localization

© 2024   Created by Daniel Leuck.   Powered by

Badges  |  Report an Issue  |  Terms of Service