Why critics of Rails have it all wrong (and Ruby's bright multicore future)
unlimitednovelty.com
unlimitednovelty.com
You don't need all the ceremony and structure of Rails and MVC to write a JSON api, you just don't.
Worrying about Rails' future and if it's still "winning"(for some definition of winning?) compared to node.js is silly and reminds me of how many Java devs had an existential crisis about the future of Java since Java 7 took a few too many years to ship.
Rails is a web MVC framework, that's it. It's not even the only or best web framework in ruby. Rails is not ruby. It is not designed to compete with node. Node is a totally different thing.
Compare node and ruby and that's a more interesting and correct comparison, but I've happily used both and neither one is going to "kill" the other any more than Rails killed PHP or Java killed C++.
Each person has a preference on where they thought something should be. Days were spent arguing over the location of mundane things. Each team member had a different understanding of REST, so without the rails routing we argued about what we thought an ideal API would look like. We argued about which view engine we should introduce. We argued about how to handle our JS/CS. We argued some more about routes and how to make them more discoverable. On and on. Not intentional, but something new would come up and we would need to figure out where to put it.
This, more than anything, hammered home for me the strength of rails. Rails is great at getting you up and running, but it's greatest strength is that it's idioms are well documented. That gives developers a strong impression of how things are going to be and where things go. This saves you so much time by not having to argue and reach consensus on every little thing.
However, Rails is just too fng complex. It's true what they're saying, Rails-only programmers aren't necessarily Ruby programmers. For a good reason, even the simplest tasks are performed with Rails metaprogramming magic behind the scenes. Idiomatic Rails programming actually means not doing imperative programming which sucks. Programmers want to actually know and control what they are doing.
I once dealt with a legacy Rails app that had really complex Models. ("Fat models, thin controllers") Of course the original designers hadn't thought about every corner case, and of course not thought about what AR is actually able to deal with seriously. The app was just slow and not maintainable.
But to say something good about Rails: creating standard web pages with it is a peace of cake. All standard tasks are automatized.
Great when building but it sucks when debugging and maintaining.
There was a recent post on HN regarding /usr/local that seemed to suggest some programmers do not understand what a partition is nor should they need to become acquainted with such unimportant details. I hope I'm wrong in that interpretation. Because if true, that is just sad.
It provides generators, basic helpers (link_to etc.) and, most importantly, it gives project a sane structure. Oh, and it does not tie you to any fixed set of components. Using Padrino and Sequel is a bliss!
But your app remains a sinatra app, small and elegant. Also, the documentation is excellent.
That's just the bad workman blaming his tools for the faults of the toolbags he works with.
Use Rack, pick your favorite gems, and go to town. Why mess with all the other Sinatra junk when you can have a simple config.ru for your app and just write a simple call method and you're good to go?
You don't need all the ceremony and structure of Sinatra to write a JSON API, you just don't.
One of the main benefits of using frameworks like Sinatra and Rails is that you often don't need to re-write and re-implement all the same boilerplate code (like parsing JSON post bodies) over and over again.
And the difference with rails and sinatra vs most other open source projects is the size and activity community, and as a result, the overall stability of those projects.
So, in theory, do you need all that "ceremony and structure"? No, but I can't think of a good reason why you'd go implement all that stuff yourself.
https://gist.github.com/3719513
Did I miss something?
Rack is "env variable goes in, array of [code, headers, body] comes out, here are some handy utils for handling grunt work". That's it. That's Rack. You can do whatever you please with it in the middle. That's all that a middleware is, and Rails itself is basically just a collection of Rack middlewares (procs) with some support libraries stapled on. That's a gross simplification, but at the end of the day, that's basically the heart and soul of the framework.
My point is that there are multiple levels of abstraction. Of course you don't have to re-write and re-implement boilerplate when you use Sinatra over Rack, just like you don't have to re-write and re-implement when you use Rails over Sinatra. "Rails bad, Sinatra good" is just silly, because it all boils down to what kind of tradeoff you're willing to make between what's done in framework versus what's done in your application.
I agree with the parent, that I have no use for the complexity of Rails and Sinatra is convenient. Other people are free to choose as they wish, and Rails might be the more appropriate library is some cases regardless of preference (like in the sibling post about teams).
But there's a ton of utility on the spectrum from Rack to Rails and it's perfectly reasonable to choose a midpoint, and perfectly reasonable to choose it because you think Rails is too much but Rack is not enough.
When you look at Rails as a collection of middlewares (which you can choose!) and support libraries (require 'rails/all' may not be needed!), instead of looking it as a monolithic black box that can't be configured to fit your needs, it's suddenly a lot less "heavy" and a lot more "wow, okay, buffet-style app composition sort of rocks". People seem to have this idea that Rails is on the far end of the "magical and heavy" spectrum, when it can be just about anywhere along the spectrum that you want it to be, based on your selection of middlewares and libraries to use.
I have production apps that range from small one-off Rack apps (my dynamic asset server, for example) to Sinatra to full-blown Rails, and they're all very similar beasts. Rails isn't some mystical black box, and Rack isn't just some RFC somewhere, and I think it does a grave disservice to both to marginalize them.
There's a continuum of "more manual work" <----> "more framework", and there's no one right answer, even for something like a JSON API.
The choice between Sinatra and Rails can reasonably be debated, but there's little reason to write a JSON API in Rack unless you need control of protocol details that neither abstraction provides. Sinatra/Rails are frameworks, Rack is middleware.
Why wouldn't you just do a 50-line Rails app instead, and slot in any other Rails niceties when you feel like you need them?
Problem solved.
There's a major flaw in the R/R culture: they have no interest in maintaining stable APIs. If you want to upgrade your Rails app to the latest version of some library, you might as well do a rewrite of your entire app. So many of the APIs change from release to release that most of your gems will stop working unless you upgrade. When you upgrade the gems, your code will stop working, because most of the gems will have changed their public API. If you've got a non-trivial application, you will essentially be unable to upgrade. You will stop getting security updates. What minor upgrades you can manage to do will leave things partially broken, and you'll just get used to it being broken. (for example, one project I've worked on is stuck with a brain-damaged RSpec library that reports error messages without interpolating the strings, you always get "Expected CONDITION, got FAILURE, see CODE" in the logs)
My recommendation: only use Rails for short term projects that will be completely shut down after a few months.
I know that it's common wisdom that "no ruby engineer will ever want to switch to java", but that's because you've never been saddled with a 2-year-old rails project (or a 5-year-old plain-ruby project, which will have the same symptoms, just more slowly). Then your team will be begging for something that runs its test suite quickly, that has enough static analysis to know if you're using an outdated API, that has a decent metrics library, where you can query the state of the VM, that doesn't constantly leak memory, that plays nicely with databases that have real constraints, that has fewer race conditions in common libraries, etc etc etc.
In your package.json can specify what version of a library to use and npm mostly figures it out for you. For instance, I'm still intentionally using an older version of Express (2.5.9 instead of 3.0.x) and npm hasn't had a problem figuring out deps.
Part of the reason this works is that the npms you install are kept in the local folder, not stored globally as many other package managers do, so some other random app on you box won't trample your delicate library configuration.
All I was trying to say (in a direct reply to Jeff) was that I think that putting Rails on hold for 2-3 years while merging in Merb (or the Merb team, depending on how you want to look at it) wasn't, in retrospect, a wise decision.
But that's old news, and doesn't need to be re-hashed now -- it's great to see Rails 4 starting to pick the pace back up, and exploring new features like improved streaming, granular cacheing, built-in queues, and more.
* December 23, 2008: Merge announced
* March 15, 2009: Rails 2.3 released
* June 25, 2009: Rails XSS protection plugin for Rails 2.3 is released
* July 20, 2009: Rails 2.3.3 is released
* July 29, 2009: The first version of bundler (0.3.0) is released
* February 3, 2010: Bundler 0.9.0, the first "modern" version of Bundler, is released with support for Rails 2.3.5
* February 4, 2010: The first Rails 3.0 beta is released
* May 22, 2010: Rails 2.3.6 is released
* June 8, 2010: The first beta of Bunder 1.0 is released
* July 26, 2010: The first Rails 3.0 RC is released
* August 29, 2010: Bundler 1.0 is released
* August 29, 2010: The first Rails 3.0 final is released
As you can see, activity in Rails hardly ground to a halt during the time period we were working on Rails 3. We didn't even ship Rails 2.3 until three months after the merge, and continued active development for some time thereafter. And during this time, we backported two major new Rails 3 features, Bundler and Rails XSS, to Rails 2.3, actively releasing new versions of Rails 2.3 to take advantage of these backports.
Further, Rails 3 itself was a MAJOR feature release. Saying that Rails was "put on hold" for 2-3 (?!) years to merge in the Merb team is like claiming that Obama is going on an international apology tour. See below for a summarized list of important features.
I won't even talk about the improvements in Rails 3.1 or 3.2, which were possible because of the firmer footing we put the entire project as part of Rails 3 development. And this analysis doesn't even cover the major ways that the project structure changed during that time (full embrace of GitHub, significant increase in long-term contributors, acceleration of the guides project and other improvements to documentation).
---
Rails 2.3 was a major Rails release, with the following features:
* Completion of the Rack integration work begun earlier in the Rails 2.3 series
* Full integration of the Engines feature that was first softly integrated in Rails 2.2
* Localized views
* Support for public HTTP caching
* Ruby 1.9 support
* Nested Attributes (in both ActiveRecord and ActionView)
* Nested Transactions, even in MySQL
* A whole slew of other significant ActiveRecord improvements (http://guides.rubyonrails.org/2_3_release_notes.html#active-...)
* Unified rendering (render :foo, render "foo", etc.)
* HTTP Digest Auth
* Various performance improvements
* Rails application templates
* A whole lot else, see http://guides.rubyonrails.org/2_3_release_notes.html
---
Rails 3.0.0 was a major Rails release. Here are a few of the important features, but summarizing them all would be difficult:
* Incorporated direct support for Bundler, and drove the initial Bundler feature-set.
* Many architectural improvements, which drove improved, more stable third-party libraries in the Rails 3.x era (http://guides.rubyonrails.org/3_0_release_notes.html#rails-a...)
* A much bigger emphasis on eating our dog food: Rails 3 internals use exposed public APIs that any third-party extension can use. In fact, ActiveRecord itself is built as an extension!
* TMail, used by Rails 2.3, was unmaintained and extremely buggy (it often crashed when reading mail). The `mail` gem, written from Rails 3, is a ground-up rewrite.
* The ActiveRecord query interface is a night-and-day difference from the Rails 2.3 query interface. Users of ActiveRecord can now mix and match query parts (where clauses, group, order, limit) without having to worry about how to describe everything in terms of a single hash. This is probably the biggest change to the way developers use a major Rails component since the embrace of REST in Rails 1.2.
* ActionMailer finally got the update it needed. By Rails 2.3, ActionMailer had become a red-headed stepchild, sharing many features of the mainline render system on an ad-hoc basis. That meant inconsistencies galore. In Rails 3.0, ActionMailer got updated to share code and interface with ActionController, locking the two together to avoid another redheaded stepchild problem.
* Large improvements to Internationalization.
* ActiveModel makes it possible to use parts of ActiveRecord without using its SQL persistence layer. Apps have used this to spruce up plain Ruby objects, and libraries like Mongoid have used this to make alternate persistence libraries feel like the ActiveRecord people know and love.
* The router was completely rewritten, adding commonly requested features like the ability to match on any element of the request (commonly used for subdomain matching) and optional segments.
* The new ActiveRecord, ActionMailer and Router APIs maintained backwards compatibility with the older APIs until Rails 3.1 or 3.2, making upgrading plausible for more people.
---
Rails 2.3.3 was a minor update, with the following features:
* `touch` in ActiveRecord, to update a record's timestamp manually or automatically when associated records change
* Improved support for legacy schemas in ActiveRecord
* A general-purpose JSON encoding API and support for the `json` and `yajl` backends
* Other features: http://weblog.rubyonrails.org/2009/7/20/rails-2-3-3-touching...
Rails 2.3.6 through 2.3.9 (The Bridge to 3.0) was a significant update, with the following features:
* Direct support for the Rails XSS plugin
* Support for permanent and signed cookies
* Support for table-name-prefixes for all ActiveRecord classes in a module
* Lots of 3.0 ActiveSupport backports and improvements
* Improved support for Ruby 1.9
* See more at http://weblog.rubyonrails.org/2010/5/22/ruby-on-rails-2-3-6-...
Well gee Jeremy, thanks for rehashing it then! ;)
I work on a legacy Rails 2.x app at my day job. Wading through the alias_method_chain soup is not fun. I for one am glad the time was spent to eliminate the technical debt that came from too much feature push in earlier versions of Rails and an underemphasis on code quality. They now have a great foundation on which to add future features (which is more or less what you're saying at the end)
I mean arel, unobtrusive js, bundler, routing API, and actionmailer overhaul just in 3.0. The asset pipeline, full engine support, modularity benefits such as pluggable ORMs and log subscribers. I find all this stuff to offer huge practical benefits on the ground.
For me the pain of Rails is mostly just the pain of Ruby—ie. it's slow, and concurrency is not really part of the DNA yet.
What are the killer features that Rails has neglected and caused it fall behind in your view?
So in all I seriousness, I want to know where he feels Rails is lagging.
Beyond this 'cheap' psychological interpretation, I think the merb merge was mainly paying the huge technical debt 'young rails' contracted in its fast growth.
Rails 3 set the ground for the future. All big project go through this teenage phase, and if Rails 4 has so much potential today is because the 'alias_method_chain' and other quick hacks were cleaned out with well thought architectural principals - even though rack and streaming don't play well...
Oh god and I haven't read the whole post yet...
Older software projects offer more features, but that doesn't mean they're materially slower or worse in any objective way.
We use Rails at Pose to deliver JSON to over 1M users on either Android, iPhone, or our Backbone Web UI. It works well, it scales, and it's clean. No complaints.
I think the problem with this outlook is that you don't need it...until you, you know, need it. Rails has a lot of niceties that you lose with Sinatra. Why not start from a superlight Rails app (using any of the many gems or ActionController::Metal options) and then add on bits and pieces when you need one? I think that this method is either just too difficult for some people to wrap their heads around or it offends their sensibilities as a programmer.
I've had too many personal Sinatra projects that have bogged down when I started reimplenting Railslike functionality.
Personally I have never had any problems with speed when developing large projects with Ramaze, Sinatra or Padrino. In my experience this only happens to Rails. No clue why though. Maybe some gems used by Rails take too long to load.
What you said about bloat and cruft is partly true. But there are other things which sometimes substantiate it - boilerplate and cognitive overhead, for example; sometimes older things have more of these than necessary, and are kept anyway because of their maturity and usefulness (and because a community has already developed which fit its brain to the project and has memorized the quirks already).
That is a good reason to keep the old thing, but it does not mean that the older thing is always optimal or even better than a newer thing.
And then later in the article...
> Jose Valim, (now former) Rails core member,
When you have to issue a retraction, you should update the article. The way the Times does it is to make the edit and then put a small note at the end of the article recording the original error. Like so: http://well.blogs.nytimes.com/2012/09/10/early-music-lessons....
Plus the code smell overall is just... lesser.
Then again, I came from a background of C and scheme, and somebody on c2 wrote that python appeals to people like me more.
Python suffers from their web framework community being too fragmented, so none of the projects are as mature as Rails.
Time Zones were added to Rails 2.1 in 2008. selenium-on-rails also debuted in 2008.
Does it not seem strange to you that it took 4 years after Rails for Django to get these features?
The time zone support, though, was really overdue.
I suspect there's a silent majority...
It's also probably worth mentioning that it literally only works with Thin, is unmaintained, largely undocumented, and that the latest master on GitHub doesn't actually work. Not really the best example to pick!
"Rails is great for JSON APIs."
"Many of these companies have client-heavy HTML5/JS applications which consume a JSON API coming out of Rails. Many of them have APIs that are routinely cited as archetypical RESTful JSON APIs."
Rails is great for JSON RPC over HTTP. It's not great for beautiful, consistent RESTful APIs. It still lacks basic support for hypermedia. Without hypermedia you aren't being RESTful and you aren't reaping the benefits of being RESTful. All you have is a weak convention for doing RPC with HTTP methods.
None of those companies are examples of REST either, they're all doing JSON RPC, and propping them up as RESTful does a disservice to REST and a disservice to Rails. How can Rails make progress towards hypermedia APIs if people won't even acknowledge that it's lacking?
I know people like Steve Klabnik are valiantly trying to get Rails to adopt hypermedia, but it isn't there yet, and there are significant technical and political barriers to overcome.
Node.js frameworks, being in their relative infancy, aren't burdened with the same barriers as Rails and have the opportunity to move faster than Rails could. No framework has dominated the Node.js community like Rails dominates the Ruby community; there's still the opportunity for a Node.js framework for building hypermedia APIs to take center stage.
And this post attempts to return the favor?
> Node is three years old now. Where are the Node.js success stories? Who's built a brand on top of Node?
> Meanwhile code quality is de-emphasized and large Node programs degrade into incomprehensible, byzantine structures of callbacks and flow-control libraries
Edit: added examples. The whole post is as much an attack on Node as it is a defense of Rails.
If you use the Gemfile at http://railstutorial.org/gemfile in a clean Ruby environment and run bundle install, the issue of dependencies shouldn't even come up. If you continue to have trouble, drop me a line at admin@railstutorial.org.
Alternatively, look at the changelog or just manually decrement the version number until my tests pass, obviously taking care not to move back past any critical security updates.
Also, the precise version numbers used in railstutorial are available in the code listings here: https://github.com/railstutorial/sample_app_2nd_ed/blob/mast...
The (fast paced|aglie|slapdash) nature of the rails ecosystem does necessitate a certain degree of attention to detail when it comes to versioning. However this applies equally to sinatra. It's worse with node.
Without the ability to do this one can only be a plumber fitting other peoples' appliances.
Rails is good for simple APIs (especially adding an API to an existing HTML site).
However, if you want to do more than that, elegantly - it isn't great yet. You quickly end up with some fairly messy serialization hacks and confusion between what should sit in the controller and the model.
The active_model_serializers gem is a really good start, but there is a way to go to get to the level of convention the rest of Rails provides.
Soon it will be great I have no doubt and the convention/stability of Rails will lead a lot of businesses to start using it.
That's not messy at all, it's basically just another TemplateHandler.
render :xml => @objects
Which, naturally, falls apart quickly beyond the most basic serialization. However, as long as you treat the API like any other view (even HTML is just another API format), then Rails does a pretty decent job.(I hope that it ends up in the Rails core someday, because it really is a gamechanger in how Rails is used for non-HTML content!)
rails boots fast if you have few files but when the project is big it is a pain, it takes a lot of time to boot and it's not fun. That's why the article says rails is "a day job".
I just hope the rails team don't think like you.
This is exactly what the Rails community did to PHP when Rails was the new hotness.
Agreed, good catch.
I have a response coming in the next week/2 weeks, so I won't go into much here, but I will say that Tony's efforts to defend Rails are admirable; despite some snarkiness and elements of being reactionary, he raises good points in his posts.
One quick question. Tony, what are your thoughts on Elixir?