I have meant to write a post for quite a while now, on how my team and I got up and running with NDepend on a big legacy codebase. So, here goes:
Preface
I am currently employed as a developer on a mortgage bond administration system. The project was started almost 6 years ago when SOA was (apparently!!) incredibly hot, so it’s got a lot of web services which was some architect’s interpretation of service-orientation.
The aforementioned “architect” left the project pretty early though, and after that our team has consisted of 4 to 8 people in various configurations.
This, combined with a couple of hectic deadlines along the way, has led to a big, fragmented codebase, where some areas have been left almost untouched for years, other areas are big balls of mud that everyone currently on the team fears to touch, other areas are characterized by developers having been in a hurry when they wrote the code etc etc.
A few times along the way, the idiomatic way of implementing new stuff has been radically changed to make things better. One example is that instead of orchestrating a bunch of web services RPC style, all new functionality is now being implemented in a single web service, messaging style.
Another thing is that the system is not built with an IoC container in mind, but that did not prevent us from introducing Castle Windsor. That means that services must be pulled from Windsor, service location style – and even though we try to encourage people to reduce their calls to the service locator in only a few well-defined places, some developers did not understand why, and they went ahead and used the locator in all kinds of places.
To put it like we do in Danish when someone owes more than their mortgage is worth: We have technical debt rising above our chimney!
What to do?
When you have so many problems that you don’t know where to begin, how do you solve your problems then?
Well, the Japanese have a word, Kaizen, which is beautifully capturing the concept of constantly improving things.
It’s a philosophy I try to consider all day long, when I write code, modify code, and even when I look at code: I constantly make small changes, remove cruft, and refactor into better more idiomatic ways. How can I do that without breaking code as well? Simple: We have automated tests!
NDepend is a really cool tool that lets us achieve Kaizen on a higher level 🙂
Quick introduction if you don’t know anything at all about NDepend
In NDepend, you create a project and add all the assemblies and .PDBs from your build. Now NDepend can analyze your assemblies and tell you all kinds of interesting stuff about your code.
E.g. it can show you a dependency matrix, which visualizes dependencies between assemblies and namespaces. That way, you can see if your application adheres to a layered structure, or if dependencies are circular.
But the feature I want to focus on here, is CQL rules. CQL is Code Query Language, which resembles SQL a bit, but is used to query assemblies, types, methods, fields etc. Using CQL, I can also generate warnings if certain conditions are not met.
What makes this extremely interesting in our case, is that we can run our CQL rules from our automated build, via CruiseControl.NET, which integrates nicely with the automatedness (is that a word?) that is required for an agile team.
A few examples
Simple stuff – people not adhering to our naming conventions
I am a firm believer that code must be clean, streamlined, and uniform, in order to reduce the perceived noise when reading it. For some reason, a couple of team members re-installed their ReSharper and got their R# naming rules reset, which resulted in numerous cases where field names were prefixed with
_.
To address this annoyance, I created the following CQL warning:
|
WARN IF Count > 0 IN SELECT FIELDS FROM ASSEMBLIES "SomeDomainAssembly.Core", "SomeDomainAssembly.CoreServices", "AnotherAssembly.Common", "FourthAssembly.Foundation" WHERE NameLike "^_" |
– which from now on will yield a warning and the names and locations of violations in the build report in CruiseControl.NET. Nifty!
More annoying stuff – people using the service locator to instantiate stuff in tests!
Building a pretty complex system with a complex domain can lead to complicated set ups when “unit” testing. E.g. when a payment is recorded in the system, a payment distributor will generate money transactions for interest, principal reduction, and more, in accordance with some particular customer’s strategy. Then, if the SUT needs a couple of payments to have been made, some of our developers took a shortcut in their test set ups, and just pulled a
IPaymentDistributor from our service locator, which – unfortunately – is fully configured and functional in our tests.
The real problem is of course that a great portion of our core modules are so strongly coupled that they cannot be tested in isolation.
But given that we have 1 MLOC, it’ s not feasible to change the design of our core module at this time. But what we can do, is to make it visible to developers when they pull stuff from the service locator during testing. That can be achieved with the following CQL:
|
WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "SomeDomainAssembly.Core.Tests", "SomeDomainAssembly.CoreServices.Tests" WHERE IsDirectlyUsing "CoreStuff.ServiceLocator" |
Most annoying stuff – people not understanding IoC containers, using the service locator from within services already pulled from a container
This is something, that makes me angry and sad at the same time 🙂 but some team members did not understand Castle Windsor and what IoC containers can do, so they just went on and did calls to
ServiceLocator.Resolve<I...> from within services that were already pulled from the container.
There’s too many reasons that this is just icky, so I will just show the CQL that will make sure that this misconception will not live on:
|
WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "SomeDomainAssembly.Core", "SomeDomainAssembly.CoreServices", "AnotherAssembly.Common", "FourthAssembly.Foundation" WHERE HasAttribute "FourthAssembly.AutoRegistration.Attributes.ServiceAttribute" AND IsDirectlyUsing "CoreStuff.ServiceLocator" |
As you can see, this rule builds on the fact that all our service registrations are made by registering types decorated with the
ServiceAttribute.
Conclusion
I have shown a few examples on how we set up automated assertion of certain properties in our architecture. NDepend has already proven to be extremely useful for this kind of stuff, and it allows us to continually add rules whenever we identify problems, which is what we need.