Several months back, I was tasked with measuring the quality of code in my organization. Foolishly, I said, "No problem." I figured that Visual Studio has a built-in code metrics tool (Analyze -> Calculate Code Metrics) and that would be a fine place to start with. I was right, but also very wrong.
The Visual Studio calculates five primary metrics: Maintainability Index, Cyclomatic Complexity, Depth of Inheritance, Class Coupling, and Lines of Code. The first two are figured at the method level, the second at (primarily) the class level, and the last is a simple count. The first question any reasonable person should ask is "Which one do I look at first?" The first question any manager is going to ask is, "What one number tells me about the whole application?"
My answer to both, in a way, was "Maintainability Index." Why? Because each of the other numbers represent one element of quality while MI is a composite number that includes Cyclomatic Complexity. I'd be lying if I said no consideration was given to the fact that it was abstract enough that it's harder for some surly developer (I've been known to resemble that remark) to start arguing why a high coupling or inheritance is no big deal or how complex requirements are to blame for complex code. I should also note that I don't think there is one magic bullet metric that will tell you objectively how good a code base is. There are a ton of different metrics out there, and each one was created for a specific purpose in mind and has a pet theory behind it. When you've got a group of developers who aren't accustomed to measuring code quality, picking a 0-100 scale, non-controversial metric that can be easily generated by tools you already own really isn't a bad place to start.
That sort of answers the question a developer would ask, but what about the management question; how do you dashboard this stuff when Visual Studio doesn't roll up the numbers to the solution level? Since VS does roll up the MI to the project level, I thought I could just figure out what sort of weighting Microsoft used to roll method scores up to the class level and then to the namespace and project levels.
I was a bit surprised by the answer: there is no weighting. That means that a class with one 1300 line method (which will score a 0 MI) and one empty constructor (which will score a 100 MI) will have an overall MI of a respectable 50. Throw in a couple of DTOs that are nothing more than getters and setters (which tend to score 95 or better) and the project ends up looking really, really healthy. The next poor bastard who has to work on the application is probably not going to be singing the praises of its maintainability, though. For the record, that 1300 line method isn't a hypothetical, either.
So, what does one do with that? Well, I decided to weight the average by the Lines of Code per method. For our above example, the formula for the class's MI becomes ((1300 * 0) + (1 * 100))/1301 = .077, rounded to 0. Sounds about right. Continue the pattern for namespace, project, solution, and even multi-solution application MI scores. This can be done relatively easily by using the "export to Excel" button and running a quick formula against the data.
On the short list of follow-up questions would be, "How do I improve my application's score?" That's an answer for another time, though.
I recently finished reading the book Brownfield Application Development in .NET by Kyle Baley and Donald Belcham. The book is available from Manning. First off, let me say that I'm a huge fan of Manning as a publisher. I've found their books to be top-quality, over all. As a Kindle owner, I also appreciate getting an ebook copy along with the dead tree copy. I find ebooks to be much more convenient to read, but hard-copies are easier to reference.
The book covers, surprisingly enough, working with brownfield applications. Which is well and good, if that term has meaning to you. It didn't for me. Without retreading a chunk of the first chapter, the authors break code bases into three broad categories: greenfield, brownfield, and legacy. Greenfield is, essentially, new development that hasn't had time to rust and is (hopefully) being approached with some discipline. Legacy applications are those that are more or less stable and functional, that do not expect to see a lot of work done to them, and are more likely to be replaced than reworked.
Brownfield code is the gray (brown?) area between the two and the authors argue, quite effectively, that it is the most likely state for an application to be in. Brownfield code has, in some way, been allowed to tarnish around the edges and can be difficult to work with. Although I hadn't realized it, most of the code I've worked on has been brownfield. Sometimes, there's talk of scrapping and starting over. Sometimes, the team dismisses increased discipline as ivory tower nonsense. And, sometimes, I've been the ignorant culprit vexing my future self.
The book is broken into two major sections, plus an introduction chapter and an appendix. The first section covers what the authors refer to as "The Ecosystem" which consists of version control, build and integration, testing, metrics, and defect management. The second section is on actually writing code for brownfield applications and discusses object-oriented principles, architecture, external dependencies, and, of course, how to deal with these when coming into an existing code base.
The ecosystem section is just shy of 140 pages long and brings some real meat to the matter. The focus on "pain points" immediately sets the tone as problem-solution, rather than academic. The authors also approach some of the topics from a different angle than some essays I've read on similar topics. For example, the chapter on automated testing is on just that -- automated testing. It's all well and good to criticize a project as conflating integration tests with unit tests, but it really doesn't make anyone's life better. The discussion on testing is more focused on the "right" level of testing for existing projects. Sometimes, an integration test is the best you can do without gutting a section of functional code. Even if you can sell other developers and/or management on doing so, it doesn't actually provide benefit to your customers to rewrite code that works. This isn't to say the authors encourage sloppy coding. Far from it. Just that they point out the wisdom of ignoring the sleeping bear until after you deal with the snarling wolf.
The other sections take a similarly real-world, workable approach to the pain points they address. As the section moves from technical solutions like version control and continuous integration (CI) to the softer, process issues of metrics and defect tracking, the authors begin to gently suggest moving toward a zero defect count. While that really sounds like an unreasonable goal for a lot of ongoing projects, it's quite apparent that the authors have first-hand experience with taming some gruesome projects. The suggestions are grounded and workable, and the difficulty of some situations is explicitly acknowledged.
I have to admit that I started getting bored by the end of the ecosystem section. No matter how valuable I think a good project manager or business analyst is to a successful ALM, at the end of the day, I'm a gear-head. Also, while I agreed with a lot of the ecosystem ideas, in theory, I didn't necessarily feel that a lot of the single-developer projects that I'm often involved in really needed that level of rigor. It's only after reading the sidebars and commentary in the coding section that I had the context for the arguments made in favor of a strong ecosystem supporting the development process. That isn't to say that I didn't support good product management -- indeed, I've probably pushed too hard, on occasion, for a strong ALM outside of just development. This book gave me deeper insight into why some corners shouldn't be cut and how damaging certain sins of omission can be.
The code section, though, kept me engaged for its entirety. Many technical books can be used as reference material from day one. The authors were clear, however, that this book is not one of these. The first chapter of the section (chapter seven, over all) addresses object oriented (OO) practices. I've read any number of definitions, discussions, and treatises on OO. None of the chapter was new to me, but it was a good review, and I'm of the opinion that it's good to review the foundations of what you do, from time to time, so I didn't mind.
The remainder of the book is really just about how to apply OOP to existing code -- and, just because all your code exists in classes does not mean that it's object oriented. That topic has the potential to be extremely condescending, but the authors miraculously managed to never once make me feel like a dolt or that they were wagging their finger at me for my prior sins. Instead, they continue the "pain points" and problem-solution presentation to give concrete examples of how to apply some pretty academic-sounding ideas. That's a point worth emphasizing, as my experience with most OO discussions is that they stay in the academic realm. This book gives some very, very good explanations of why things like the Liskov Substitution Principle exist and why a corporate programmer should even care. Even if you know, with absolute certainty, that you'll never have to work on an existing code-base, I would recommend this book just for the clarity it provides on OOP.
This book goes beyond just theory, or even real-world application. It presents some methods for fixing problems that any developer can, and probably will, encounter in the wild. First, the authors address refactoring application layers and internal dependencies. Then, they take you through those layers from the UI to the data access layer and external dependencies. Finally, they come full circle to tie it all back to the overall process. By the time the book is done, you're left with a lot of ideas, but also a reasonable plan to begin to improve an existing project structure.
Throughout the book, it's apparent that the authors have their own preferred methodology (TDD and domain-driven design), as well as some preferred tools. The "Our .NET Toolbox" is something of a neon sign pointing to that latter point. They do not beat the reader over the head with anything resembling a "One True Way" mentality. Even for the most emphatic points, the tone is quite congenial and helpful. With some of the near-theological divides that exist within the tech community, I found this to be one of the more remarkable characteristics of the book. Although the authors favor tools that might be considered Alt.NET, there is no reason the advice and techniques given couldn't be quite successful in a pure Microsoft shop with Team Foundation Server. For that matter, even though the book specifically addresses .NET, it could be applied to a Java and Oracle shop, as well.