Jake Scruggs

writes ruby/wears crazy shirts

If you're going to be in the Chicago area on or about Saturday, September 20th, you can see me give my "Using Metrics to take a Hard Look at Your Code" talk at the Windy City Rails Conference. David Heinemeier Hansson, David Chelimsky, and Noel Rappin have been announced as speakers and I'll be joining them for a interesting day of Rails related goodness.

Here's the abstract:

It's an interesting fact of human nature that you can't do something every day and not secretly suspect that you're good at it. Which goes a long way toward explaining why everyone thinks they write fine code. To combat this self-delusion you can use metrics to take a hard look at your application. This talk will discuss the ways in which you can measure how good your Rails project really is. And how to fix the bad parts.

Using a daily metrics build (run every day by CruiseControl.rb) you can compile a 'hit-list' of the worst methods in your application. Then your team can spend some time every iteration trying to refactor these methods.
In my talk I will discuss 4 tools to identify problems:

* Code coverage with Rcov
* Cyclomatic complexity with Saikuro
* Flogging with Flog
* Using source control to figure which files change the most

All of which are pretty easy to use, especially if you use metric_fu -- a Rails plugin (blatant plug). Once you've figure out where the badness is, you must fight it. While showing examples, I'll be discussing:

* Why 90% (or even 100%) test coverage isn't a silver bullet against bugs.
* How Saikuro and Flog measure complexity and the advantages of each.
* When complexity is OK
* Why 'inject' sucks, except when it's pretty cool (see above point).
* Strategies for figuring out if your fancy metaprogramming is worth the extra complexity and what to do if it isn't.
* What is good enough coverage.
* Failing your build on poor metrics numbers.
* Why you should always remember Carlin's law:
"Have you ever noticed that anybody driving slower than you is an idiot,
and anyone going faster than you is a maniac?"

As I am clearly crazy, but hear me out. Say there's a Car object that uses single table inheritance and all the objects that descended from it are routed through the car method in the CarController. The CarController looks like this:




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class CarController < ApplicationController
def car
layout = nil
@scripts = []
@stylesheets = []
@car = Car.find(params[:id])
if @car.is_a?(Toyota)
layout = 'two_column'
@stylesheets << 'two_column_layout.css'
@stylesheets << 'two_column_theme.css'
elsif @car.is_a?(Hyundai)
layout = 'three_column'
@stylesheets << 'three_column_layout.css'
@stylesheets << 'three_column_theme.css'
@stylesheets << 'hyundai_theme.css'
@scripts << 'discount.js'
elsif @car.is_a?(Ford)
layout = 'ford'
@stylesheets << 'ford_layout.css'
@stylesheets << 'ford_theme.css'
@scripts << 'discount.js'
elsif @car.is_a?(Saturn)
layout = 'two_column'
@stylesheets << 'two_column_layout.css'
@stylesheets << 'two_column_theme.css'
@scripts << 'poll.js'
#
# 100 more lines like above
#
end
render :action => find_view, :layout => layout
end
end
So I'm not very happy about this and ultimately I'd like to refactor the app to use different controllers for different objects (crazy huh?), but that is a major undertaking as the whole app assumes this architecture. It occurred to me the other day that there's an in-between step. I could do something like this:




1
2
3
4
5
6
7
8
9
10
11
class Car < ActiveRecord::Base
def layout
'two_column'
end
def stylesheets
['two_column_layout.css', 'two_column_theme.css']
end
def scripts
[]
end
end
And then the Ford class would look like this:




1
2
3
4
5
6
7
8
9
10
11
class Ford < Car
def layout
'ford'
end
def stylesheets
['ford_layout.css', 'ford_theme.css']
end
def scripts
['discount.js']
end
end
So the CarController could then be shortened to this:




1
2
3
4
5
6
7
8
9
class CarController < ApplicationController
def car
@car = Car.find(params[:id])
@scripts = @car.scripts
@stylesheets = @car.stylesheets

render :action => find_view, :layout => @car.layout
end
end
Any model that needs to overwrite the default layout, scripts, or stylesheets can.

So should I take this step and put view information in the models, or should I just hold out for the big refactor into separate controllers? Keep in mind the big refactor is so big that it may never happen.

Aug 21, 2008

Perks vs. Salary

When I worked for ThoughtWorks I once got on a bus after work -- no ordinary bus, mind you, this was "Party Bus." On this "Party Bus" there were leather seats, flashing lights, a big screen TV with karaoke, and many bottles of Patron Silver and Grey Goose. We drank and sang like maniacs, then we got to the Casino where we ate a very fancy dinner (including some very pricey wine), did a little gambling, caused some trouble, and then back on the bus for more drinking and karaoke. The only thing I payed for was the gambling.

The next day I was pretty hung over but I still got to pair with some of the best programmers I've ever worked with.

I often think about this when people ask how ThoughtWorks is able to keep such excellent programmers when they could easily go somewhere else and make more money. If you took all that money that was spent on our night out, divided it by the 10 or so people that went, and added it to our salary, there's no way we would have valued it anywhere near as much. I now have a story I can tell for the rest of my life and that's worth something.

Which is why I was pretty happy when Obtiva (my new employer) took all of its employees and their wives, girlfriends, and children out for a 'booze cruise' (with good food too) on Lake Michigan during the Chicago Air and Water Show last weekend. Perks inspire loyalty and camaraderie far beyond their monetary value.

Not having to get on a plane twice a week is nice too.

I've been using Flog to measure the complexity of my Ruby code and I was wondering how others interpret Flog numbers. I'll go first with my opinions:


Score of Means
0-10 Awesome
11-20 Good enough
21-40 Might need refactoring
41-60 Possible to justify
61-100 Danger
100-200 Whoop, whoop, whoop
200 + Someone please think of the children

(note: these are scores for an individual method)
The 20 - 60 range is interesting to me. Sometimes the complexity of what you're doing will justify the score and other times the method is in desperate need of refactoring.

The highest Flog score I've seen in the wild was in the 300's. What's you're personal high and what do you think of my rankings?

In other news, I've redesigned the look of my blog so if you only see my post through a reader you might want to stop on by, take a look, and tell me what you think.

I had a problem recently with TextMate's svn integration and finding the solution taught me some interesting things about Subversion and TextMate that I'd thought I'd share:

First -- TextMate fails with a non-helpful error message if you don't cache svn passwords. For a long time I didn't even realize that my two problems were related. I had to type in my username and password for svn every-time (even though I wasn't using svn+ssh). And TextMate exploded when I tried to see the svn log of a file. Turns out one causes the other -- obvious in retrospect.

Second -- Subversion keeps passwords and configuration files inside a .subversion folder inside the user's home directory. Try looking for a strangely named file inside ~/.subversion/auth/svn.simple for cached passwords. And there's a config file at ~/.subversion/config If you want to stop this caching of passwords you can set:
store-passwords = no
inside this file.

Third -- Subversion will fail to cache silently if the permissions are not right on the folder where it attempts to save the username/password. This was the sticking point for me -- everything I tried failed and I wasn't getting any helpful error messages (and none from the command line svn client) but then I noticed that my ~/.subversion/auth/svn.simple directory had different permissions (and a different modified time) than the other folders next to it. One simple chmod command later and I was caching passwords and using TextMate's svn integration like nobody's business.

Fourth -- Yes I still use Subversion -- feel free to leave me a comment telling how much happier I'd be with git.

Resources:
Subversion and TextMate
Short version of Subversion's password caching
Longer version