Software architects and engineers need a vocabulary for communicating the reasons why a refactor is needed or why a system or codebase needs improvement. Here, I will share with you what I’ve learned about connascence and how it may apply to real world examples. My hope is that more architects and engineers can use connascence as a common language for design best practices.
It’s debatable whether he coined the word, but Meiller Page-Jones, a trainer and software consultant, elaborated on the topic of connascence in his book first published in 1995, What Every Programmer Should Know About Object Oriented Design.1 The categories and description below are intepretations from his writing as they apply to modern code, combined with some of the ideas on connascence shared in Marco Consolaro’s talk on the subject.
The concept of connascence was originally developed as away to describe how to encapsulate object-oriented classes, and to identify unnecessarily coupled areas of the system. Connascence provides a way to understand which parts software are dependent on each other and whether there may be a better way to encapsulate them.
The word “connascence” broken into its parts, “con” (meaning with or together) and “nascence” (to be born), means “born together”, or “when one thing changes, something else also changes.”
There are two main categories of connascence, static and dynamic. Static connascence can be observed within the code and dynamic connascence occurs at runtime. There are good (or “weak”) forms of connascence within static and bad (or “strong”) forms of connascence within both static and dynamic categories.
Static Connascence
Connascence of Name
Of the types of connascence, there are two forms which are considered weak and desirable: connascence of name and connascence of type. In the ‘Pizza’ example below, a pizza may be instantiated with a type of crust and then changed in the ‘make_pizza’ method. When the crust changes in ‘make_pizza’, it will also change for the ‘get_description’ method due to connascence of name.
Connascence of Type
The ‘Pizza’ class also exhibits connascence of type: if sauce changes from boolean to string in ‘make_pizza’, sauce data type must also change in ‘make_pizza’ and ‘get_description’.
These two types of connascence, name and type, are discoverable within the code itself and are the only types connascence which are desirable in a software sytem.
Other types of static connascence are “stronger” and should be avoided in software design, including connascence of position, meaning and algorithm.
Connascence of position
The position of the arguments in the instantiation of ‘Pizza’ or the ‘make_pizza’ method illustrate connascence of position. Changing the order of arguments in the method requires a change in the order of arguments wherever the function is invoked.
One way to correct connascence of position, is to use arguments in the form of a class or named types. A linter or the interpreter will not allow an argument to be passed with the incorrect type. In other words, connascence of name and type are employed to alleviate connascence of position.
Connascence of Meaning
Connascence of meaning occurs when two or more components use a convention to agree on specific values. In the following ‘make_pizza’ example, ‘itemId’ is used to signify a type of pizza. Throughout the code, we must understand to what the ‘itemId’ refers. Additionally, anywhere we conditionally compare the ‘itemId’, it will need to change when the numerical value changes.
Correcting this problem again involves applying connascence of name and type. By identifying ‘pizzaType’ with an enum, we abstract away the numerical association and also move the value to a single named enum to be shared for any additional use of the identifier. When it becomes necessary to change the numerical association, there is no need to update the code except for within the enum itself.
Connascence of Algorithm
Connascence of algorithm occurs when two or more methods share the same logic in the code. The fix for this is to consolidate the method into a single method.
Here, the validation is redundant because toppings cannot be instantiated without calling the validation meethod. Therefore, the duplicate code within ‘make_pizza’ can simply be removed.
Dynamic Connascence
Connascence of Execution
Connascence that is only discoverable at runtime is called dynamic connascence. The first of this category, execution, is the dynamic equivalent of connascence of position which is also concerned with order. An example of connascence of execution is the necessary set of in-order steps to order a pizza. The developer must have some knowledge of these steps or they make break the system by changing the order of operations. The pizza system would break if the pizza was sent for delivery before it was assembled and cooked.
One way to mitigate this type of connascence is to include the use of a state pattern, encapsulating allowed operations to enforce the order of execution in state transitions. In the example below, each method kicks off the subsequent stage of the process.
Connascence of Value
Connascence of value also occurs at runtime when related components require validation that is not expressed by their primitive types. Using our pizza example, passing invalid toppings is an expression of connascence of value.
To correct connascence of value, we can once again employ connascence of name and type. By leveraging types and a validation method, the compiler prevents the possibility of incorrect values.
In another example of connascence of value, two databases hold the same redundant information. If one database receives an update, all other instances of the data must also receive the same update. An orchestration manager needs to maintain consistency between databases to ensure identical values in each database.
Connascence of Timing
Temporal connascence, or connascence of timing, occurs in real-time systems. Some systems have to be coupled with time, so this type of connascence is hard to fix. For example, a database requires a certain length of time to write a change to disk. If requests are made to overwrite a row at the same time, a deadlock will occur in the database. Solutions for connascence of timing include the use of timeouts and retries.
Connascence of Identity
Connascence of identity occurs when multiple objects must point to the same object. An example is the use of a singleton pattern, where every instance points to the same object. The ‘PizzaOrderManager’ class is adjusted below to show how a singleton may be implemented. We would expect ‘takeoutOrders.get_orders( )’ to return ["Pepperoni Pizza”], but instead it returns the ‘dineInOrders’ orders due to the instantiated reference to the same instance of ‘takeoutOrders’.
The fix for this is to remove the method that enforces the singleton and instantiate separate instances of ‘PizzaOrderManager’.
Conclusion
Having a common vocabulary to describe how a system is tightly coupled and connascent can provide a way for architects and engineers to communicate reasons why a system may need redesign or code may need refactoring. Understanding the worst cases should help prioritize which problems to tackle and why.
In the comments below, I’d love to know your stories of connascence and how you approached the fix for it. Are there other forms of connascence in your organization not covered in the categories above?
Page-Jones, Meiller. What Every Programmer Should Know About Object Oriented Design. (New York, NY: Dorset House, 1995) pp. 179-195
It might be a stretch, but infrastructure and other configuration changes and updates often require a sleep time while something external restarts or does some other business.
The animations help communicate the concepts more easily than "digital correlation." By which I mean using my actual finger (digit, heh) to audit positions, etc.