You’re reading Signal v. Noise, a publication about the web by Basecamp since 1999. Happy !

Jamis

About Jamis

He makes marshmallows and bow ties, plays guitar, and researches dead people. Sometimes all at the same time.

The road to faster tests

Jamis
Jamis wrote this on 33 comments

A week ago, it took about 15 minutes to run all of Basecamp’s tests.

Now, if you adhere to the test-driven development (TDD) philosophy, you’ll know that tests are meant to be run often. Like, every few minutes, preferably. The tests are what give you confidence in your code, and the ability to refactor with impunity, not fearing that your changes are breaking existing features. They are the safety net for our daily tight-rope walk across our code base.

But if your test suite takes a quarter of an hour to run, that puts a severe damper on your willingness to run it often, or even at all. Those tests definitely don’t get run every few minutes. They might get run before you commit a change. More likely, they get run around the end of a development cycle, when the new work is preparing for deployment. Although they can still be marginally useful when run like that, they are a far cry from what they might be.

At the beginning of this latest development cycle, I begged leave to spend a few days digging into our test suite to see what could be done. Just about all of the programmers here have had a turn looking into the tests, but with other responsibilities it was hard to find enough time to do more than prove the hypothesis-du-jour wrong. I hoped that with some dedicated time I might be able to make some significant improvements.

Continued…

Making sense with Ruby's "unless"

Jamis
Jamis wrote this on 30 comments

Among the things that people new (and old!) to Ruby find delightful are the little things it does to make the language feel intuitive. Case in point: unless.

The unless keyword is just if in reverse. It’s a conditional statement that executes only if the condition is false, instead of true. This lets you write little gems like this:

  i += 1 unless i > 10

  unless person.present?
    puts "There's no such person"
  end

However, as with anything that gives you a little power, it can be abused. The following abomination is but a sample:

  unless !person.present? && !company.present?
    puts "do you even know what you're doing?"
  else
    puts "and now we're really confused"
  end

I do not doubt that there are people out there who can decipher the logic above without breaking a sweat. But for most folks, combining negations, multiple conditions, and (gasp!) else clauses with an “unless” statement makes for challenging reading. It also makes it far too easy to introduce bugs into the logic.

Some rules of thumb when using unless:

  1. Avoid using more than a single logical condition. unless foo? is fine. unless foo? && bar? is harder to parse.
  2. Avoid negation. “Unless” is already negative. Piling more on only makes it worse.
  3. Never, ever, ever use an else clause with an unless statement.

Not only will others thank you when they have to read your code, you’ll thank yourself when you have to return to that code a month or two down the road.

The fiddly thing about practice, whether at programming, exercising, playing the trumpet, drawing, or anything else, is that you rarely notice improvement between one session and the next. Don’t let it discourage you. Being consistent over the long haul is what is required. Eventually you’ll look back and realize you’ve climbed a mountain, one day at a time.

cryptic.png

I have to think (and experiment) every single time I want to decipher one of these keyboard “shortcuts”. Why is it that only the command key (⌘) actually has the symbol printed on the key itself? And what’s up with the symbol for the option key (⌥)?

When knowledge makes us hesitate

Jamis
Jamis wrote this on 19 comments

I’ve been fighting my way through Clausewitz’s On War, and came upon a passage in Book 1, Chapter 3. Boiled down to bullet points, here’s what he has to say about the effect of chance in war:

  • War is the province of chance.
  • Thus, situations are constantly changing beyond what you plan for.
  • If situations change sufficiently, you may need to come up with an entirely new plan.
  • A new plan requires new data, but you are often required to make a decision on the spot, before you have a chance to really analyze the new situation.
  • Usually, though, chance events only serve to make us hesitate, and do not completely change our plans.
  • Learning about some chance event has increased our knowledge of the situation.
  • However, this new knowledge has increased our uncertainty, instead of decreasing it.
  • Why? Because these chance events are always occurring, and make us feel constantly on the defensive.

I particularly loved the insight from those last three bullet points. How ironic, that we tend hesitate instead of move forward when presented with new information!

He then goes on to say that the solution is to cultivate (among other things) coup d’oeil, which is the ability to grasp a situation at a glance, and resolution, which he defines as a “moral corage”, or “courage in the face of responsibility” (as contrasted with courage in the face of physical danger).

In other words: to prevent analysis paralysis, just make a decision and move on. Planning is guessing, after all.

Don’t do this:

  <% if File.exists?(model.path) %>
    ...
  <% end %>

Do this instead:

  <% if model.file_exists? %>
    ...
  <% end %>

Finding creativity

Jamis
Jamis wrote this on 6 comments

Yip Harburg was a famous lyricist (“Over the Rainbow”, anyone?) who discovered himself during the hardest of times: the Great Depression.

I went into business… I thought I’d retire in a year or two. And a thing called Collapse, bango! socked everything out. 1929. All I had left was a pencil.

Luckly, I had a friend named Ira Gershwin, and he said to me, “You’ve got your pencil. Get your rhyming dictionary and go to work.” I did. There was nothing else to do…

I was relieved when the Crash came. I was released. Being in business was something I detested. When I found that I could sell a song or a poem, I became me, I became alive. Other people didn’t see it that way. They were throwing themselves out of windows.

Someone who lost money found that his life was gone. When I lost my possessions, I found my creativity. I felt I was being born for the first time. So for me the world became beautiful.

(From Hard Times: An Oral History of the Great Depression, by Studs Terkel)

Music collaboration via git?

Jamis
Jamis wrote this on 15 comments

I was digging through some old backups this week, and I stumbled upon a bunch of music that I had written twelve and thirteen years ago. No, I will not inflict it on you. Much of it is so embarrassingly bad I can only laugh, but there are a few pieces I thought would be worth archiving (for my own personal history’s sake). So I downloaded LilyPond and began transcribing them.

LilyPond is free, but it is not a graphical music notation editor. I’m not into music composition enough these days to desire to spend any money on it, so “free” is about all I’ll budget for it. What LilyPond does is take text files containing (essentially) gibberish, and emit a PDF with beautifully laid out music. It’s easy! All you have to do is learn to convert your music into gibberish.

The interesting thing about this, though, is that it essentially lets you convert music into source code. Once it is in a text format, you can treat it just like any other text file, including hosting it on GitHub (or similar). So I wondered: are there any projects out there, where people have worked on a musical composition as a community, using something like git or subversion to collaborate?

I’m sure I can’t be the first that this has occurred to. And while my own musical chops aren’t up to participating in a songwriting project, I would love to follow along, all from the comfort of my command-line!

Introducing the ProjectSearch Rails plugin:

Jamis
Jamis wrote this on 19 comments

Two years ago I was feeling some pain. I was trying to find a particular helper method in one of our applications, and full-text search across an entire project was not easy to do, by default. I was using TextMate at the time, which has a full-project search feature, but unless you took the time to actually configure your project just right, it would search everything (log files, Rails, etc) and that was painful. Furthermore, I was at the command-line a lot, and it wasn’t very fun to have to switch back to TextMate just to search the project.

What I typically did was just issue a suitably arcane Unix command via the command-line:

grep "def some_helper_function" app/helpers/*.rb

That’s not too bad, really, though it gets tedious when you do it often. What’s worse is when you want to find everywhere in your project that you reference that method:

find app lib -name '*.rb' \
  -o -name '*.rhtml' \
  -o -name '*.rjs' \
  -o -name '*.rxml' | \
  xargs grep some_helper_function

That’s where the pain begins to feel crippling.

So, about that time I created a utility script, called (unimaginatively) “find”, which essentially was a thin wrapper around find+xargs+grep. Instead of the above, it let me simply say:

script/find some_helper_function

However, I could scope the search, too, for narrower (and thus faster) searches:

script/find helper "def some_helper_function"

It turned out to be so handy that we’ve since copied it to nearly all of our projects. For two years I’ve relied on this script, to the point that when I started a new (personal) project this last week, I really felt the lack.

So I got permission from David to release it as a Rails plugin. I rewrote it a bit so it doesn’t rely on find, xargs, or grep (so it can be used on more than just Unix platforms), added some simple documentation, and posted it on GitHub.

Behold: the ProjectSearch plugin for Rails.

And if you happen to be a fellow Vimmer, you might find this vim script handy, which uses script/find to search in your project:

function! RailsScriptSearch(args)
  let l:savegrepprg = &grepprg
  let l:savegrepformat = &grepformat

  try
    set grepprg=script/find
    set grepformat=%f:%l:%m

    execute "grep " . a:args
  finally
    execute "set grepformat=" . l:savegrepformat
    execute "set grepprg=" . l:savegrepprg
  endtry
endfunction

" search with explicitly provided arguments
command! -n=? Rgrep :call RailsScriptSearch('<args>')

" search for the word under the cursor
map <leader>rg :silent call RailsScriptSearch(expand("<cword>"))<CR>:cc<CR>

" search for the method definition of the word under the cursor
map <leader>rd :silent call RailsScriptSearch(expand("'def .*<cword>'"))<CR>:cc<CR>

Enjoy!

Letting things go

Jamis
Jamis wrote this on 38 comments

Capistrano had been my baby since mid-2005. It was a fun project, and all the more gratifying because it was fairly popular as a deployment tool for Rails applications. But this last year I’d begun to notice something.

I wasn’t enjoying Capistrano as much as I used to.

It had become a chore to work on. It already did everything we at 37signals needed it to do, everything and more. I didn’t feel any need to extend it, and yet bug reports, feature requests, and patches still trickled in. I began to resent each issue that was raised. I began to resent Capistrano itself, because it stood between me and other projects that I wanted to participate in.

And yet, Capistrano was my baby. I couldn’t just leave it! How can you just walk away from something that you’ve put almost four years of blood and sweat into? Is it even possible?

Continued…