Steve Smith's Blog

Musings on Software and the Developer Community

Insidious Dependencies

In the last year or so I've really seen the light on how to really write loosely-coupled code.  I thought I knew something about this concept before - I mean, I knew loose coupling was good, generally speaking, and I knew data abstraction was one of the key ways to limit dependencies between classes.  However, I didn't realize that I was unintentionally adding all kinds of coupling into my applications despite my best efforts to the contrary.  Let's talk about some dependencies, including some obvious ones, as well as some insidious dependencies that lurk in most applications I've seen.

Big Fat Obvious Dependencies

Let's assume you're working with a .NET application.  Guess what?  You're dependent on the .NET Framework, a particular CLR version, and most likely the Windows platform unless you've tested your application on Mono.  Are you working with a database?  If so, then you likely have a dependency on your database platform, and depending on how flexibly you architect your application, you'll either be tightly or loosely coupled to your database implementation.  Naturally it's going to be better for maintenance and testing if you can easily swap out your data access and persistence provider.  This is the canonical dependency that most people think about when they need to demonstrate a use for Dependency Inversion / Injection.  Consider a few others, however...

 

Insidious Dependencies

 

The File System

The file system is a clear dependency that should be abstracted behind an interface and passed into methods or objects that require it.  This will allow tests to be written without regard for where files are located, and it makes it much easier to set up new test environments (such as when a new developer needs to get set up).  This one is pretty obvious, but is easy to overlook.  Look for references to System.IO in your project that aren't in a particular implementation of an IFileSystem interface.

 

Email

I blogged recently about avoiding dependencies, with the example in that case being email.  Direct calls to System.Net.Mail should be avoided - instead create an IEmailProvider or INotificationService interface that is responsible for abstracting the process of delivering messages.

 

Web Services and Requests

Anything that calls out of process is automatically something you should be looking at as a dependency for your application.  Wrap calls to web services and other System.Net requests in interfaces so that you can easily implement test versions of their functionality.  As with System.IO, be wary of any System.Net namespaces you find in your code that isn't in a service implementing an interface you've set up to shield yourself from this dependency.

 

DateTime.Now and DateTime.Today

Definitely under the category of insidious.  Having references to the system clock within your application code is a big time dependency that makes code much more difficult to test.  For instance, what if you have a requirement that you only send emails on weekdays, not weekends - how are you going to test that it's working correctly?  Only run some tests on weekdays and other tests on weekends?  A better alternative is to have an IDateTime or ICalendar interface and a default SystemDateTime implementation that provides access to the system clock.  In tests, a separate StubDateTime (or a mock framework) can be used which can have specific values specified for its Now and Today methods.  I have a ton of DateTime.Now calls in one of my applications that I'm slowly converting, but it's good to see the code get better and the testing easier.

 

Configuration

I didn't realize until recently that even configuration files are dependencies that one might want to abstract out.  I'd thought that config files were already loosely coupled, because they were easy to separate and alter independently from the application itself.  However, as they grow in number and complexity, configuration files can quickly add significant overhead to setting up a new test or staging environment for an application.  In one of our projects, configuration files hold certain file paths which vary based on the developer's environment, so each dev has a separate, different copy of the config file with their own local paths specified.  These files cannot go into source control (directly - a template is in source control) and so new devs must create their own config file.  Further, these config files need to be deployed during testing, so MSTest must be configured to deploy them.

A better alternative - wrap all of the configuration logic behind an interface and in testing modes, have a simple config class that doesn't read its setting from a file but simply returns back the reasonable test values.  This combined with mocking out the File System would eliminate the need for complex configuration file set up and deployment within the test and dev environment.  This is still on my TODO list for this project, so I'll blog more once it's done.

 

New

Finally, the most insidious dependency of the bunch has to be the new operator.  Every time you instantiate a concrete instance of a class, you're establishing a dependency on that particular implementation.  With an IoC container, it's possible to minimize the number of places where you need to do this.  Instead, wherever variation or coupling may exist, interfaces can be used in place of implementation classes, and the IoC container can be used to determine the concrete implementation used.  Naturally you can't get away from all instances of new in your project, but do realize that wherever you see it, it's indicating a dependency in your code.

 

Others?

This is, I'm sure, not an exhaustive list.  It represents some of the things I went looking for in one of my projects' codebases this weekend.  What other insidious dependencies have you uncovered in your own projects?  How did you deal with them?

    kick it on DotNetKicks.com

Monday, 20 October 2008

Comments

 avatar

Andrei Rinea said on 22 Oct 2008 at 7:54 AM

Great post! I knew about much of these but didn't think about the calendar dependencies :)


 avatar

Sidar Ok said on 22 Oct 2008 at 10:12 AM

We can group most of these under the category of "Depending on Concrete Implementations". Implementations should depend on abstractions, abstractions shouldn't depend on implementations.

That is, for instance, if you are writing an interface instead of using List<T> you should consider using IList<T>. All of the things you listed are examples of concretions that we depend on "Insidious" ly because they are usually provided by the framework and not coded against any abstractions, so it becomes subtle that they are really dependencies.

Nice post and listing them up, thanks for that.


 avatar

SuperJason said on 22 Oct 2008 at 5:10 PM

Thread.Sleep is one that I ran into recently. It was slowing down my unit tests when it dawned on me that I could make an interface to abstract it out.


ssmith avatar

ssmith said on 22 Oct 2008 at 9:12 PM

@superjason,

Wow, I never thought of that one, thanks! I've occasionally used Thread.Sleep in tests (like to make sure something expired from a cache - not a good practice but I've done it) but I don't think I've ever needed production code to run more slowly than test code. But I can see how it could happen, so definitely a good one to watch out for!


 avatar

Chris Marisic said on 24 Oct 2008 at 10:42 AM

Steve,

Your post definitely pointed on some very easy to miss dependencies or slight of mind to easily forget about them during development especially the DateTime usage, I never would've considered that one.

But adding in the "new" operator one, that's a bit of a stretch, if you're going to add that there make sure you include ANY static method because you'd be just as dependent on it as the new object (see DateTime.Now).

I think what you are going for is the new operator that is out of bounds for the application layer, take the MVP pattern. Any new operator or static method that talks to anything other than the presenter is out of bounds, ie the developer that tries to directly call the controller directly from the aspx page that you repeatedly have to explain to them to stop that.


ssmith avatar

ssmith said on 24 Oct 2008 at 11:20 AM

@chris,

You're correct, static methods would be another insidious dependency - good catch! But no, I don't think it's going too far to say that any instance of new is a dependency. Obviously at some point you have to instantiate real objects, but the point is that you try and localize such calls to areas of your code that are easy to update, rather than having them sprinkled throughout the code. This has a bit more to say about it: en.wikipedia.org/.../Dependency_inve and the Head First Design Patterns book does a great job of explaining how "new" is a dependency as well.


 avatar

Chris Marisic said on 24 Oct 2008 at 1:57 PM

I'm all for DI, which is why I brought up the boundaries issue for code, if you follow the MVP pattern or similar, generally nothing will even create an object unless you have intermediary objects that you create when you scrape the data from the form, which the presenter will pass to the controller where the controller will hit a service of some kind and return back the results of the service.

Now after stating this, I can see much more why you should look at the new statement since it very rarely should ever need to be invoked from anything that's not a service returning something or from a screen scrape.


 avatar

Khalid Abuhakmeh said on 27 Oct 2008 at 8:25 AM

I agree with you, I have found that IoCs are a good way to loosen dependencies on classes. The only problem is that you are now dependent on your IoC (oh the irony!). Although recently I have seen an all encompassing interface for IoC's. You just need to wrap your favorite IoC in this interface and you are loosely coupled again. Their is a point of diminishing returns when separating dependencies; i.e. creating an interface for string might be a bit much, haha.


 avatar

ulu said on 11 Oct 2009 at 12:30 PM

Your main argument is that such dependencies make your code hard to test. True, unless you use some well-known tools. So, there comes the next argument (that you don't mention) that testability (*without* these tools) leads to a better (llosely coupled, maintainable) design. I absolutely agree, but there are edge cases when the effort put into making your design testable is just not worth it: you end up with several extra classes and interfaces that make your design even more obscure. So, I believe that instead of blindly following this dogma, you should consider it on a case by case basis.

For example, depending on HttpContext.Current is bad. Why? Not because you think you'll need another implementation one day. And not because it is impossible to write the tests for (it *is* possible). But because you access some global state that should be prepared. So, if another developer tries to use your class in a different environment, she won't know about the dependency and will get mysterious errors.

On the other hand, the dependency on DateTime.Now is not so harmful. It's gonna work anyway. You *can* test it (with a certain commercial tool). You don't want to be decoupled from it (you're coupled to mscorlib anyway). Sure you can make a wrapper, but it won't buy you anything in terms of design.

So, while testability is a great guide in pushing you toward a better design, you should follow it with open eyes, because there are much more important reasons to avoid hidden dependencies.


ssmith avatar

ssmith said on 11 Oct 2009 at 2:03 PM

@ulu,

Yes, there *are* tools that can try and help you test things when your design is overly coupled, but creating an ICalendar that you then implement with a class called SystemCalendar that hands you back .Today and .Now requires about as much time and effort as my typing this comment does. However, learning a new tool (and buying it) to overcome dependencies on implementations rather than abstractions will take significantly more time, at least in this case. You might argue that you only need pay the price of license + learning once, and over time it will pay dividends, and that's possible. But that also assumes that anyone else who ever works on your code will also need to pay for and learn such tools, and I'd rather not make commitments like that if I can avoid them with a simple interface and default implementation that follows good SOLID OOP principles.


 avatar

Giorgio Sironi said on 13 Oct 2009 at 1:40 PM

I would add the Random class (not know how it is called in .NET), as difficult to test if not injected.