Reading Time: 5 minutes

Using Shotgun to Find and Limit Indirect Dependencies

by Daniel Bray, posted 21/10/2021

 

On a well-run project, over time, novelty tends to zero, and all good software, if it’s being used at all, will eventually go into maintenance.

At the beginning of a project, when designs are still coming together, commits are likely to be in many different components of the application. As the application moves into maintenance, then – if the SOLID principles have been applied – one would expect commits to impact on smaller and smaller sections of the codebase. This would indicate that the changes for issue fixes and the like don’t require lots of small changes all over the codebase.

Shotgun (and its related gradle plugin) is a new tool that can identify overly complex and interdependent elements of your code base that other tools can’t.

What does this give me that a cyclomatic complexity rating doesn’t?

We already have tools for calculating the complexity of code (e.g Cyclomatic Complexity) but they rely on finding the elements of code that talk directly to each other. Shotgun, however, reports on what elements of code are updated at the same time, measuring how coherent your commits are within a code hierarchy.

For example, if you had an event-based architecture, where services interact through a queue, then you may find that changing one service might require changes in the events being sent, and so on down to the receiving services. A cyclomatic complexity rating wouldn’t take account of this, since the services don’t directly interact with each other. Shotgun, however, will report that when these elements are updated at the same time over and over again.

The idea is that – if we’ve been doing our job right – then over time the complexity of commits is getting smaller. If it’s not, then it’s likely that “small” commits are touching too many files because the code is overly interdependent.

The report looks like this:

 

With:

  • A heatmap showing the complexity of each day’s commits.
  • A list of active commit sets: these are sets of files that are regularly committed in one go.
    These file sets have a high inter-dependence.
  • A list of active files: these are single files that are being updated a lot.

You can also click on any given day and view the details of the commits for that day.

What do I do with this information?

Shotgun tells you if every change you’re making is a big change in lots of different components.

At the beginning of a project this is normal – you’re just figuring things out. If, after a while, most commits are touching elements all over the codebase, then you need to start thinking about refactoring.

  • The active commit sets will point to groups of elements that are committed together a lot. These are highly coherent. If the sets are too big then there’s probably a need to abstract some common behaviours to reduce the interdependence.
  • It’s also worth checking the larger commits for the same issues.

How is shotgun coherency calculated?

The aim is to derive a score for each commit, that is lower if:

  • The commits are limited to small numbers of files.
  • The commits are limited to files in the same package.
  • The commits are limited to files in the same package hierarchy.

The actual score is calculated as follows.

Imagine a commit like this:

The process is a simple:

  • Ignore any merge commits – we don’t want to risk double-counting, or be at the mercy of whether or not the merge was fast-forwarded.
  • Ignore any files that were deleted – removing code doesn’t add to the complexity of the application.
  • Split the files up into different source sets, e.g.
    • src/main/java
    • srs/main/resources
    • src/test/java
    • srs/test/resources
  • Build a set of simple directed graphs, where the vertices are the directories and files in the commit.
  • Prune out the roots of these graphs so we’re left with only the common root of the commit. In this example we’d be removing “com.sonalake
  • Finally, add up all the edges of the graphs.

This is the shotgun coherency score.

  • Where there are multiple commits on a given day, then it is the median score that is used for that day.

Some examples:

DescriptionScore
A single file commit

1
Two files in the same directory

2
Two files in the same hierarchy

3
Two files in parallel directories

4

How can I use it?

There is a basic library that comes with a command line: shotgun; where you can find full details of the configuration parameters; but in short you can define things like:

  • The different source sets.
  • How small should commits get before they are not included in the home page.
  • The size of the heatmap buckets.

The easiest way to use the tool in a project is to use the shotgun-gradle-plugin

Just drop this in as a plugin in your gradle configuration (full details here):

plugins {
    id 'com.sonalake.shotgun-gradle-plugin' version "1.0.0"
}

And configure it appropriately for your project:

shotgun {
  inputDirectory            = "$projectDir"
  outputFile                = ".shotgun/report.htm"
  sourceSets                = ["src/main/java",
                                "src/main/resources",
                                "src/main/webapp",
                                "src/test/java",
                                "src/test/resources"]
  minimumCommitInterest     =   3
  topCommitValueForFileSets =   10
  topCommitValueForFiles    =   40
  legendLevels              =   [10, 20, 30, 50, 80, 120]
}

Why is it called shotgun?

The purpose of this app is to spot when changes are consistently being applied in a scattergun approach over the entire codebase.

Also because of this.

Can I contribute to this project?

We hope this tool is useful to everyone, so we made it public, along with some other tools, libraries and examples, in our Sonalake github project.

If you think there are improvements to make, please fork the project and submit them, and we’d be delighted to review and merge them.