Improve your .NET code quality with NDepend

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

Value Objects A Tool for Self-Documented Code and Fewer Errors-01

Value Objects: A Tool for Self-Documented Code and Fewer Errors

Have you ever heard of value objects? I bet you have. Even though they’re talked about a lot less than I’d like, they’re still talked about enough that many developers have at least some passing familiarity with the term.

“Passing familiarity” isn’t good enough, though. So that’s what we’re fixing with this post. Today you’re going to learn what value objects are and how you, as a C# developer, can harness their power to make your code clearer, self-documenting, and less error-prone.

What Are Value Objects?

Value objects are one of the building blocks of domain-driven design, as proposed by Eric Evans on his seminal book Domain-Driven Design: Tackling Complexity in the Heart of Software.

Simply put, a value object is an object that represents a value. And I’m aware that sounds excruciatingly obvious and even boring when said this way. So, what’s all the fuss about it?

Continue reading Value Objects: A Tool for Self-Documented Code and Fewer Errors

Delegation as a developer Building the Next You

Delegation As a Developer: Building the Next You

“I love me some me!”  

Yes, there’s more than just an ounce of truth in that statement said by the great NFL receiver Terrell Owens.  Terrell loved some Terrell. In fact, if you knew one thing about the man, it’s that he loved himself. Most of us can hide it a little bit better than Terrell, but we’ve all been guilty of this sentiment at one time or another. We suffer from pride and do our best to counteract it with humility.  I’ll admit it—as a software developer, after I write a solid, beautiful piece of code, my pride gets a first-class ride on the ego train!

Why start an article about delegation with an eccentric statement about ego?  The truth is we’re all good at what we do.  We spend time honing our craft, studying our industry, and trying to better ourselves.  Eventually, through experience, we’re able to understand and execute on things that less senior developers just can’t.  Our peers and managers recognize this, so we begin the journey to the role of leader.  And that leadership role doesn’t just entail learning all of the new frameworks and extensions. It also means learning a new set of skills.  One of those skills is delegation.

The Quandary of Leadership

When I was first recognized as a leader on my team, the recognition was extremely rewarding.  I had worked hard as a developer on this team and naturally stepped into some very minor leadership roles along the way.  I was honored to be selected as the team lead, and soon the team was coming to me for direction while the boss was looking to me for updates on how the team was performing.  

Rather quickly, I found myself thrust into a lonely middle ground. Management expected me to keep my team on task, and I was also trying to meet my standard deliverables.  It was obvious that keeping the team running and efficient required some new skills, and these skills weren’t the kind of things I could obtain by entering commands on my keyboard and getting results.  

These were skills that required—gulp—talking to people and finding out what makes them tick. Oh, and guess what? These skills had no tangible output. There was no rewarding pull request with a peer review after finding out that Jack needs a little more help with CSS and could use some training there.  There was no successful build notification after discovering that Diane had some issues going on at home and it was starting to impact her work output.  While I spent time building relationships with my team members, guess what I was NOT doing?  Producing!

All my career to this point was spent producing. Talking and relationships were a side gig that never got much attention, but they unknowingly got me much further along in my career than I like to admit.  I always liked to think that I would be promoted on technical merit, but it seems that managers are always looking for the next manager, and the technical merit is just expected.  

This transition from producer to leader presented me with a new skill set to learn and start putting into practice.  

Continue reading Delegation As a Developer: Building the Next You

Following the Software Architecture Career Path

Following the Software Architecture Career Path

I can recall a certain day in my career with remarkable clarity.  I say remarkable because this happened well over a decade ago, when I was a relatively fresh-faced software engineer.  My manager had called me in for a chat — quarterly review or some such. He said something that stopped me in my tracks.

“Do you want to follow the technical track or the management track in your career?”

Yikes!  I remember panicking.  On an otherwise unremarkable morning, I had unexpectedly come to a crossroads in my career.  Did I want the organizational clout and higher paychecks of management?  Or would I stick with the technical stuff that I so loved?

Of course, this turned out to be a comical overreaction on my part.  My answer didn’t, in any way, bind me for life.  And the whole thing was something of a false dichotomy anyway.  But it did get me thinking about what I would later regard as the software architecture career path.

The Software Architecture Career Path

I certainly wasn’t alone in my confusion over what becomes of programmers as they advance in their careers.  Some continue programming indefinitely, while others, eagerly or reluctantly, become managers and climb the corporate ladder.  But the software architecture career path splits the difference in a confusing variety of ways.

I challenge you to find a job title with as much variance as “software architect.”  The title itself has many different flavors:

  • Software architect
  • Application architect
  • Technical architect
  • Solutions architect
  • Enterprise architect

You get the idea.  But beyond the title variance, you also see wide diversity in responsibilities.  Some architects are literally just programmers with developmental job titles.  Others are highly technical, mentoring developers and approving of solutions.   Still others are more like project managers or business analysts.  It really varies.

And in this variance lies opportunity.  You can take the opportunity to find a role that suits you well in terms of responsibilities while also advancing your career.

For the rest of this post, I’m going to talk about how to take advantage of this opportunity.  What skills do you need, and how do you find success?  Bear in mind, I’m also going to describe the different flavors of architects in somewhat broad strokes.  The industry doesn’t have a great consensus on what each of these roles really means, so I’m basing this on my own experience.  Whatever you call the different flavors of architect is less important than what they do in their roles and whether the role might suit you.

Continue reading Following the Software Architecture Career Path

Cargo Cult Programming is the Art of Programming by Coincidence

Cargo Cult Programming Is the Art of Programming by Coincidence

I first learned about cargo cult programming a few years ago. I remember thinking back then, “What a strange name for a programming-related concept.”

If you share my past self’s astonishment, then today’s post is for you!

First, you’ll see what cargo cult programming is and why you should care. Then, we’re going to look at some practical examples, using the C# language. Finally, we’ll close with advice about what you can do, as a developer, to avoid falling into this trap.

Cargo Cult Programming: Doing Stuff Just Because

According to Wikipedia, “Cargo cult programming is a style of computer programming characterized by the ritual inclusion of code or program structures that serve no real purpose.”

In other words, it’s when a developer writes code without really understanding it. The developer may use a very trial-and-error approach—maybe copy and paste some code from somewhere else and then tweak it and test it until works, or sort of works. Then the developer will stop tweaking the code, for fear it will stop working. In the process, maybe they leave some lines of code that don’t do anything.

Or maybe they tried to use an idiom they picked up from another developer while failing to understand that the contexts are different and it’s useless in the current situation.

Finally, it might just be lack of education: maybe the developer has a poor mental model of how the tools they’re using really work.

Why is Cargo Cult Programming a Problem?

As Eric Lippert puts it, cargo cult programmers struggle to make meaningful changes to a program and end up using a trial-and-error approach since they don’t understand the inner workings of the code they’re about to change.

This is not so different from what the Pragmatic Bookshelf calls “programming by coincidence”:

Fred doesn’t know why the code is failing because he didn’t know why it worked in the first place. It seemed to work, given the limited “testing” that Fred did, but that was just a coincidence.

That single sentence pretty much sums it up for me: if you don’t know how or why your code works, neither will you understand what happened when it no longer works.

Continue reading Cargo Cult Programming Is the Art of Programming by Coincidence

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

L:ack of Cohesion of Methods: What Is This And Why Should You Care?

Lack of Cohesion of Methods: What Is This And Why Should You Care?

Lack of cohesion of methods (sometimes abbreviated LCOM) is one of those things that occurs fairly high up on the software hierarchy of needs.  What’s the “software hierarchy of needs?”  It’s a thing that I just made up, shamelessly copying Maslow’s Hierarchy of Needs.  (Though in a quick search to see how effectively I’ve planted this flag, I see that Scott Hanselman had the idea before me.  But mine looks a little different than his because I’m talking about the software, rather than the humans writing it.)

Anyway, here’s the hierarchy.

  • The cost of change stays relatively flat because the software is highly maintainable.
  • You can change it over time in response to evolving requirements and market realities.
  • It performs and behaves acceptably in production.
  • Technically, it fulfills the functional requirements and user value proposition.
  • It simply exists in production.

The software hierarchy of needs.

When it comes to something like lack of cohesion of methods, you’re talking about the top two categories.  Early in your career, you’ll tend to have worries like just wheezing past the finish line with a feature and like getting your code to do what you want it to.  With practice and time, you then start worrying about non-functional concerns and maintainability.  And when you start worrying about that, you should start paying attention to lack of cohesion of methods (among other things).

So for the rest of this post, I’ll focus on this one idea: lack of cohesion of methods.  What is it, how do you measure it, and why should you care?

Continue reading Lack of Cohesion of Methods: What Is This And Why Should You Care?

A Guide to Code Coverage Tools for C#

I promise that I’ll get to a treatment of code coverage tools in short order.  But first, I want to offer a quick disclaimer and warning.

If you’re here because you’re looking for a way to let management track the team’s code coverage progress, please stop and reconsider your actions.  I’ve written in the past about how code coverage should not be a management concern, and that holds as true now as ever.  Code coverage is a tool that developers should use — not something that should be done to them.  Full stop.

What Is Code Coverage and Why Do It?

Okay, with that out of the way, let’s talk extremely briefly about what code coverage is.  And take note that this is going to be a very simple explanation, glossing over the fact that there are actually (slightly) different ways to measure coverage.  But the short form is this.  Code coverage measurements tell you which lines of code your test suite executes and which lines it doesn’t.

Let’s look at the simplest imaginable example.  Here’s a pretty dubious implementation of division:

Let’s say this was the only method in our codebase.  Let’s also say that we wrote a single unit test, from which we invoked Divide(2, 1) and asserted that we should get back 2.  We would then have 67% code coverage.  Why?  Well, this test would cause the runtime to test the conditional and then to execute the return x/y statement, thus executing two-thirds of the method.  Absent any other tests, no automated test ever executes that last line.

So in the grossest of terms, that’s the “what.”  As for the “why,” developers can use code coverage analysis to see holes in their testing efforts.  For instance, code coverage analysis would tell us here, “Hey, you need to test the case where you pass a 0 to the method for y.”

What Are Code Coverage Tools?

What, then, are code coverage tools?  Well, as you can imagine, computing code coverage the way I just did would get pretty labor intensive.

So developers have done what developers do.  They’ve automated code coverage detection.  In just about any language and tech stack imaginable, you have ways to detect how thoroughly the unit test suite covers your codebase.  (Don’t confuse code coverage with an assessment of the quality of your tests, though.  It’s just a measure of whether or not the tests execute the code.)

The result is an array of tools to choose from that help you see how well your test suite covers your codebase.  And that can become somewhat confusing.  So today, I’ll take you through some of your options in the .NET ecosystem.

Continue reading A Guide to Code Coverage Tools for C#

C# 8.0 Features: A Glimpse of the Future

C# 8.0 Features: A Glimpse of the Future

It’s been almost 20 years since Microsoft released the first version of the C# language. From its inception—when some unjustly deemed it a mere Java copycat—until now, C# has had a remarkable evolution.

Nowadays, it’s frequently featured in both most used and most loved programming languages lists. You can use it to develop desktop, web, and mobile apps, and you can write code that will run in all the major operating systems. Or you can jump right onto the IOT bandwagon and write code to “smarten” your house. We live in interesting times to be a C# developer indeed.

If the present is already exciting, what about the future? Would it be possible for us to get a glimpse of what lies ahead for the language?

Of course it is. Microsoft has developed C# “in the open” for quite a while now. You can just take a look at the GitHub repo to read (and participate—why not?) in the discussions and proposals.

Today, we’ve selected three feature proposals for C# 8.0 to talk about here: extension everything, default implementations on interfaces, and nullable reference types.

Continue reading C# 8.0 Features: A Glimpse of the Future

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