A blog by Gary Bernhardt, Creator & Destroyer of Software

Measure the Goodness of Your Code

06 Feb 2007

Because I want to keep my code short, I like to see statistics on my sandbox before committing to subversion. At first, I just did things like svn diff | grep '^+' | wc -l, but that got old fast. So, I wrote a little script called gn (for "goodness") that computes some simple statistics. Here's what it says about my sandbox right now:

grbmbp:~/trunk grb$ gn
591 lines of diff
129 lines added
186 lines removed
-57 lines net change

I've added 129 lines and removed 186, which is a net change of -57. Any line that's simply replaced will result in one line removed and one added, for a net change of 0. The implication here is that the more negative your "net change" is, the better. (DISCLAIMER: Please don't take this literally and post angry comments.)

Sometimes I want to know the goodness of a bunch of related changes, so the script can also take any arguments that "svn diff" can take. It just passes them on, so you can compute statistics across revisions, etc.:

grbmbp:~/trunk grb$ gn -r420:451 
4246 lines of diff
1099 lines added
1627 lines removed
-528 lines net change

As you can see, I've been killing a lot of code recently. The script is below, in case you want to try it for yourself. I've only tested it on OS X; YMMV.

#!/usr/bin/python
import sys, os, re

svn_args = ' '.join(sys.argv[1:])
pipe = os.popen('svn diff %s' % svn_args)
diff_lines = pipe.readlines()

# Added lines start with '+' (but not '+++', because that marks a
# new file).The same goes for removed lines, except '-' instead of
# '+'.
added_lines = [line for line in diff_lines
    if line.startswith('+') and not line.startswith('+++')]
removed_lines = [line for line in diff_lines
    if line.startswith('-') and not line.startswith('---')]

print '%i lines of diff' % len(diff_lines)
print '%i lines added' % len(added_lines)
print '%i lines removed' % len(removed_lines)
print '%+i lines net change' % (len(added_lines) -
                                len(removed_lines))