Why do I do what I do? To make a difference in the lives of other developers. I was delighted to receive the following email recently.
Hi James,
I’m writing you because I just wanted to thank you!
It was about two months ago and I attended TechEd/Orlando. I have to say that it was the first time for me and it really was an honor to be the one and only chosen from approximately 300 software developers working in my company to go to Orlando. I was very impressed about the good quality of the sessions, especially on the architecture track, but your tiny little discussion on Tuesday evening really opened my mind. At that time I had been working as a software developer for 7 years with about 5 years experience in software design and project management and 5 years of .NET experience. I was fighting with a 400,000 LOC .NET legacy monster that’s used by public safety agencies around the world in security related areas. I have a team of 12 developers and we were trying really hard to keep this beast up and running and extend it with new features. I think you can imagine that most of the time we were using the trial and error approach to get new features in (just hack it in and prepare for long debugging sessions hunting weird bugs in parts of the application you never expected to be affected by the new feature…). The main problem was – guess what – the dependencies. There were about 50 classes (all singleton “Managers”), and every single manager was tied to at least 10 other managers – no interfaces at all – and that’s just one of several layers of the app… We knew that dependencies were our problem, but we had no clue how to solve it – there was this chicken/egg problem – I want to decouple my system, which needs a lot of refactoring. To ensure that I don’t break anything I’d need unit tests but I can’t use them because my system is so highly coupled 😉 We have tried TypeMock, but my feeling was that this went in the wrong direction. Needless to say that this attempt failed.
During the discussion after your session you gave me some quite useful hints:
1. Read Applying Domain Driven Design and Patterns by Jimmy Nilsson
2. Read Working Effectively with Legacy Code by Michael Feathers
3. Use ReSharper (especially for Refactoring)
4. Use a Mock-Framework (preferably RhinoMocks)
5. Use a Dependency Injection Framework like Spring.NETI bought Jimmy Nilsson’s book in the conference store and read it cover to cover on my flight back to Vienna. Then I bought the second book and read it within one week. I started to use ReSharper more extensively to apply some of the patterns from Michael Feathers’ book to get some unit tests in place. I extracted a lot of interfaces, brought Spring.NET into action and used RhinoMocks and the VS2005 built in UnitTest-Framework to write some useful unit tests. I also used the built in code coverage functionality in combination with the unit tests. In addition we already started Design for a messaging based service application that we want to develop in a very TDDish style.
As you can see there’s a lot going on since I attended your session. It was really this discussion about agile principles that gave me sort of a boost.
So again – thanks for opening my mind and keep on doing this great work!
Regards,
Karl
All I can say is wow! Karl, you did the really hard work of tackling and taming a 400 KLOC legacy monster. Kudos to you and your team.
I would like to comment on a few points…
TypeMock vs. RhinoMocks
Karl’s team tried using TypeMock to, presumably, introduce unit tests into their code. Unfortunately TypeMock too easily takes you down the wrong path. TypeMock uses the Profiling API and allows you to mock anything in the .NET Framework. Want to mock out System.String? Go right ahead. Unfortunately mocking out framework types does not get you any closer to your real goal, which is to lower the coupling and break dependencies in your system. There has been much discussion of whether TypeMock is too powerful. I wouldn’t say that it’s too powerful, but more that it encourages you to do things that aren’t necessarily a good idea – like mocking types in the .NET Framework.
RhinoMocks uses Dynamic Proxy 2 to perform its magic. In order to generate a proxy, you must mock out interfaces or abstract classes (with virtual methods). This forces you to confront your coupling and dependencies head-on. You could do the same thing with TypeMock, but it takes discipline. RhinoMocks forces you to improve your design in order to make it testable.
If you’re wondering how to retrofit unit tests into an existing code base safely, I would highly recommend checking out Michael Feather’s Working Effectively with Legacy Code mentioned above. Oren Eini (aka Ayende Rahien), the creator of RhinoMocks, wrote RhinoMocks after being inspired by this book.
Singletons and the Single Dev
As Karl mentioned, they originally had a lot of singleton managers. The code probably looked something like this:
CustomerManager.Instance.Save(customer);
The singleton is the most-overused Gang of Four (GoF) pattern in existence, probably because it is so easy to implement. It also leads you down the dangerous path of essentially global variables, which OOP tried to get rid of. Rather than a global string variable, we now have a static instance to which everyone couples – the “Instance”. (Hey, don’t feel bad. I once overused singletons too! Live and learn.)
Why is this bad? Because any class that uses the CustomerManager is now tightly coupled to CustomerManager. And as Karl found out, when you’ve got 50 managers, they quickly become coupled to every other manager. If you try to test any small part of your system, you end up pulling in a large fraction of the 50 managers and likely they bring along most of the rest of the system. You end up with a tangled morass of virtually untestable code. Your codebase ends up being relatively fragile because a change in one manager often ripples to every other part of the system. So making small localized changes is difficult.
The solution is to move toward interfaces and dependency injection. I’ll be writing more about this in the future, but for now, go back and read this for a refresher. Once you have dependency injection in place with unit tests and mocks, you can introduce an Inversion of Control (IoC) framework and take things to the next level. Which brings us to…
Inversion of Control (IoC) Containers
Karl and his team chose to use Spring.NET, which is a good IoC framework. There are many options for Inversion of Control containers, including Spring.NET, Castle Windsor, and StructureMap. I have personally been working with Castle Windsor and am pleased with the functionality and integration that it offers. I would encourage readers to investigate all three and make a decision for themselves. I’ve got some blogposts and screencasts coming in the next few months on the importance of IoC and how to get started with it.
VstsUnit vs. NUnit
Karl mentions using VstsUnit (aka mstest.exe) for unit testing. Even though I wrote the VstsUnit Plugin for ReSharper, I would personally recommend using NUnit or MbUnit as both are more TDD-friendly than VstsUnit. Many improvements are being made to VstsUnit in the VS 2008 release, which will make it more TDD-friendly, but for now, stick with NUnit or MbUnit.
Code Coverage
For code coverage, I would recommend using NCover and Grant Drake’s NCoverExplorer combined with NUnit or MbUnit. NCoverExplorer provides nice UI navigation of NCover results. If you want easy integration between all these tools, I would recommend checking out Jamie Cansdale’s excellent TestDriven.NET add-in for Visual Studio.
ReSharper
What can I say? I love JetBrain’s ReSharper. If you haven’t seen it before, it’s an add-in for Visual Studio geared toward refactoring and TDD. Doing TDD without ReSharper installed is like coding Windows Forms in Notepad. It can be done, but it’s painful. To give you another analogy… Vanilla Visual Studio is good for production line assembly of code. ReSharper turns you into an artist sculpting code to be aesthetically beautiful. JetBrain’s tagline is “Develop with pleasure!” and ReSharper definitely lives up to that. If you haven’t checked it out, I would definitely recommend downloading it and checking it out.
Conclusion
Karl, I am so glad that my session inspired you. I am delighted that you took the initiative to tame a 400 KLOC monster into something that’s a pleasure to work with. Thanks for taking the time to write and making my day. That’s why I do what I do.