Two thoughts on enterprise software

September 1st, 2006

Thought #1: “enterprise software development” is that particular kind of software development where the process matters more than the results.

Thought #2: “enterprise software” it’s the same category that includes those billions of lines of COBOL code that used just two digits to store year values.

The price of a rescue

August 31st, 2006

I’ve been abusing Ruby exception handling lately. Instead of doing something like:

if user and user.group
  user.group.do_something
end

or a cleaner

user and user.group and user.group.do_something

I just do

user.group.do_something rescue nil

And let Ruby deal with the consequences of a missing link.

It was all nice and pretty, until I saw this little guy standing in my shoulder — dressed in white and with a halo over his head — telling me that laziness is wrong and that there would be a price to pay. After all, exception handling requires setting up flags and pointers and blocks and contexts and who knows what else. They must be expensive. And of course, there was another guy — red, with horns and a tail — calling the white guy an asshole and a coward.

In the end I decided to stop taking drugs, and to do my own research into the subject, instead of paying attention to my own hallucinations.

I wrote the simplest test cases, and used the ‘benchmark’ library to measure their performance:

require 'benchmark'

n = 500000
Benchmark.bm(7) do |x|
  x.report("plain") do
    for i in 1..n
      1.0/5.0
    end
  end

  x.report("safe") do
    for i in 1..n
      begin
        1.0/5.0
      rescue
        0.2
      end
    end
  end

  x.report("rescue") do
    for i in 1..n
      begin
        1.0/0.0
      rescue
        0.2
      end
    end
  end
end

It does half-a-million floating point divisions. The first scenario (”plain”) has no exception handling. The second one (”safe”) has exception handling, but never raises an exception. And the final scenario (”rescue”) uses exception handling all the time, triggered by a division by zero.

The results are something like this:

             user     system      total        real
plain    0.780000   0.010000   0.790000 (  1.769231)
safe     0.910000   0.020000   0.930000 (  1.781405)
rescue   0.900000   0.010000   0.910000 (  1.945978)

I know Zed will probably kill me for making any conclusions based on such a small sample (the half-a-million divisions are not the sample size… the sample size is one single run of the test). But the numbers are pretty much the same when you run the tests several times.

Using rescue is not much more expensive than running naked. In my tests in particular, it never was more than 5% slower. It might even be cheaper than multiple tests.

It’s fast enough for me not to worry about it, specially considering the fact that my own operations are probably going to be a lot slower than a simple division, thus diluting any slowdown even further.

If you really care about this, then by all means make your own tests and take your own conclusions. Otherwise, just trust the guy dressed in red, with the horns and the pointy tail: use and abuse rescue until you’re tired of it; it’s not worth worrying about its performance impact.

URLs on Rails

July 7th, 2006

One of the parts of rails that some people consider “ugly” and go to greath lenghts to “clean up” is the use of numeric ids in URLs: /accounts/edit/12.

URLs are considered extremely valuable real estate. Not only because users have to see them all the time, but also because search engines give them a lot of weight: since it’s a “limited resource” where you can only include a few keywords, you better use the keywords that matter most.

Rails does an excellent effort to help you use nice and clean URLs, but it stops at the :id. And it does this for one good reason. If it were to use, say, a user’s login instead of it’s numeric ID, then when a user changed his login, old URLs would no longer be valid. Yeah, I can hear you say that users don’t change logins, but what about a blog entry title? or a person’s full name? or a project name? As soon as you use some user-editable piece of information in your URLs you create the problem of state URLs, and that’s even worse than ugly URLs.

But there is a very simple solution. Use both a permanent id and a nicer textual description. Instead of an ugly /accounts/edit/12 or a perishable /accounts/edit/john-doe why not use /accounts/edit/12-john-doe. Your code has the “12″ it needs to look for the user, even if it later changed his name to “john-d-doe”, and your users and search spiders have the “john-doe” to feast on.

Implementing this is extremely simple, because Rails treats :id as a special parameter in routes. It’s specialness comes from the fact that it would try to call the to_param method on any object passed when creating URLs. That’s why url_for :id => @account is equivalent to url_for :id => @account.id, because ActiveRecord model’s have a default to_param that returns the id of the object.

All you need to do is define your own to_param for your models, and make sure you don’t explicitly include the .id in your url_fors and link_tos, because then you would be skipping your own to_param call.

class Account < ActiveRecord::Base
  def to_param
    "#{id}-#{full_name.gsub(/[^a-z1-9]+/i, '-')}"
  end
end

The second part of this solution is, of course, making sure your actions can handle these extended :ids. The smart ones amongst you would immediately think about monkey patching ActiveRecord’s find to clean up the parameters in calls like Account.find(params[:id]). But the not so stupid way would be to forget about dealing with this, giving it a try and look surprised when it works miraculosly as expected.

See, this is just a coincidence; it was not designed as part of Rails (or DHH wouldn’t have looked surprised when I brought this up at RailsConf). It’s just that Rails will pass your long :id string to the database server, which, on seeing that the id column is actually an integer, will try to convert the parameter to a number before using it, and it happens that such conversion will just use any numerical characters it finds and drop the rest, thus converting “12-john-doe” into plain 12. See, accidental behaviour over configuration; what can be better than that?

Of course, you might want to add a couple of unit tests just to make sure whatever database server you’re using behaves in this particular way. I’m not sure if that’s part of the SQL-92 standard, but I would be surprised if any major database server works differently.

So there you have it, now go add useful information to your URLs, like “asbestos-mesothelioma-canada-drugs-viagra-ambien”.

Oh, and in case you were wondering, hyphens/dashes (-) work better as word separators than any other characters. Google will match “canada drugs” against an URL like canada_drugs, but it won’t match “canada” alone. If you use hyphens, as in “canada-drugs”, then it considers them as separate, independent words.

Update: Some good points have been raised in the comments.

First, is that Aristotle Pagaltzis brought up pretty much this same argument more than six months ago.

Second, is that you might want to use redirects from any partially valid (i.e. the ID is correct, but not the rest of the slug) to the “official” URL. This is simple to implement, but requires some extra code on each controller.

Third, is that some database servers do not perform type coercion, and might get very angry if you don’t pass an integer for your id queries. Postgres was cited as an example. The solution for this is extremelly simple: just make sure this code is executed when your application starts (i.e. put it in lib and require it from environment.rb, make it a plugin, etc)

class ActiveRecord::Base
  def self.find_from_ids_with_coercion(id, options)
    find_from_ids_without_coercion(id.to_i, options)
  end
  alias_method :find_from_ids_without_coercion, :find_from_ids
  alias_method :find_from_ids, :find_from_ids_with_coercion
end

No need to try

July 6th, 2006

I previously talked about our try method as an easy way of dealing with exceptions inside expressions:

puts try {patient.name.first_name} || "-- no name --"

Otherwise, you would have to set up your own begin / rescue / end blocks:

puts begin
  patient.name.first_name
rescue
  "-- no name --"
end

which would have been too ugly for our modern sentitivities.

I was wrong. I should have known it.

It turns out that ruby lets you use rescue without a begin. This is most commonly seen in exception handling for methods:

def my_method
  # do something
rescue
  # handle exception
end

It also turns out the ruby lets you use rescue as a postfix modifier for an expression, just like all those if and unless. So you can write something like:

puts patient.name.first_name rescue puts "-- no name --"

And it also happens that “inline rescues” work inside parenthesis. So you can do something like:

puts (patient.name.first_name rescue "-- no name --")

Which is exactly the use-case for our original try method.

So now that I had a piece of code blessed by _why, I’ll have to get rid of it or risk not beeing rubyish enough. You live and you learn.

One of, a pocket case

June 30th, 2006

Another one of our snippets of code (all collected in a ‘private’ plugin called, surprisingly, ’snippets’) is one_of.

Suppose you have an html form, with a select that let your users specify the sort order for your index page. Said order can take a value of “name”, “name desc”, “date” or “date desc”. The more observant among you are probably yelling “SQL injection!!!, SQL injection!!!”, because even if your select only has those four options, nothing can prevent a malicious script kiddie to modify the request and ask for sort_order=your momma or perhaps sort_order=name; delete from users.

So smart web developers (or dumb web developers smart enough to remember when their sites got hacked because of a SQL injection) validate anything that comes from the outside world.

Our snippet, one_of, provides a simple way to do a very common validation: making sure a value is one of a given set of possible values.

class Object
  # Makes sure the value is "one of" the list given,
  # otherwise returns the first value from the list
  def one_of(*args)
    args = args[0] if args.size == 1 and args[0].kind_of? Array

    (args.include?(self) && self) || args[0]
  end
end

It’s use is exemplified, for example, in the following examples:

query[:sort] = params[:sort_order].one_of("name", "name desc",
                                                           "date", "date desc")
answer = answer.one_of("yes", "no")
answer = nil.one_of("yes", "no")  # => "yes"

This method serves it’s purpose well, so we haven’t had a need to enhance it. But I can easily think of a couple of improvements, making it more case-like.

class Object
  # Makes sure the value is "one of" the list given,
  # otherwise returns the first value from the list
  def one_of(*args)
    args = args[0] if args.size == 1 and args[0].kind_of? Array

    args.each do |arg|
      return (arg.kind_of?(Regexp) ? $1 : self) if arg === self
    end
    args.first
  end
end

See? Now you can do thinks like:

"abc".one_of(String, Numeric) # => "abc"
"abc".one_of("none", /([bc]+)/)  # => "bc"

It works almost like a pocket-sized version of case, with the default being first instead of last.

But be careful. Since the default value is the first one in the array, you might end up with a Class object, or a Regexp.

Do or do not… or maybe try?

June 28th, 2006

During RailsConf, _why presented the last two installment of his fabulous series: The Least Surprised. This time, they even were multimediatically animated, with sound and movement.

In one of them, Malsky (that’s his name) talks about exception handling and compares Java’s wimpy, tentative try (”uhm… let’s see, are we suppose to be doing this?”) and catch (”oh, oh, they threw a ball at me… don’t close your eyes… don’t close your eyes…”) with ruby’s assertive and daring begin (”we’re on a mission, here we go”) and rescue (”something went wrong, but it’s ok, we can deal with it”). And he finished with a “in ruby, there is no try” and a round of applause. I’m paraphrasing here, trying to fetch the memories behind a cloud of drunken stupor, but I’m sure it was pretty much like that, if not literally, at least in spirit.

So, anyway, after his totally and amazingly rocking performance, I approached him. Once I got the autograph out of the way, I showed him this little nugget of code we use at streeteasy.

module Kernel
  # Returns 'value' in case of an exception, otherwise returns the execution
  # of the given block
  def try(value = nil)
    yield if block_given?
  rescue Exception => exception
    value
  end
end

Which provides the simplest possible exception handling: in-line inside your expressions, with a default value in case of any exception. Some examples are in order:

try {params.join} || ""
try("") {params.join}
"User: #{user} (#{ try {user.group.name} || "no group" })"
user.group = try {Group.find(params[:id])}

See? a clean way of dealing with “minor” exceptions. And _why, after clarifying that it was Malsky’s opinion, not actually his, agreed that “it was tentative enough, and an apropriate use of trying”, and that the || syntax was prettier (we agree).

So now, our source code looks like this:

module Kernel
  # Returns 'value' in case of an exception, otherwise returns the execution
  # of the given block
  #
  # Now with the official stamp of approval of _why the lucky stiff
  #
  def try(value = nil)
    yield if block_given?
  rescue Exception => exception
    value
  end
end

Feel free to use it in your code, and try to give us credit (heh, see, a pun). Why not? (heh, another pun, I’m funny).

RailsConf Calendar

June 20th, 2006

I have made available an iCal file with the schedule for RailsConf 2006. I created it even before the official schedule was made public, but I have kept it up to date. The description even
includes a list of changes.

You can Subscribe in Apple iCal, or use the url above in any other program supporting the iCal format.

Hello world!

June 20th, 2006

It was about time for me to start blogging in english. I’ve been doing it in spanish for more than 5 years, and even had one or two false starts at the english blogging thing.

Now I’m starting for real. I’ll keep blogging in this site forever. That is, if you consider that “blogging forever” can include indefinitely long periods of silence.

So bear with me, bear with my ESL (that is English as a Second Language, for those of you gringos that have never had to deal with an immigrant) and please don’t call La Migra on me.