Wednesday, October 2, 2013

Upgrading Legacy Applications: Ruby 1.8 Pitfalls

When dealing with large, long-lived enterprise apps, the upgrade cycle is often much slower.  Because these applications aren't always under active development, the prospect of "long-term support" releases becomes more important.

We recently upgraded a client app from Ruby 1.8.7 to Ruby 1.9.3, just as support for Ruby 1.8.7 was ending.  We ran into some "gotchas" while upgrading, so perhaps these will help other developers in the future.

In general, the incompatibilities between Ruby 1.8 and 1.9 are well-known, but we discovered a few lesser-known issues during this upgrade.

1. Array#to_s

In Ruby 1.8, calling to_s on an Array is equivalent to calling join.

In Ruby 1.9, calling to_s on an Array is equivalent to calling inspect.


This seems like a minor change, but it is significant.  There are many Ruby 1.8 libraries that will display arrays directly to users via to_s, assuming the array will be suitable for display directly to an end-user.

If that code is run against Ruby, 1.9 the displayed array will include both the brackets and commas, usually making it unsuitable for end-user display.

2. Symbol#to_i gone

As of Ruby 1.9.2., symbols are no longer internally represented as integers and Symbol#to_i was removed (thanks to andrewjgrimm for pointing this out).  Thus, if you call string_object[:symbol], it will use the symbol's integer representation to reference an element under Ruby 1.8.

3. Creating Hashes

In Ruby 1.8, you can actually create a hash by putting an list of keys and values directly in the curly braces. In Ruby 1.9, that doesn't work.

KEY TAKEAWAY

Backwards compatibility in languages is important, especially when building enterprise applications.  Even if a seemingly-arbitrary change looks like a net win for program correctness, it can cause problems in legacy software.

For example, consider the following code:

Let's say you have a bug in method_that_returns_a_hash, and it sometimes returns a String instead.  Under Ruby 1.8, that bug might be completely innocuous, because address_map[:canada] would still return nil, and your program could still execute correctly.  Under 1.9, that would raise an error.

What other obscure incompatibilities have you encountered while upgrading legacy software?

2 comments:

andrewjgrimm said...

In Ruby 1.8, Symbols had a #to_int method.

>> :foo.to_int
=> 15113
>> x = "a" * 15000 + "b" * 1000 ; nil
=> nil
>> x[:foo]
=> 98
>> x[:foo,1]
=> "b"

By contrast, in Ruby 1.9, Symbols don't have :to_int (or :to_i).

Mike Leone said...

Thanks much for pointing that out, andrewjgrimm. I updated this post and gave you credit.