Years ago I was heavily into Ruby. I wrote REXML, which was adopted into the standard library. For a while, I lived in hope that I’d be able to jump from Java to a career in Ruby. Well, that didn’t work out; at the time, there were very few job opportunities for predominantly Ruby development, and I needed to support my family more than I needed to get out of Java. I consider this one of those lucky happenstances, where — by thwarting my immediate desires — The Universe saves me from a worse fate.
It started turning sour when I noticed what I now refer to as The Scripting Trend. It goes like this:
Write a nice little script that satisfies a need, say a small application that provides some service. It’s bigger than a shell script, but smaller than something that needs its own ticket tracking project.
Debug the heck out of the script, ending up with something like five times the lines of testing code per line of actual application code. That thing is solid.
Deploy, and, for a while, monitor. Soon, you get bored of the perfection and forget about the script.
Time passes. Maybe a few months, maybe a year.
At some point, one of the routine upgrades upgrades your scripting language. Oh, you see where this is going, do you?
Suddenly, things starts falling over everywhere. Services are down, programs aren’t running. Say you don’t notice immediately, because it’s something like backups aren’t happening nightly, and you really only pay attention to backups when you need to restore.
Much debugging later, you realize that it’s all due to some breaking change to the interpreter. More likely, a breaking change to a library — core or not — but sometimes it’s an actual breakage in the core language interpreter itself.
This only needs to happen twice for it to be really frustrating, and a third time it become unacceptable. That’s when I completely dropped Ruby for anything other than occasional one-liners. Because you write something, and odds are good that that same program will not work even only months, much less years, later.
This is one aspect of the Dependency Hell problem. As good as you are, somebody upstream will eventually screw you, and you have no practical control over it. It will happen: we all stand on the shoulders of giants (if we’re lucky), or at the very least, our peers. You depend on the Linux kernel. You depend on libc, or libm. The further up the stack you go, the more precarious the perch. libxml. Ruby.
I encountered this problem twice today. Once while trying to install some software which was abandoned a year or so ago. The software is written in Ruby, and used Rake and Gems to build and install the package. Or, should have, if the Rakefile still worked, but apparently enough changes had been made to Rake in the past year to break the Rakefile.
The second problem happened with a Python package. In this case, I installed a newer version of some software, and one of the plug-ins for it stopped working not because of a change to the plugin API, but because the main program used a function in a core Python library, and that changed in a way that broke the plug-in.
And this is the fundamental problem with scripting languages: they amplify dependency issues. The exacerbate the fact that — no matter how clean, no matter how well written, no matter how perfect your code, casual changes by one of your dependencies can utterly destroy your software, at best creating headaches and extra work for you, and at worst rendering great software unusable.
I’ve come to agree with the camp that advocates the solution of static binaries. If you have a binary, it’ll work essentially forever. That’s peace of mind.