In the Codeless Code, a creative blog on software engineering, there’s this scribe named Qi and he is a total badass. Qi is source control personified; a pragmatic storyteller that reasons about systems as the sum total of all that came before.
I wrote the
qi CLI tool (GitHub, PyPi) as a hat-tip to the author—and with his gracious permission for use of the name. The idea is to bring developers leverage in discussions with management through a noticeably less violent variant of Case 89.
qi is only a Python wrapper to
git that prints revision hashes at the end of fixed time intervals. This enables running an analysis at, say, the end of every week over the past two quarters to check if a team decision in January is having an intended effect in June.
This helps developers and managers accept the same facts under pressure. I once worked in a highly active JS shop with over 30 developers and over one hundred commits every day. We had Bamboo running builds and automated tests every so often to catch problematic changes, but it was hard to know if our changes this sprint followed a specific “direction” for system quality, whatever that meant. After I wrote and ran
qi, I discovered that the team was, on average, creating seven production code files for every one test suite. To make matters worse, roughly 20% of our combined JS and CSS went completely unused.
We all knew that these things happened, but once I had the numbers to show that matters were getting worse due to management pressure, we re-prioritized to allow more time to increase code coverage for critical tests. Qi knew that commit history could not just be read, but leveraged.
qi is useful when one of the following conditions apply to you:
- You want to gather facts and correlations to study the code’s history, review recent changes, or to prove a point.
- You have no way to monitor the state of a codebase in terms of metrics or correlations that are important to you or management.
qi is not useful under these conditions:
- Your project(s) are small or neglected because there is rarely a big picture to derive, let alone a desire to find one.
- Your team has a strong grasp on the system being maintained, or already performs sufficient analysis.
Consider the command
qi --every month --past "3 years".
The command does what it says. It will print revision hashes that mark the end of every month for the past three years, with some caveats:
qistarts from HEAD. The query against history will search 3 years prior to the commit date from HEAD, not from the time you ran the command.
- If HEAD is younger than 3 years, then the output will end at the commit marking the end of the first month of development.
- If HEAD is not even a month old, there will be no output.
Printing hashes is one thing, but we want to do something with this information. Since
qi is basically a time iterator, we can tell it to run a command at each commit. Note that
qi’s short options use the same letter that starts their long options.
qi -e month -p "3 years" -c "./analyze"
When you specify a command for
qi to run,
qi will check out each significant commit (detaching HEAD) and run the command against the codebase at that time.
If your analysis finishes or crashes,
qi will attempt to put HEAD back where it started. But if
qi crashes, HEAD will be left detached and you will need to run
git checkout to put it back yourself.
qi -c lets you model informational metrics over time, such as the proportion of dead code in the project and the current code coverage by tests. You decide what metrics matter, because
qi is not a reporting tool, it is one kind of iterator a reporting tool might use.
Let’s look at a couple of fun examples.
For trivia, if you run
qi -e year -p "12 years" -c "du -sh ." on Torvald’s Linux source, you will see the disk usage growth of the project over it’s time on GitHub.
[sage linux]$ qi -e year -p "12 years" -c "du -sh ." 2.8G . # 2017 (to date) 2.7G . # 2016 2.6G . # 2015 2.6G . # 2014 2.5G . # 2013 2.5G . # 2012 2.4G . # 2011 2.4G . # 2010 2.4G . # 2009 2.3G . # 2008 2.2G . # 2007 2.2G . # 2006 2.2G . # 2005
Another example is if you want React’s complexity report at the end of every year of its development. In this case you could use this setup:
$ git clone https://github.com/facebook/react.git && cd react $ qi --every year --past "3 years" --command "./analyze.sh"
#!/bin/bash TIME=$(git show -s --format=%ct HEAD) # Using https://www.npmjs.com/package/complexity-report cr -e -f json ./src > $TIME.json