NDepend

Improve your .NET code quality with NDepend

extension_methods_decline_of_traditional_oop

Extension Methods and the Decline of Traditional OOP

A bunch of years ago, I wrote a post on my own personal blog titled, “Why I Don’t Like C# Extension Methods.”  Over the years, I’ve updated that post a bit, but mostly left it intact for posterity I guess.  But if you ask me how that post has aged, I’d respond with “it’s complicated.”

I didn’t like extension methods back then.  I do like them now, provided they aren’t abused.

So, what gives?  Am I just a hypocrite?  Or is it a matter of growth?

Well, none of the above, I’d argue.  Instead, the programming landscape has changed and evolved.  And I’d submit that extension methods were actually ahead of their time when the language authors came up with the concept.  But now they’re hitting their stride.

Don’t take my word for it, though.  This is another one of my research posts, whose previous ranks have included how functional approaches affect codebases, what unit tests do to code, and a look at singletons.  Today, we’ll take a look at extension methods and what properties of codebases they correlate with.

Continue reading Extension Methods and the Decline of Traditional OOP

autofac_detailed_look_under_hood

Autofac: A Detailed Look Under the Hood at Its Code Quality

In case you haven’t seen it, I’ve been diving ever-further down the code research rabbit hole.  Today it brings me to the Autofac codebase (found here).  But to understand how I’ve arrived here, consider some of the questions that I’ve answered previously.

With each of these studies (and the rest of the series), the research has become more sophisticated.  This is because I’ve started working with folks that have a data science and machine learning background.  So I’m now having fun assembling measurable codebase properties into broad categories and seeing how my corpus of 500+ open source codebase resolves into patterns.

And that brings me to Autofac.

It caught my eye by ranking in the 85th percentile for what I’m calling “testable” codebases and in the 58th for “readability.”  And in general, it scores decently to well in just about every measure.  This gave me an idea for a post in the same sort of vein as the one I did about Moq.  Let’s do a deep dive into Autofac’s codebase using NDepend.

Continue reading Autofac: A Detailed Look Under the Hood at Its Code Quality

log4net vs NLog A Comparison of How They Affect Codebases

Log4net vs NLog: A Comparison of How They Affect Codebases

Ah, the old “versus” Google search.  Invariably, you’re in the research stage of some decision when you type this word into a search engine.  Probably not something like Coke vs Pepsi.  Maybe “C# vs Java for enterprise projects” or “angular vs react.”  Or if you landed here, perhaps you’re looking at “log4net vs NLog.”

With a search like this, you expect a certain standard script.  The writer should describe each one anecdotally, perhaps with a history.  Then comes the matrix with a list of features and checks and exes for each one, followed by a sober list of strengths and weaknesses.  Then, with a flourish, I should finish with a soggy conclusion that it really depends on your needs, but I maybe kinda sorta like one better.

I’m not going to do any of that. Continue reading Log4net vs NLog: A Comparison of How They Affect Codebases

Moq a Detailed Look at its Code Quality

Moq: A Detailed Look at Its Code Quality

In case you haven’t seen it, I’ve been doing a series of research-oriented posts for this blog.  This is going to be in the same vein but focused on the Moq codebase instead of focusing on hundreds of codebases.  Why Moq?  Well, I’ll get to that in a moment.

I started this by making a set of observations relating unit test prevalence to properties of clean code.  That generated considerable buzz, so I did some more studies in that vein, refining the methodology and adding codebases as we went.  By the end of the series, we’d grown the sample size to 500 codebases and started doing actual regression analysis.

Since then, we’ve enlisted the help of someone who specializes in data analysis to do some PCA with the data, which far outstrips my background studying data.  We brought this to bear in studying the effects of functional-style programming on codebases and also on categorizing codebases according to simple vs. complex and monolithic vs. decoupled, in addition to functional vs. OOP.  Doing this across more than 500 C# codebases has produced a wealth of information.

Okay, But How Does Moq Fit in?

I give you all this backstory in case you want to read about it but also to explain that I’ve looked at these codebases en masse.  With hundreds of codebases and millions of lines of code, I’m not going in and poking around to see if the code looks clean.  I’m using NDepend to perform large-scale robo-analysis.

And, while that’s been great, I started to want to see just how these categorizations stacked up.  So I started scrolling through the summary data and Moq jumped out at me.

First of all, I know Moq pretty well from using it over the years, and secondly, it had stood out when looking at the rate of unit test methods in the codebase.  Nearly half of its methods are test methods, which seems reasonable for a tool designed to help you write unit tests.  Combine that with these stats in the PCA:

So as a quick interpretation, Moq counts as reasonably non-monolithic, very simple, and very functional.  Add to that the high degree of unit testing, and I figured we’d have a codebase that was a joy to look at.  So I popped open the source code (as it was at the beginning of the year when I was grabbing codebases en masse) and analyzed it with NDepend, fresh off my excitement about using the new dark theme. I wanted to see if it was as much of a joy to look at as all of this data and statistical rigor indicated it would be.  And spoiler alert, it was the kind of codebase I’d feel right at home in.  Let’s take a tour.

NDepend Analysis, Test Coverage, and First Look

The first thing I wanted to look at was code coverage.  This was because I wanted a quick test case to see if my assumption that a high rate of test methods would correspond to high coverage.  And, it did.  Here’s a quick look at what happened after I imported coverage data and then ran analysis on the project.

Now I could see with Visual Studio’s coverage tool itself that Moq was sporting roughly a 90% test coverage.  But by importing the coverage data into NDepend, you can paint a much more compelling picture.

The heat map dominating most of the screen shows squares corresponding to methods in the codebase.  Larger squares are larger methods.  And the coloring indicates test coverage.  Over on the right, among the 2,152 methods in question, you can actually scroll through them in order of percent coverage and navigate to them if you want to take a look.

Taking a Look at the Dashboard and Technical Debt

So far, all systems go.  Most of the stats on the codebase looked good, coming in from the broad aggregates I have in a spreadsheet.  And then, following the same trend, Moq looks great in the IDE from a testing perspective.  But I looked at the dashboard and saw this:

Basic stats about the codebase lined up, and there’s the test coverage, hovering around 90%.  But a C for its tech debt rating?  10 critical rules violated?  This surprised me, given how rosy everything looked in the statistical analysis.  I drilled in to take a look.

And, sure enough, there be some dragons.  Huge types and overly complex methods are a problem.  Also in there are mutually dependent namespaces, which create coupling that hurts you as a codebase grows.  And you’ve got some hiding of base class methods and global state.  To get a sense for what I was looking at, I created another heat map. In this case, we’re looking at types.  The bigger the square, the more lines of code, and the closer to red, the more methods in that type.

This explains why the averages looked good in my spreadsheet but why NDepend has some objections and critical rules violated.  By and large, you have a lot of little green types, which is what you’d hope to see.  But there are some pretty hefty types in the mix, both in terms of lines of code to the type and number of methods to the type.

Some of these are gigantic unit test classes, while others are the API.  For those of you familiar with Moq, this should make sense to you.  Think of how many static methods you invoke on the Mock class. This illustrates the classic tradeoff between shooting for clean code guidelines about methods per type and such and between providing the API you want to furnish.

Drilling Into the Sources of Debt

NDepend has a view where you can drill into the technical debt per type or per method, and sort it accordingly.  I did that per type, and here’s what I saw:

No big surprise there.  The technical debt was coming disproportionately from these large types.  So I took advantage of another view to see where the debt was coming from, by rule violation. Here’s what that looked like:

Topping that list is a series of things that I would make it a priority to address in my own codebases, time permitting: types that are too big, types that have too many methods, namespace dependency cycles, and so on.  There is, however, one exception to what I would worry about as a top priority issue—visibility of nested types as a design choice.  That’s based on a Microsoft guideline and I don’t personally favor an approach where I use this a lot.  But it doesn’t bother me, either.

I see that the creators of Moq did that 395 times, so clearly they view it as a useful design choice.  This made me curious about what would happen to the tech debt grade if I disabled that particular rule.  So I did that, and the result was a somewhat greener and more pleasing grade of B:

What’s the Verdict With Moq?

I also spent some time scrolling through various classes and methods.  I didn’t want the entirety of the experience to be just a matter of data gathering since that’s what I’ve been doing for months.  And the verdict I have is that this particular data point fits in nicely with the aggregate.

Moq has excellent stats for most of what I’ve been looking at.  And, indeed, there are a lot of simple, functional methods in the codebase, almost all of which are thoroughly tested.  I would happily work in this codebase.

But NDepend is calling out real and important opportunities for improvement.  If I were working on this codebase, I’d make an effort to break some of those gigantic unit test classes into smaller ones that are more cohesive over their context.  I’d also take a hard look at the mutually dependent namespaces and either merge them or rework the dependency direction a bit.  And even I’d give some idle thought to how I might segment the large Mock class somehow into smaller chunks if that wound up making sense.

So the whole thing winds up being an interesting microcosm to me.  Moq is, as the stats would indicate, a pretty nice codebase.  But, as with just about any codebase, there’s plenty of room for improvement.  And having a tool to show you where to improve quickly is invaluable.

Functional C# Improves Your Design without Making Your Code Cleaner, Exactly

Functional C# Improves Your Design Without Making Your Code Cleaner, Exactly

Today I offer another one of the code research posts we’ve been doing.  If you want more backstory on the series, check out the last post in the series, where I give a brief history.  You should also read it if you want to understand both what I mean by functional C# and for details about its impact on codebases at the method level.

Quick editorial note: a couple of people have commented/sent notes asking about p-values.  I’ve been eliding those to keep the posts more narrative.  But as we’ve expanded the set of variables we capture, we’ve been looking only at dramatically lower p-values.  Those cited in this post, for instance, range between 0 and 0.04, with most being less than 0.01.

I’ll summarize the last functional study here, briefly.  Last time, I studied about 500 codebases to see what functional-style programming did to methods and types.  And the answer was that it made them less object-oriented, but it had surprisingly little influence on clean code statistics, like

  • Lines of code per method or type.
  • Cyclomatic complexity.
  • Parameters per method.
  • Method nesting depth.
  • Methods per type.

I expected that functional codebases would correlate with a reduction in all of those things.  In other words, I figured that functional-style programming would lead to smaller, clearer, more focused, and less complex methods.  It didn’t.

Undaunted, I vowed to take a broader look at the effect of functional programming on a wider array of concerns. And I did just that, with the help of my partner who runs the statistical regressions. Continue reading Functional C# Improves Your Design Without Making Your Code Cleaner, Exactly

Functional Programming Makes Your Code Not OO...And Thats It

Functional Programming Makes Your Code Not OO…And That’s It

Over the course of the fall and winter, I’ve been gaining momentum with code research posts.  Today, I bring that momentum to bear on the subject of functional programming.  Or at least, on functional style of programming in C# codebases.

But before I do that, let me provide a little background in case you haven’t caught the previous posts in the series.  It started with me doing automated static analysis on 100 codebases to see how singletons impact those codebases.  Next up, I used that data to look at how unit tests affect codebases.  That post generated a lot of buzz, so I enlisted a partner to help with statistical analysis and then boosted the codebase sample size up to 500.

At the end of that last post, I suggested some future topics of study.  Well, now I’ve picked one: functional programming.

What Is Functional Programming?

The idea with this post is mostly to report on findings, but I’d be remiss if I didn’t provide at least some background so that anyone reading has some context.  So first, let’s cover the topic of functional programming briefly.

Functional programming is one of the major programming paradigms.  Specifically, its calling card is that it disallows side effects.  In other words, it models the rules of math, in which the result of the function (or method) is purely a deterministic function of its inputs.

So, in pseudo-code, it looks like this:

This is a functional method.  But if you do something like this

or like this

then you’re out of the functional realm because you’re adding side effects.  These two modified versions of Add() each concern themselves with the world beyond processing the inputs to add.  (As an aside, you could “fix” this by passing the global variable or the _databasePlopper dependency to the method as a parameter.)

Now, take note of something because this matters to the rest of the post.  While C# (or any other object-oriented language) is not a functional language, per se, you can write functional methods in C#.

Continue reading Functional Programming Makes Your Code Not OO…And That’s It

Unit Tests Correlate With Desirable Codebase Properties

Unit Tests Correlate With Desirable Codebase Properties

Today, I give you the third post in a series about how unit tests affect codebases.

The first one wound up getting a lot of attention, which was fun.  In it, I presented some analysis I’d done of about 100 codebases.  I had formed hypotheses about how I thought unit tests would affect codebases, and then I tested those hypotheses.

In the second post, I incorporated a lot of the feedback that I had requested in the first post.  Specifically, I partnered with someone to do more rigorous statistical analysis on the raw data that I’d found.  The result was much more clarity about not only the correlations among code properties but also how much confidence we could have in those relationships.  Some had strong relationships while others were likely spurious.

In this post, though, I’m incorporating the single biggest piece of feedback.  I’m analyzing more codebases.

Analysis of 500 (ish) C# Codebases

Performing static analysis on and recording information about 500 codebases isn’t especially easy.  To facilitate this, I’ve done significant work automating ingestion of codebases:

  • Enabling autonomous batch operation
  • Logging which codebases fail and why
  • Building in redundancy against accidentally analyzing the same codebase twice.
  • Executing not just builds but also NuGet package restores and other build steps.

That’s been a big help, but there’s still the matter of finding these codebases.  To do that, I mined a handful of “awesome codebase” lists, like this one.  I pointed the analysis tool at something like 750 codebases, and it naturally filters out any that don’t compile or otherwise have trouble in the automated process.

This left me with 503 valid codebases.  That number came down to 495 once adjusted for codebases that, for whatever reason, didn’t have any (non-third party) methods or types or that were otherwise somehow trivial.

So the results here are the results of using NDepend for static analysis on 495 C# codebases.

Continue reading Unit Tests Correlate With Desirable Codebase Properties

The unit test effect study, refined

The Unit Test Effect Study, Refined

About a month ago, I wrote a post about how unit tests affect (and apparently don’t affect) codebases.  That post turned out to be quite popular, which is exciting.  You folks gave a lot of great feedback about where we might next take the study.  I’ve incorporated some of that feedback and have a followup on the unit test effect on codebases.

Summarized briefly, here are the high points of this second installment and refinement of the study:

  • Eliminating the “buckets” from the last time.
  • Introducing more statistical rigor.
  • Qualifying and refining conclusions from last time.

Also, for the purposes of this post, please keep in mind that non-incorporation of feedback is not a rejection of that feedback.  I plan to continue refinement but also to keep posting about progress.

Addressing Some of the Easier Questions and Requests

Before getting started, I’ll answer a few of the quicker-to-answer items that arose out of the comments.

Did your analysis count unit test methods when assessing cyclomatic complexity, etc.?

Yes.  It might be interesting to discount unit test methods and re-run analysis, and I may do that at some point.

Can you show the code you’re using?  Which codebases did you use?

The scraping/analysis tooling I’ve built using the NDepend API is something that I use in my consulting practice and is in a private repo.  As for the list of specific codebases, I’m thinking I’ll publish that following the larger sample size study.  In the most general terms, I’m going through pages like this that list (mostly) C# repos and using their links.

What about different/better categorization of unit test quality (test coverage, bolted on later vs. written throughout vs. demonstrably test driven)?  

This is definitely something I want to address, but the main barrier here is how non-trivial this is to assess from a data-gathering perspective.  So I will do this, but it will also take time.

Think of even just the anecdotally “easy” problem of determining TDD vs. non-TDD.  I approximated this by positing that test-driving will create a certain ratio of test methods to production methods since any production method will be preceded by a test method (notwithstanding future extract method refactorings).  We could, perhaps, do better by auditing source control history and looking for a certain commit cadence (modification to equal numbers of test/production classes, for instance).  But that’s hard, and it doesn’t account for situations with large batch commits, for instance.

The upshot is that it’s going to take some doing, but I think we collectively can figure it out.

Continue reading The Unit Test Effect Study, Refined

Unit testing doesn't affect codebases the way you think.

Unit Testing Doesn’t Affect Codebases the Way You Would Think

I’ve just wrapped up another study.  (The last one was about singletons, if you’re interested.) This time, I looked at unit testing and the impact it has on codebases.

It didn’t turn out the way I expected.

I’m willing to bet that it also won’t turn out that you expected these results.  It had a profound effect on certain unexpected codebase properties while having minimal effect on some that seem like no-brainers.  But I’ll get to the specifics of that shortly.

First, let me explain a bit about expectations and methodology.

Unit Testing: The Expected Effect on Code

Let’s be clear for a moment here.  I’m not talking about the expected effect of unit tests on outcomes. You might say, “We believe in unit testing because it reduces defects” or, “We unit test to document our design intentions,” and I didn’t (and probably can’t) measure those things.

When I talk about the effect of unit testing, I’m talking about the effect it has on the codebase itself.  Let’s consider a concrete example to make it clear what I mean.  You can’t (without a lot of chicanery) unit test private methods, and you can’t easily unit test internal methods.  This creates a natural incentive to make more methods public, so we might expect heavily unit-tested codebases to feature more public methods.

This actually turns out to be true.

The rate of public methods increases with increased prevalence of unit testing.

I’ll talk more about the axes later in the post.  But for now, check out the plot and the trend line.  More unit testing means a higher percentage of public methods.  Score one for that hypothesis!

With a win there, let’s think of some other hypotheses that seem plausible.  Methods with more “going on” tend to be harder to test.  So you’d expect relatively simple methods in a highly tested codebase.  To get specific, here was what I anticipated in heavily tested codebases:

I also had some thoughts about the impact on types:

  • More interfaces (this makes testing easier).
  • Less inheritance (makes testing harder).
  • More cohesion.
  • Fewer lines of code.
  • Fewer comments.

Continue reading Unit Testing Doesn’t Affect Codebases the Way You Would Think