Years ago, I led a team of software developers. We owned an eclectic portfolio of software real estate. It included some Winforms, Webforms, MVC, and even a bit of WPF sprinkled into the mix. And, as with any eclectic neighborhood, the properties came in a variety of ages and states of repair.
Some of this code depended on a SQL Server database that had a, let’s just say, casual relationship with normalization. Predictably, this caused maintenance struggles. But, beyond that, it caused a credibility gap when we spoke to non-technical stakeholders. “What do you mean you can’t give a definitive answer to how many sales we made last year?” “Well,” I’d try to explain, “I can’t say for sure because the database doesn’t explicitly define the concept of a sale.”
Flummoxed by the mutual frustration, I tried something a bit different. Since I couldn’t easily explain the casual, implied relationships in the database, I decided to do a show and tell. First, I went out and found a static analyzer for database schema. Then, I brought in some representative stakeholders and said, “watch this.” With a flourish (okay, not really), I turned the analyzer loose on the schema.
While they didn’t grok my analogies, they the tens of thousands of warnings and errors made an impression. In fact, it sort of terrified them. But this did bridge the credibility gap and show them that we all had some work to do. Mission accomplished.
Static Analyzer Issues
I engaged in something of a relationship hack with my little ploy. You see, I know how this static analyzer would behave because I know how all of them tend to behave. They earn their keep by carpet bombing your codebase with violations and warnings. Out of the box, they overwhelm, and then they leave it to you to dial it back. Truly, you can take this behavior to the bank.
So I knew that this creaky database would trigger thousands upon thousands of violations. And then I just sat back waiting for the “magic” to happen.
I mention all of this to paint a picture of how static analyzers typically regard the concept of “issue.” All categories of severity and priority generally roll up into this catch-all term, and it then refers to the itemized list of everything. Your codebase has issues and it has lots of them. This is how the tool earns its mindshare and keep — by proving how much it can surface, and then doing so.
Thus you might define the concept simply as “all that stuff the static analyzer finds.”
NDepend’s Issue Philosophy
With the recent release of NDepend v2017, the issue concept gets a significant overhaul. It becomes much more of a first class citizen of the static analyzer world.
While I can’t speak for every analyzer out there, I know that many resemble the tool (whose name escapes me) that I pointed at the SQL Server schema. These tools have an issue paradigm that resembles a checklist. They exist until you fix them — cross them off the list. Typically, they’ll also have some rudimentary concept of priority… but that’s really it.
With NDepend, you can now do things like track them through time, weigh their impact on your application, think of them in terms of time and money, and customize them in various ways. Hidden among perhaps flashier features on the surface of this release, issue management can really shine for you. But, don’t worry — they still have the basics of prioritization and checking off.
First of all, consider a pretty obvious (once you already know about it) capability worth adding. I mean the ability to differentiate between new and existing issues.
Historically, this could prove surprisingly elusive. To anchor your perception, think of the most ubiquitous static analyzer of all: the compiler. You kick off a build, and the compiler tells you about warnings and errors. You make a few changes, and then compile again, and you get a different list of errors and warnings. Which are new, and which remain? Unless you took a screenshot from the last compile or you revert your changes and compare, no one will ever know.
With NDepend, you can baseline your current set of issues. This allows you to understand which you’ve newly introduced and which you already had.
Query and Group Issues
Once you’ve gotten used to differentiating issues as a function of time, you should next play with the idea of categorizing issues in ways that help you. In other words, you no longer find yourself at the mercy of a giant file dump from the analyzer.
Using NDepend’s query editing and exploring, you can now do some really cool stuff. For instance, you can right click on a class or method and “select issues on me and child code elements.” Let that sink in for a moment. Instead of picking a problem and searching through the codebase for it, you can flip the script. “Show me the problems for this thing and everything in it. Oh, and do it since the last time I analyzed.”
That really just scratches the surface, though. With the powerful underlying functionality of CQLinq and the fact that issues now exist as first class entities there, you can do a whole lot more when you customize.
Prioritize the Issues and Persist the Prioritization
Armed with this potential, you can sort and filter issues to your heart’s content. For instance, let’s say you do exactly as I’ve described above, and you select issues for a given type and any children.
Doing this opens the NDepend query editor, which should feel familiar. Check out the screenshot here to understand what I mean.
It displays this and the results below it (results not important for this particular discussion). If you look at the query, you can see what I mean about the concept of “issue” receiving first class treatment in CQLinq. You can actually find them, filter them, and sort them.
But look at what it says above. “The query is not persisted.” This means that you can persist it, the way you would with any custom CQLinq query you might write. With this capability, you can now create your own ways of filtering, sorting, and prioritizing the issues in your codebase.
Reason about Time, Money, and ROI
As much power as that capability gives you, I’d like to close with one I appreciate even more. Take a closer look at the CQLinq query, and notice that the results display the concepts debt, annual interest, and “breaking point.” With this, you get (out of the box) estimations for time to fix the issue, how much extra time it will cost left unfixed, and how long before you expect it to cause a defect.
Of course, you can also customize the underpinnings of these calculations, should you choose. You have an entire dashboard to customize the calculations of debt in both time and money, tweaking it until you have something that makes sense for your team.
But pull back from the details and appreciate how powerful a feature you have here. And appreciate how far this brings us from a checklist of issues dubbed “serious” or “cosmetic” or whatever. Now, you have the ability to customize, locate, filter, prioritize issues in your codebase. And you can all of that on a basis of the impact on the business in terms of time and money.
The humble static analyzer issue has certainly come a long way.