Thoughts on Circular Dependencies

What is a Circular Dependency, why is it a bad thing and what can we do about it?
By Dave Hillier Posted 14 March 2017

Thoughts on Circular Dependencies

In a recent meeting the seemed to be some confusion around why a circular dependency might be a bad thing or even what they are. It was kind of suprising to me; I'd assumed most experienced developers had been through Dependency Hell. I thought I'd write a quick post to hopefully clear up some of the confusion. Normally I'd just send a link to wikipedia, but its not great, so I thought I'd write some notes here.

What is a circular dependency?

A circular dependencies is when two software modules depend on each other; either directly on indirectly. They can exist at any level of our software architecture, a module could be a class, assembly, library, service or even a database. Any component that requires another so that it can function.

Sometimes circular dependencies are easy to spot. If you see a diagram of dependencies with a double headed arrow or two arrows in opposite directions, where A depends on B and B depends on A, then you have a circular dependency.

Other times it can be much harder than this, circular dependencies can be hidden inside any length of chain, for example, A dependencs on B, B dependencies C and C depends on A.

Circular references are a form of tight coupling and they suffer from the same problems any other tight coupling.

Why are circular dependencies bad?

Circular dependencies are a Chicken and Egg problem; which comes first? One cannot exist without the other. You must create the first component in a partially constructed state and then later attach the second.

Correspondingly the same problem might apply on destruction; circular dependencies can be the source of memory leaks. Which one do you shut down first?

Components like this are harder to use as they have more possible states that they can exit in. Perhaps this means your database must have fields that must be nullable or maybe the same thing applies to your class.

Not only are the components harder to reason about, but the system as a whole is harder to reason about. In a circular dependency, you're subject to at least twice the level of change; its much less stable. If one component in the dependency chain changes then it can impact the whole chain; you must understand all components in the chain.

If we're worrying about services, availability of these components is limited to that of the least available. They all must exist for a working system, therefore if one part goes down, they all do.

If we're building applications, classes can not be re-used independently and most dependency injection systems wont work.

What can we do about it?

There are a number of things we can do to either refactor our code or fix our architecture to eliminate circular dependencies.

The similest of things can be to admit defeat and accept when components are very tightly coupled and they cant be used independently, then they are actually the same component! You can often find opportunity to simplify by merging components. When the circular dependency exists you have no benefit from the independence so they may as we be together. At the very least, this can help to simplify usage, in the case of classes, or deployment, in the case of databases.

Alternatively, you can extract the part of each component that contains the cycle. Introducing new components can help remove cycles if you extract the tightly coupled parts into a new component. Hopefully these, smaller, independent parts are easier to reason about than two larger tightly coupled components.

If you're working in .Net a great tool for dependency analysis is NDepends. You can see the docs here on DSM which show how to identify cycles. Visual Studio Ultimate Edition has some dependency mapping tools built in now too.

If you spot a dependency in your architecture you should see it as a smell. Dealing with cycles is hard. We should all be working to eliminate cycles from our code and systems.

Dave Hillier
Dave Hillier
Development Director