views:

2265

answers:

9

Why are circular references in Visual Studio a bad practice?

First, I will describe an example of how this can happen using C# in Visual Studio, since VS will typically inform you if you have a circular reference and prevent it.

First, a Utilities class is created relying only on the code given to you by Visual Studio and .Net. Then, an E-mail class is created that depends on Utilities. Instead of adding both projects to a single solution, a new solution is created and a reference to Utilities.dll is added. Then, some time later, someone decides that they want the Utilities class to send an e-mail and adds a reference to Email.dll. Visual Studio is perfectly happy to let you do this, but now the source won't compile as-is without one of the binaries.

At my place of work it is standard procedure to copy-and-paste binaries when doing development and then only building the projects you're working on. This has led to at least one circular reference in the codebase that has gone unnoticed for over 3 years.

This seems like a very bad practice to me because there is no way to build either project from source without having the DLLs first. That argument falls a little flat to the "practical" people I work with since it seems highly unlikely that we will lose all copies of our binaries simultaneously. The binaries aren't stored in version control at any point, which only makes me more worried.

This seems like a situation that should be avoided, but not a situation that poses any appreciable threat. Are circular references between projects really a big deal or am I blowing it out of proportion?

A: 

The question I would pose to your coworkers is:

Okay, it is very unlikely we'll lose all our binaries at the same time. But, tell me, what's the benefit of this approach?

If they come with anything different than "We've always done it this way", I'd like to hear it. And, as everybody knows "we've always done it this way" is NOT a good reason and they shouldn't defend it.

Vinko Vrsalovic
"we've always done it this way" is one step away from "It costs the company money to change it"
Jimmy
It will cost the company more money not to
Vinko Vrsalovic
A: 

I'm pretty sure that isn't quite a correct answer @Selfinflicted. You are describing the algorithm of reference counting which is a very elementary way of performing garbage collection. Modern garbage collectors all are based on 'Mark and sweep'. You can read about it here http://www.brpreiss.com/books/opus5/html/page424.html

Additionally Garbage collection varies, but .NET and Java use a generational garbage collection scheme: http://www.csharphelp.com/archives2/archive297.html

Generational garbage collection expands on mark and sweep

DanielHonig
Yeah, I took it to mean Visual Basic code and memory management at first. But it means sub-project assemblies and their (circular) dependencies.
Roboprog
+19  A: 

Yes, this is a bad practice for precisely the reason you've stated - you cannot rebuild from the source.

Peter LaComb Jr.
I tried to come up with more to say than this answer, and I cannot. +1
David B
Ditto. Upvote for you.
Kevin
I would add that a good way to avoid this kind of problems is automated continuous integration.
Juozas Kontvainis
A: 

Hm, I think in terms of the OP question, about building projects from scratch, it is bad practices, due to:

  1. Inability to build from source / scratch - What if I maintain several versions of code. Between each version, a few changes to each of the libs have been made.. The only way I can feasabily maintain these concurrent versions, is to now store the compiled binaries in my version control. This also means comparisons between each library version is that much harder. (I need to find out, what version is being used, then go look at the actual source code for the lib, rather than just a direct diff on the source itself.
  2. Possibly harder to push out patches to customers which have incrementally patched versions.. Where and what to patch?
Alex Lim
+3  A: 

Aside from build issues, circular references always indicate a design flaw. In .NET, a circular relationship makes two assemblies effectively one assembly. If neither one can live on its own without the other, building them separately is just an exercise - it doesn't change the fact that together they represent a monolithic assembly.

I've noticed this a lot with utility assemblies. Must be an anti-pattern.

Corbin March
A: 

circular dependencies are bad because:

  • project A references project B
  • when project B changes, project A needs to be rebuilt
  • now if project B also references project A
  • then when one of them changes, the other one needs to be rebuilt
  • which causes the other one to need to be rebuilt
  • which causes the other one to need to be rebuilt
  • etc.

this is potentially an infinite-loop in the build process

Steven A. Lowe
+6  A: 

This is one of the symptoms of over-modularization.

I worked at a company once with about twenty developers and more than sixty different active projects in the SVN repository. Every project had its own build script and produced a JAR file that was a dependency of at least a half-dozen or so other projects. Managing all those dependencies was so complicated that we wasted a ton of time trying (unsuccessfully, I might add) to set maven projects to automatically fetch all the correct libraries (and the correct versions) for all those little micro-projects.

The funny thing (to me) was that it was really only one project, and it wasn't even something we distributed to the outside world. It was a hosted application, with a web front-end, running on a single server cluster.

Sheesh.

Another side-effect of the architecture was that the same kinds of functionality got duplicated over and over again (not always with the same algorithms, or results, for that matter) in several different projects. I think part of the reason was because people didn't want to introduce a new dependency on an entire sub-project just to get access to a few of its classes. But I think another reason was the people just didn't know what code existed where, and rather than go to the trouble of finding the code they wanted to reuse, they'd just rewrite it in their own project.

Of course, modularity is generally a good thing.

But like all good things, it can be taken to ridiculous extremes.

My advice is to find those circular dependencies and merge the projects into bigger chunks, since the current project breakdown probably represents a false modularity. Better to divide your big project into a few well-separated modules than to have a zillion pseudo-modules creating artificial boundaries between logically-coupled classes.

benjismith
A: 
EnocNRoll
A: 

If you're having trouble convincing your "pragmatic" colleagues that this is a bad practice, offer a pragmatic solution: Remove these circular references and build times will go down since loads of unnecessary rebuilding will be avoided.

Robert Jeppesen