Initial reactions to Rails 1.2... annoyed
Posted by Mathew Abonyi Sun, 18 Feb 2007 17:15:27 GMT
I’ve held off my opinion about Rails 1.2 until I had a little more experience with it. First of all, I’m going to say I’m glad Rails has put out this release, because some of the features in Edge I’ve been needing for a while and, in cases where I couldn’t wait, like my simply_restful_backport, I hacked a ported version to 1.1.x. I also had unpublished ports for REST, routing, and a few deprecations to prepare myself. Most of the added features are bang on what is needed when following the Rails methodology of web development: singleton resources, nesting resources, the emphasis on REST, the respond_to(&block), etc.
But my optimism and preparations were in vain. I’m annoyed. My initial reaction is that Rails 1.2 is a fascist. The front-facing libraries, ActiveRecord and ActionPack, are not the cause of my annoyance. It’s the underbelly, ActiveSupport, which has acquired a rank smell.
How Deprecations are handled
I’ll be brief. Backward compatibility is beautiful and when you can’t maintain it, you prepare developers for the transition. Deprecrations are usually the answer: you say it is going to be removed for a long time, encourage people away from it, then finally remove it in either the next major release or the one after that, safe in the knowledge that everyone who will be affected by the change has got the message.
Rails has not held to the time-honoured traditions of deprecation. It does one of three things: fill up the logs with warning messages, raise annoying exceptions that apologise something is gone, or just plain crash because the old API had changed so much. That’s not deprecation. That’s just API upheaval.
And it’s unacceptable to have so many differences in the framework which, first, are undocumented, and second, change development so much. I would expect this behaviour from a 1.3 to 2.0 upgrade, but not between two minor releases. Maybe Rails Core should have re-numbered the release to warn users that Rails has changed quite drastically under the hood. The changes are fine, but it’s the way they came unannounced that is annoying.
Especially after so much time between the two minor versions, there was ample opportunity to tell developers… 1.1.6 was the last opportunity, and a better time was back at 1.1.2, when these additions already existed or were being considered. That is still too quick, but to give no warning, not even a proviso ‘this may change’, until it is too late is just insufferable.
ActiveSupport::Dependencies < ActiveSupport::Devil
For example, I don’t know why an innocuous ObjectSpace block in routes.rb should cause a blocking exception from ActiveSupport::Dependencies. It prevented my application from even loading and took half a day to track down. Or why I needed to alter ExceptionNotifier to require all its libs. Or track down 6 other ‘dependencies’ and make them explicit—I thought Rails was supposed to make this stuff easier, not more of a pain than C’s #include <hardtofind.h>?
It’s ridiculous for an ‘improvement’ release to be so fragile. Rails 1.1.x was pretty flexible on these terms. However, Rails, in its growing focus on encouraging programmers into standardised practices, is looking more like 37signals practices. And to say they are ‘best practices’ or good ‘conventions’ is a bit rich, because Rails is not the cleanest set of libraries. Just run it with $VERBOSE = true and you’ll see what I mean.[1]
In fact, after using ActiveSupport::Dependencies for a while, I’ve come to the conclusion that it is too clever for its own good and I foresee lots of bugs in Rails Core Trac about it. It’s too prying. It’s too observant of everything that is happening. It infects the production environment. It’s a glorified const_set trace_func.
ActiveSupport.include :bloat
What kind of overhead does ActiveSupport::Dependencies have? How many orders of magnitude slower is it than the previous Reloadable module, which just included a method, reloadable?, and was found via ObjectSpace? First of all, it didn’t need a revolution like Dependencies; it was clean, simple, elegant and extendable. It didn’t solve all the problems and had room for improvement, but removing it I think was a mistake. ActiveSupport::Dependencies, unlike it’s predecessor, is a beast. Here’s a quick LOC comparison between activesupport 1.3.1 and 1.4.1 (ignoring hooks in the other gems):
$ grep -v '^\s*#' activesupport-1.3.1/lib/active_support/reloadable.rb | wc -l
28
$ grep -v '^\s*#' activesupport-1.3.1/lib/active_support/dependencies.rb | wc -l
172
$ grep -v '^\s*#' activesupport-1.4.1/lib/active_support/reloadable.rb | wc -l
56
$ grep -v '^\s*#' activesupport-1.4.1/lib/active_support/dependencies.rb | wc -l
538Not the most reliable comparison, but it gives a good idea of the bloat introduced by, first, dependency tracking and, second, simple deprecating. Most of those lines of code in Reloadable are about how it is not just deprecated, but downright crippled and how evil it was. This is not a good practice for a developing an open source framework which is not easy to upgrade (and we live with it, because Rails is useful). The difficulty of upgrading is precisely because of these badly planned deprecations and backward incompatibilities.
Enough ranting
In this sense, Rails 1.2 is by the far worst gem update I’ve experienced so far. How many blogs have posted about the many little things that needed to be changed to upgrade? How many forward-thinking developers looked in cringing, abject horror when their tests failed left and right after that (apparently) over-optimistic gem update rails? How many people flew to IRC and the mailing lists for help? Let me be so conceited as to introduce a ‘best practice’: discuss major changes with your users.
I realise that Rails is first and foremost a 37signals project, evolved from DHH’s work there and is primarily altered and improved for that company’s purposes, but as an open source project trying to get more users, it’s important to either branch those changes which are special to that company or to be more considerate to the user base. I think Rails development would benefit from:
- a separate experimental branch (different from Edge trunk) for API changes—Radiant does this quite nicely
- a highly visible discussion board on rubyonrails.org to discuss major changes with a link to it in IRC and regular updates to the Rails mailing lists
Granted, much of my qualms with Rails 1.2 is with Dependencies and with some good old fashioned hacking, they can fix it. However, I think it and the library from which it has spawned are worth my comment. Dependencies is the most invasive addition to the framework I’ve seen to-date. I appreciate the attempt at innovation, but Rails needs to stop doing this kind of widespread deprecation and fundamentalist intolerance to the unconventional.
I wish the development team would take more pleasure in the diversity of uses of Rails and less perverse pleasure in the militant drilling of young, transitional and experienced programmers. I like the framework, but I don’t work (or want to work) at 37signals.
1 I’ve been aware of my own transgressions for a while. I’m in complete agreement with Mauricio on the topic of warnings. They are there for a reason: to create semantically correct Ruby. We should all be able to run our fantastically short, readable, meta-programmed, overloaded and mixin-loving code with $VERBOSE = true. It isn’t difficult to achieve and it helps in the long-run to make that lovely little code more readable, usable and quiet.
