Archive for September 2007

Scourging your Ruby code with Flog

Flog is a tool build by Ryan Davis to analyze Ruby code complexity. It’s dead simple to run, and immediately provides useful metrics in the form of a “Flog score” per method. Don’t get too hung up on the actual values, but high outliers are prime candidates for refactoring.

I ran this on a couple recent projects I worked on, and it proved quite accurate in identifying the areas of code that seem to be in the most pain. Flog doesn’t replace hands-on code review, but it is still a helpful aid.

Below is a rake task I threw together for running Flog on a Rails project. Seems like a great candidate to install via Chris Wanstrath’s Sake, a system for handling Rake tasks that you want to be available from anywhere.

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
def flog(output, *directories)
  `find #{directories.join(" ")} -name \\*.rb|xargs flog > #{RAILS_ROOT}/tmp/flog/#{output}.txt`
end

desc "Flog models, controller, helpers and lib"
task :flog do
  flog "all", *%w[app/models app/controllers app/helpers lib]
end

namespace :flog do
  desc "Flog code in app/models"
  task :models do
    flog "models", "app/models"
  end
  
  desc "Flog code in app/controllers"  
  task :controllers do
    flog "controllers", "app/controllers"
  end
  
  desc "Flog code in app/helpers"  
  task :helpers do
    flog "helpers", "app/helpers"
  end
  
  desc "Flog code in lib"  
  task :lib do
    flog "lib", "lib"
  end
end

Modeling tip: Replace booleans with timestamps

Let’s say you’re building a blogging application and you’ve got a Post model. Posts should not show up on the public site until they are approved. Approval is done by clicking an “Approve” button next to the post in the admin area.

Your first inclination (and mine, up until recently) might be to implement this with a boolean column:

1
2
3
4
5
6
7
8
class Post < ActiveRecord::Base
  # assuming posts.approved_at is a boolean column

  def approve!
    self.approved = true
  end

end

That works just fine, and may be all you need. On the other hand, my experience has shown that storing a little bit more data is generally worth the tiny incurred complexity. For example:

1
2
3
4
5
6
7
8
9
10
11
12
class Post < ActiveRecord::Base
  # assuming posts.approved_at is a datetime column

  def approve!
    self.approved_at = Time.now
  end

  def approved?
    !approved_at.nil?
  end

end

This has proven extremely valuable for debugging and audit trail purposes, even if the extra timestamp data is never exposed in a user interface. Approved posts can still be easily queried using SQL by using a WHERE approved_at IS NOT NULL condition.

How much money is your slow build wasting?

Recently, I've noticed a trend of unnecessarily slow builds hurting development velocity. I fear that developers may be overlooking the fact that a build composed of great code with great coverage is only truly useful if it can be run quickly.

Slow builds hurt development velocity in two primary ways. First, the developers must wait while the build is running. Remember how compiling provided ample time for extra coffee breaks or checking email and RSS feeds? If running the build is filing that gap, you've got a problem.

The second impact is harder to quantify, but equally important. Slower builds get ran less often. That means an increased feedback loop between code modifications and confirmation of their correctness. Tightening that loop is a central tenet of TDD, and it is what allows programmers to efficiently write code with confidence that it works.

If your continuous integration build fails because someone didn't run all of the tests before checking in, you're seeing another side effect of this problem. Continuous Integration is there to catch integration issues, not serve as a substitute for running the build locally.

Below I've setup a simple calculator to estimate the money directly wasted by a slow build. Five day work weeks are assumed. On a recent project I roughly estimated a loss of at least $7,500 per week due to unnecessarily slow builds.

Full-time developers
Old build duration minutes
New build duration minutes
Development rate dollars per hour
Builds per developer per day

The slow ass build is wasting at least $0 per week. Ouch.

A little humor goes a long way

There’s something ironic about an error message that makes you laugh out loud.

It is what it is.

Monitor builds from your desktop with CCMenu

So let’s just say you’re being a good developer and you’re writing tests. They are have good coverage (because you’re writing them first), they run fast, and you’ve setup a continuous integration (CI) server (like CruiseControl.rb) to run the build on every checkin.

CI servers can generally send email notifications for build failures and fixes, but when I’m in heads down development I often don’t check my email for hours at a time. Hours is far too long for a build to sit broken, especially when there are many other developers working on the project.

CCMenu tightens that feedback loop so I get immediate build status information in my menu bar. It uses Growl for notifications and provides a dashboard view of each project and its detailed build status. That’s a big improvement, and will help hold me over for a bit longer while I convince my boss to get us a build-controlled traffic light in the office.