Table of Contents
Introduction
In the previous post, we described connascence and explained why it’s so important to understand for software developers and architects. In this post, we will show a practical approach on how to fix different levels of static connascence. As a summary, connascence has nine levels (five static and 4 dynamic). The higher the level, the less code quality.

Connascence has three properties. Think of connascence properties as toggles that we can turn on in order to improve code quality within the same level of connascence. All three properties contribute to connascence at the same time. For example, connascence of position within private members of a class is not as bad as connascence of position that spans the entire codebase.

Connascence of Name
Connascence of name is the least evil level. It’s also hard to avoid as it happens every time we have a named construct that is referenced by other code. Simple examples: invoke a function by name, instantiate a class by name, pass named parameter, reference global constant or variable. The main idea captured by connascence of name is when a name changes, all code that references the name needs to change as well.
Code below shows different variations of concatenation of name, comments explain which is where.
ublic class ScheduleItem { public string Flight { get; set; } public string Origin { get; set; } public string Destination { get; set; } public DateTimeOffset DepartureDt { get; set; } public DateTimeOffset ArrivalDt { get; set; } public string Print() { // 1. We are referring properties here. // If a property name chages, we have to change it // in this line as well. return $"Flight {Flight} from {Origin} on {DepartureDt} to {Destination} on {ArrivalDt}"; } } public class Itinerary { // 2. If we rename ScheduleItem class we will have to change // the class name here to make code compilable. // 3. We may also need to change _scheduleItems field names // to keep it consistent with the ScheduleItem class name. private readonly IList<ScheduleItem> _scheduleItems; public void Print() { foreach (var item in _scheduleItems) { // 4. If we change Print methond name, we will have to // change it in this line as well. Console.WriteLine(item.Print()); } } }
Most of modern IDEs support renaming capabilities; therefore, changing names is least of a problem. For statically typed languages like C#, Golang, C++, and others, connascence of name is not a problem because of the compiler. If we introduce an error while renaming, then the compiler will not be able to resolve the name, and compilation will fail. We will know about the error right away, and the error will not make its way into production.
Different stories are for dynamically typed languages like Python, JavaScript, and others alike. Unless rigorous linters and extensive automated testing are used, a trivial renaming may introduce an error that may end up in production and cause a failure at runtime.
Fix Connascence of Name
This is a very basic level of connascence that does not have a fix and is inevitable. The only thing we can do is to work with connascence properties, e.g. increase locality, reduce degree, and strength.
Connascence of Type
This consciousness level appears quite often when code makes statements or assumptions about the types it operates on. Simple example: a function takes an argument of an integer type, so we can’t pass a string value to the function.
void Sum(int a, int b);
For statically strongly typed languages (C#, C++, Golang, and alike), the compiler takes care of most issues that can occur as a result of type violations. The code below is in C# and shows a few examples of the consequence of type and how the compiler helps to catch the issues.
public class ScheduleItem { public string Flight { get; set; } public string Origin { get; set; } public string Destination { get; set; } public DateTimeOffset DepartureDt { get; set; } public DateTimeOffset ArrivalDt { get; set; } public string Print() { return $"Flight {Flight} from {Origin} on {DepartureDt} to {Destination} on {ArrivalDt}"; } } public class Itinerary { private readonly IList<ScheduleItem> _scheduleItems; public string Print() { List<string> printedSchedules = new List<string>(); foreach (var item in _scheduleItems) { // COMPILE ERROR // printedSchedules is a collection of strings, // therefore we can not add ScheduleItem instance // into the collection. printedSchedules.Add(item); // FIXED // A fix is simple, developer just forgot to call Print() on item. // Print() returns a string that can be added to printedSchedules. printedSchedules.Add(item.Print()); } return string.Join(Environment.NewLine, printedSchedules); } }
In dynamically typed languages (Python, JavaScript, and others), the responsibility falls on a developer, so she needs to follow the rules, write automated tests, and ensure linters run before code gets deployed. Below is functionally similar code, but re-written in Python. If compared to the above, we can see differences in how statically and dynamically typed languages handle issues caused by the connascence of type.
class ScheduleItem: def __init__(self): self.flight = "" self.origin = "" self.destination = "" self.departure_dt = None self.arrival_dt = None def print(self): return ( f"Flight {self.flight} from {self.origin} on {self.departure_dt} " f"to {self.destination} on {self.arrival_dt}" ) class Itinerary: def __init__(self): self._schedule_items = [] def print(self): printed_schedules = [] for item in self._schedule_items: # No error, item is added to a collection that is supposed # to be a collection of strings. When code runs user will see that # itinerary is not printed correctly. printed_schedules.append(item) # A fix is simple, developer just forgot to call print() on an item. # However the error was discovered after the code was run. printed_schedules.append(item.print()) return "\n".join(printed_schedules)
Fix Connascence of Type
Connascence of type is thought to be a little stronger than connascence of name; however, it is a very basic level of connascence that does not have a direct fix and is also hard to avoid. The only thing we can do to improve connascence is to work with properties: locality, strength, and degree.
Connascence of Meaning
Connascence of meaning appears when a specific meaning is assigned to a value. For example, a function returns the string “SUCCESS”
when it is executed successfully. The code needs to check the function return value and compare it to the “SUCCESS”
string value. If we ever need to change the value of “SUCCESS”
, we will have to change it everywhere in the code.
The code below is full of examples of connascence of meaning.
“SUCCESS”
string value is returned when functionBookFlight()
executes successfully.0
is returned fromBookItinerary()
when the entire itinerary is booked.
public class ScheduleItem { public string Flight { get; set; } public string Origin { get; set; } public string Destination { get; set; } public DateTimeOffset DepartureDt { get; set; } public DateTimeOffset ArrivalDt { get; set; } } public class FlightBookingService { private static readonly Random _rand = new Random(); public string BookFlight(string flight, DateTimeOffset departureDt) { if (_rand.NextDouble() > 0.95) { return "FAILURE"; } return "SUCCESS"; } } public class Itinerary { private readonly IList<ScheduleItem> _scheduleItems; public int BookItinerary() { var bookingService = new FlightBookingService(); foreach (var scheduleItem in _scheduleItems) { var status = bookingService.BookFlight( scheduleItem.Flight, scheduleItem.DepartureDt); // Check if BookFlight completed successfully by // comparing retuned result against string value. if (status != "SUCCESS") { return 1; } } return 0; } }
How to fix connascence of meaning
Usually, a fix is very simple. We can assign a value to a global constant with a proper name, or create an enum to map a group of values to proper enum members. This way, we reduce the level of connascence by converting connascence meaning to connascence name.
Connascence of Position
Connascence of position appears when the order of elements assumes a certain meaning. For example, a user entity is represented as an array where the position of an element carries meaning: [FirstName, LastName, PhoneNumber]
. Another example, a function takes a long list of arguments where, of course, the order of arguments matters.
lass UserFactory { public string[] CreateUser( string firstName, string lastName, string phoneNumber) { // User is represented as an array where // each element position has meening. return new string[] { firstName, lastName, phoneNumber }; } }
How to fix connascence of position
Connaissance of position can be converted to connaissance of name by employing proper data types, e.g. classes to represent domain entities.
Functions that take long lists of arguments are most likely too large in size and are responsible for too many things. In this case, refactoring to multiple functions that are more focused will not only reduce connascence but also improve code readability and maintainability. Also, there may be a proper type hiding behind a long list of arguments, so grouping arguments into a proper data type may be a good solution that will reduce the level of connascence to the connascence of type.
public class User { public string FirstName { get; set; } public string LastName { get; set; } public string PhoneNumber { get; set; } } public class UserFactoryFixed { public User CreateUser( string firstName, string lastName, string phoneNumber) { return new User { FirstName = firstName, LastName = lastName, PhoneNumber = phoneNumber }; } }
Connascence of Algorithm
Connascence of algorithm appears when components need to agree upon the algorithm being used. For example, component A encrypts data and component B decrypts it. Components A and B need to agree upon the encryption algorithm; otherwise, component B will not be able to decrypt data that’s encrypted by component A. We can also think of string encoding (UTF-8, base64, etc.) and serialization formats (JSON, XML, protobuf, etc.) that need to be agreed upon by both sides.

How to fix connascence of algorithm
The best solution is to extract an algorithm into a separate component and share the component with others. To fix the example above, we can extract encryption and decryption logic into a component C so that components A and B would use component C to encrypt and decrypt data.

Summary
We walked through all five static levels of connascence and discussed possible fixes for each of them. Most of the fixes can be distilled down to two categories:
- Refactor code to reduce connascence level where possible. For example, convert connascence of position to connascence of type.
- Refactor code to adjust connascence properties, increase locality, and decrease degree and strength.
One thought on “Fixing Static Connascence”