Code Quality – Issues with Circular References

code-qualitycoding-style

I was involved in a programming discussion today where I made some statements that basically assumed axiomatically that circular references (between modules, classes, whatever) are generally bad. Once I got through with my pitch, my coworker asked, "what's wrong with circular references?"

I've got strong feelings on this, but it's hard for me to verbalize concisely and concretely. Any explanation that I may come up with tends to rely on other items that I too consider axioms ("can't use in isolation, so can't test", "unknown/undefined behavior as state mutates in the participating objects", etc.), but I'd love to hear a concise reason for why circular references are bad that don't take the kinds of leaps of faith that my own brain does, having spent many hours over the years untangling them to understand, fix, and extend various bits of code.

Edit: I am not asking about homogenous circular references, like those in a doubly-linked list or pointer-to-parent. This question is really asking about "larger scope" circular references, like libA calling libB which calls back to libA. Substitute 'module' for 'lib' if you like. Thanks for all of the answers so far!

Best Answer

There are a great many things wrong with circular references:

  • Circular class references create high coupling; both classes must be recompiled every time either of them is changed.

  • Circular assembly references prevent static linking, because B depends on A but A cannot be assembled until B is complete.

  • Circular object references can crash naïve recursive algorithms (such as serializers, visitors and pretty-printers) with stack overflows. The more advanced algorithms will have cycle detection and will merely fail with a more descriptive exception/error message.

  • Circular object references also make dependency injection impossible, significantly reducing the testability of your system.

  • Objects with a very large number of circular references are often God Objects. Even if they are not, they have a tendency to lead to Spaghetti Code.

  • Circular entity references (especially in databases, but also in domain models) prevent the use of non-nullability constraints, which may eventually lead to data corruption or at least inconsistency.

  • Circular references in general are simply confusing and drastically increase the cognitive load when attempting to understand how a program functions.

Please, think of the children; avoid circular references whenever you can.