Bootstraps
I have said before that I have no axe to grind against Microsoft, and I stand by that. They set out long ago to have a computer on every desktop and Microsoft software running on every computer. They have gone a long way towards achieving that goal, and in the process they have brought value to more people than I can count. Since their inception they have produced operating systems, languages, and development tools that have allowed people without computer science backgrounds to learn how to program computers to do actual stuff.
Now, granted, Microsoft has promoted development techniques that are much more in line with their own marketing plans than with sound design principles. But I cannot think of a time during Microsoft's existence when information about sound design principles was unavailable, or when Microsoft has ever actively sought to squelch that information. And I'm old.
I guess my point is this: Microsoft has produced some really nice tools, and it's up to me as a developer to use those tools wisely. But it's also up to me to find other tools and techniques that help me to be more effective as a developer. I feel a constant call to improve myself as a developer. I also feel a call to watch old Twilight Zone episodes on YouTube. But that first call is still there, and I respond to it too. I certainly benefit from my efforts to learn and grow, and my team benefits from my efforts as well. And ultimately, so does my company.
Many companies I have worked for have thought of their developers as commodities, particularly those who use Microsoft technologies. Approaching hiring, training, and project management practices from a commodity perspective has lead to commodity behavior in many developers: one MCP is as good as another; it Microsoft didn't invent it, it can't be good; we're all "human resources" rather than "personnel".
If you're in this situation, I have good news for you: you can take the red pill. You don't have to wait for your employer to educate you about your chosen profession. You can take the grass by the roots and educate yourself. There are tons of resources out there for you. One of those resources is a great little tool called NDepend.
A Real-World Example
The purpose of NDepend is to give you views into the actual structure of your software, regardless of how you intended to design it, that you would otherwise be unaware of. By blindly following the herd rather than thinking about long term design consequences, we often end up with some silly, expensive situations. NDepend can help us pinpoint the details of those situations so that we can address them.
A while back Chad Myers posted about the hazards of having WAAAAAAYYYYYY too many projects in a single solution file. I have wanted to write a post on NDepend for a while now. Others have already written better overviews than I could produce. And the NDepend tool itself ships with a whole host of how-to's in its documentation. Instead, I want to write about how we use NDepend at my office to address problems like those Chad describes, and that will probably take more than one post.
At my office we definitely suffer from the malady Chad describes. Our problem started innocently enough: years ago, we thought that we should package our API so granularly that we could deploy different pieces of it in an easy and atomic way. Well, it has proven not to be easy, but it is certainly atomic. And not in the good way. As we have added to our codebase over the years, we kept following the idea of many granular assemblies, but we somehow lost sight of the goal of easy deployment, and we ignored our need for maintainability. We now have a single solution file for assemblies meant to be shared among multiple applications that has nearly 150 projects in it. NEARLY 150!!! How do you even start to address a situation like that? Step 1 is admitting you have a problem. So first, we'll consolidate our assemblies, and manage our code with namespaces and project folders. But that's the subject of another post.
Step 2 is finding dead code and eliminating it, and that's the subject of this post. My problem here is that I'm under an NDA, so I can't show you details of the real situation, although I would love to. Instead here's a very trivial example to illuminate a couple of concepts: download. At work, I've got a (HUGE!) set of shared assemblies, and a couple of apps that consume those assemblies. So my example contains code meant to represent a situation like that: the CouplingExample assembly is the stand-in for the shared assemblies, and the CouplingExample.ConsoleApplication and CouplingExample.UnitTests assemblies represent the apps that depend on our shared assemblies.
NDepend ships with a SQL-like scripting language called Code Query Language (CQL). CQL allows you to write custom queries about the assemblies you're analyzing based on several different kinds of code metrics. NDepend ships with a bunch of pre-built CQL queries to give you a good starting place for cracking into your code. Since I'm trying to kill dead code, the metric I'm particularly interested in is afferent coupling, or actually the lack of it. For CouplingExample, I want to know which methods in the CouplingExample assembly have no methods that depend directly on them. I'm interested in non-public methods - it's a pretty safe bet that any non-publicly accessible method in my assembly that has nothing depending on it is dead code, and can therefore be eliminated. But I'm also interested in public methods with an afferent coupling of zero. Since I know all the applications that consume my shared assemblies, I can include those app assemblies in the same NDepend project. I can narrow down the scope of my CQL query just by including an ASSEMBLIES list. I swiped NDepend's canned "Potentially unused methods" query, and bent it to my own bidding like so:
1: // <Name>Potentially unused shared assembly methods</Name>
2: WARN IF Count > 0 IN SELECT TOP 10 METHODS FROM ASSEMBLIES "CouplingExample" WHERE
3: MethodCa == 0 AND // Ca=0 -> No Afferent Coupling -> The method is not used in the context of this application.
4: !IsEntryPoint AND // Main() method is not used by-design.
5: !IsExplicitInterfaceImpl AND // The IL code never explicitely calls explicit interface methods implementation.
6: !IsClassConstructor AND // The IL code never explicitely calls class constructors.
7: !IsFinalizer // The IL code never explicitely calls finalizers.
Share this post : |