When Rails Fails

rorFor the past years there has been a lot of hype around Ruby on Rails. It’s a modern web framework built on the dynamically typed Ruby language. It makes heavy use of Ruby’s meta-programming abilities to essentially create an internal domain-specific language for building web applications.

People love Rails, and rightfully so, but it’s not perfect. An often overlooked problem is debugging; what if programmers make mistakes? Sure, real programmers don’t make mistakes, especially ruby on rails programmers, but let’s say they do, for argument’s sake. What happens then? Is the bug easy to track down, or do we get buried in stack traces that only DHH understands?

To test this out, I followed Rails’ "Getting Started" guide and made "accidental" mistakes along the way, to see how the system would react.

Mistake #1: Route to a non-existing controller

In my routes.rb I made a typing mistake, referring to a non-existing controller:

map.root :controller => "homme" # should be "home"

Ideally I would get an error message saying "no such controller named ‘homme’". Instead, this is the result when loading the root page of the application:

rails-fail-1

The error here is "Unintialized constant HommeController". There are a few problems with this error message. The first is the name mangling: I typed "homme", which is apparently turned into "HommeController". I have no clue where I made my mistake, there’s a stack trace, but none of these files are mine, they’re all Rails framework files. Where did I make my mistake?

Mistake #2: mispell the symbol :controller in the mapping file

I misspell the symbol :controller as :controler 

map.root :controler => "home"

The result, when starting the server: 

rails-fail-2

This is actually not bad, it says :controller must be specified, so apparently it is missing. It would be better if it would have noted that the :controler symbol we used makes no sense, but still. Not bad.

Mistake #3: Linking to non-existent pages

Let’s link to a page that does not exist, from our erb code:

<h1>Hello, Rails!</h1>
<%= link_to "My Blog", post_path %>

Where post_path, should have been posts_path, results in:

rails-fail-3

The full error got truncated there, this is the full message:

post_url failed to generate from {:controller=>"posts", :action=>"show"} – you may have ambiguous routes, or you may need to supply additional parameters for this route.  content_url has the following required parameters: ["posts", :id] – are they all satisfied?

As a newbie Rails user I’m thoroughly confused by this. The code that I editted is 100% generated by scaffolding commands. Why does it talk about "posts" when I said "post", and what is this :id thing? Looking through my controller code, it turns out that there is in fact a post controller to be invoked here, namely the show action of the posts controller. Still, to a newbie, this message is thoroughly confusing. It is nice that Rails at least attempts to give a specific error message with suggestions on how to fix it, but to be honest, I don’t get it.

Mistake #4: misspelling :confirm

Another piece of view code: 

<%= link_to 'Destroy', post, :confrm => 'Are you sure?', :method => :delete %> 

Note the misspelling of :confirm, there. If you spell it right, you’ll see the following window when pushing the "Destroy" link:

rails-fail-4

However, with :confirm misspelled as :confrm, it is simply ignored, resulting in immediate deletion of this record, without any confirmation. Ouch.

Mistake #5: Mistyped property names in data model validation rules

I accidentally misspell the name of a property :name in my data model code:

class Post < ActiveRecord::Base
  validates_presence_of :nam # should be :name
end

When I now add or edit a post object through a form, I receive a validation error:

 rails-fail-5

Nam? I don’t see a nam field.

Sure, the bug is tracked down fairly easily, but still it looks odd.

Mistake #6: Misspelled formats

In controllers you can specify different representations of your output, e.g. a HTML, JSON or XML representation. But what if you specify a format that does not exist, such as htm?

respond_to do |format|
  format.htm # should be html
  format.xml  { render :xml => @posts }
end

This is what happens when this piece of code is invoked:

rails-fail-6

That message reads "uninitialized constant Mime::HTM", this is slightly confusing because we’re talking about formats, not constants. Plus, apparently format is replaced with Mime:: or something and the "htm" is capitalized to "HTM". In the long stacktrace (which I truncated for the reader’s sake) there is one mention of my code (the line that starts with /Domain) and indeed, this is the line where the mistake was made, still it’s pretty hard to find.

Mistake #7: mistyping of a symbol in the context of data binding

Consider this piece of code:

  def create
    @post = Post.new(params[:poost])
    respond_to do |format|
      if @post.save
        flash[:notice] = 'Post was successfully created.'
        format.html { redirect_to(@post) }
        format.xml  { render :xml => @post, :status => :created, :location => @post }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @post.errors, :status => :unprocessable_entity }
      end
    end
  
end

Spotted the mistake? Let’s see what happens if we add a new post, which invokes this create action. First we fill in the form:

 rails-fail-7a

We then push the "Create" button:

rails-fail-7b

Wha? Where did all my data go? Oh right, I misspelled :post as :poost in the second line of that controller code.

Conclusion

If I would poke around a bit more, I’m sure I would find a lot of similar examples. I’m not looking for explanations of this behavior, I get how it works. The thing I’m trying to demonstrate here is that poor error messages and very limited checking of your programs is a drawback of internal DSLs like Rails. It would be much nicer for the programmer if programs were analyzed statically giving domain-specific error messages, rather than obscure runtime exception stack traces that are complicated to decipher.

Update: My response to common criticisms.
Update 2: When JBoss Seam Fails.
Update 3: When Scala DSLs Fail.

Got something to say?
  1. R.Sriram says:

    Agree with you. I too faced such issues

  2. Matchu says:

    A lot of these could be fixed by having more exceptions rescued, but one of them most definitely could not. In the last instance, there is no sensible way of catching the developer's error. He tries to initialize a post using nil for attributes. Such is a perfectly valid request, and there is no feasible way for Rails to catch that misspelling.

    I grant that more generic error messages could be better humanized when using the more human helpers. But it's not really the program's responsibility to catch the programmer's faults.

  3. Zef Hemel says:

    “it's not really the program's responsibility to catch the programmer's faults”

    You cannot catch every mistake, but I feel it is the framework's job to catch as many mistakes as possible. Of course, if the programmer does not express his intentions well, there's no way of saving that. The examples given here are all honest typos that could occur, so those should be caught in a satisfiable way as much as possible. Right?

  4. drawohara says:

    what you are saying might have some merit if it weren't also true of css, html, javascript, json, and every other web technology – many of whom actually rely on syntax errors in order function in crappy interpreters like IE. i've developed everything from satellite systems to web systems and can assert quite confidently that the web is not the place for developers who are uncomfortable with fast and loose interpreter based technologies – since they are what hold the web together.

    you criticisms are of course valid, but they apply to the whole of web development which is unlikely to be statically analyzed anytime soon.

  5. drawohara says:

    actually there is a perfectly simply way to catch nil errors:

    <pre>
    def create(hash = {})
    raise if hash.nil?
    end
    </pre>

    as it stands rails does

    <pre>
    def create(hash = {})
    hash ||= {}

    end
    </pre>

    but i think people would quickly tire of

    <pre>
    model = Model.new(params[:model]) if params[:model]
    </pre>

  6. mpeters says:

    You don't need static typing to get good error messages, nor is this a fault of using a DSL. It's simply that Rails hasn't invested in decent argument/parameter checking.

  7. A lot of these problems stem from a mismatch between what you expect and what Rails expects. For instance, the first error message is perfectly sensible. You told it to go to a “HommeController”, and it didn't find one. It can't possibly understand that you had a mispelling in your route.

  8. Darrell says:

    “It would be much nicer for the programmer if programs were analyzed statically”

    If static analysis is what you want, then write your code in a static language. I think you are discovering the fundamental trade-off of static vs dynamic languages. When exactly should this static analysis occur ? Compile time? Oh wait there is no comple time.


    On a more practical note, If you use netbeans for rails, ctrl+space auto-complete will help a lot with the misspelled or incorrect params. I am amazed at how well net-beans does for a dynamic language.

  9. Dale says:

    Where are your tests? If you were doing this with TDD or BDD you wouldn't have a lot of these errors. This is part of the reason testing is such an important idea in Rails (and in most dynamically typed languages). Admittedly a lot of the simple starter tutorials don't insist on starting with tests but you can hardly attribute that to be the fault of Rails.

  10. Chris says:

    Damn I miss the punch card days.

  11. Chris says:

    Damn I miss the punch card days.

  12. bunn says:

    It is quite odd and has someone noticed that all the errors have something to do with incorrect spellings. Does Java Struts, Hibernate and .NET stack give you a plain friendly error message. I guess not? I mean you make a mistake in a jsp file and you won't know where the error is…it is so vague.

  13. John says:

    Rails is opinionated, and Ruby is a dynamic language. Virtually all of your gripes are encompassed in those two facts, the second of which obviates most of the article. If nothing else, one more person who isn't interested in rails makes it easier to keep my job/ standard of living.

    One more awesome opinion piece on teh intarnetz.

    Cheers!

  14. Aldo Wishfield says:

    Here's the trade off. Use a dynamic language and you will have less code and less hoops to jump through, but you can't be careless and expect the language to undersand what you were thinking. If you want to write a bunch more code and have a language that can make sense of your carelessness, then go back to something statically typed.

  15. tphyahoo says:

    check out happstack.com

    haskell's answer to ruby on rails. (and php)

  16. Dallas Chen says:

    If you are having problems like this in rails beyond the first hour or two you use the framework, then you'll probably have significantly worse problems using any other framework.

  17. Ryan Bigg says:

    Hi Zef, apologies in advance if you don't like long blobs of text. I think that if you spend some of your important time reading what I have written below you will be more enlightened and possibly realize that it isn't as bad as you have come to think.

    I believe what you've written has been well-thought out and you do make a very valid point that these are confusing to new people. Fortunately there are people who can help you and unfortunately you haven't met these people yet. I can understand that you would like to learn it all on your own, but asking other people who have been through these problems before is a fantastic way to learn from their mistakes and make some of your own. That's the best way to learn.

    Now let's start with the mistakes you've pointed out.

    1) Route to non-existing controller:

    When you see this error your first reaction should be to wonder “where did I say homme?” which should lead to your second reaction of searching your project (case-insensitively) for “homme”, in which case this should lead you to your routes file which will make you realize you've mis-typed the name. Yes, in this case the error is vague but with a bit of searching you'll figure it out.

    2) mispell the symbol :controller in the mapping file

    For this one it's a bit harder to understand. I agree that it should complain about keys that it doesn't recognize, but the idea of routing is that it should be quick. Error-checking your routes file is not the highest priority on the “How to Make Routing Fast” list. Ease-to-debug is sacrificed for speed here.

    3) Linking to non-existent pages

    Yes this one I completely agree with is very hard to debug and I didn't know what caused the error until somebody explained to me that I was using a singular route instead of a plural. I think this is being improved in Rails 3, but this is pure rumour.

    Mistake #4: misspelling :confirm

    link_to can support attributes for HTML which is why it cannot check for valid keys here; the attributes for the a tag may change which would mean that any attribute / option checking would have to as well. Seeing that your dialog box doesn't come up should make you look twice at your code and see that you've done this.

    Mistake #5: Mistyped property names in data model validation rules

    Obviously here you'll see in the error that it's called “nam” which should lead you back to validates_presence_of definition which should make you realise you've missed the letter.

    Mistake #6: Misspelled formats

    The first line of this error message says “NameError in PostsController#index”, which should lead you to the index action of the PostsController. Again: do a search for HTM (case-insensitive) in that file and that'll find it for you.

    Mistake #7: mistyping of a symbol in the context of data binding

    This is based on how hashes work. When you attempt to access a key that has no value assigned to it, by default Ruby will return nil so you're effectively calling Post.new(nil). Again, check your code. This one was a bit hard for me to spot initially too.


    Now, to find those people that can help you. There's http://stackoverflow.com if you like posting questions and having them answered by reputable people or you could go play IRC Roulette on Freenode in the #rubyonrails channel and finally for what I deem the best, most personable help around, the #railsbridge channel is always looking to help new people and build a more inclusive rails community.

    Lastly, if you wish to talk about anything Rails I have a weekend coming up and I'll be available on GTalk and Skype for quite a large portion of it and I would find it interesting to speak to a new-to-rails person. I'm radarlistener@gmail.com on Gtalk and simply radarlistener on Skype.

    Hope this helps you, help yourself.

  18. dsifry says:

    As a Rails Newbie myself (having done LOTS of programming in many other languages, and web programming for 10 years), I can relate to your issues. There's lots of Rails-isms that made me scratch my head and ask myself, WTF? Fortunately I have a great group of people around me who are willing to give advice and help, and the IRC channel actually does help as well, if you can figure out how to phrase your question in a short, reasonable way. The other thing I find is that by doing constant iteration and testing, I can usually localize the issue fairly quickly. The debugger command in my code, along with mongrel and irb are quite helpful as well.

    Overall, it's a reasonable tradeoff – but it would definitely be nice to have a set of more intermediate tutorials or guides for people who are still getting their feet wet with Rails. Another thing I'd love is a really good quick reference guide – e.g. common commands and options for Rake, for script/generate, routing, etc. Perhaps I'll just have to write one…

  19. Andre L says:

    Anticipating specific kinds of mistakes has costs — the framework authors spend time on that rather than something else, the framework gets heavier, and the extra error handling may impact performance. You have to find a framework that makes trade-offs you're comfortable with. In my case, I'd rather skimp on anticipating every spelling mistake in favor of a framework that is dynamic, vibrant, and quickly-evolving.

  20. Jelly says:

    Why are people even bothering to debate this? This has to be the stupidest criticism of Rails I've ever read. Has it really gotten to the point that this is all that's left to criticize? You'd have been much better off telling us how Rails can't scale and that we should be using Django or something.

  21. mattmcknight says:

    The stupidest part about this is that he is suggesting that compilers (or the mysterious “static” analyzers) provide better error messages. Interestingly enough, the RubyMine IDE provides any excellent static analyzer that underlines many of the errors listed here.

  22. mattmcknight says:

    “I think you are discovering the fundamental trade-off of static vs dynamic languages.”
    I wouldn't call this a tradeoff, it's just a difference as to when things are evaluated. You can run into the exact same run time vs. compile time error checking with uncompiled JSPs or use of reflection in Java or C#.

    The fundamental tradeoff is performance.

    (definitely agree with you on the capability of the IDE in these circumstances.)

  23. Zoltan Olah says:

    One of the greatest misconceptions with Rails that I've seen is that it is often thought of as a 'newbie's web framework' because it is so high level and you write such a small amount of code.

    I've seen beginner web programmers have a really hard time with rails because they don't understand the fundamental concepts that rails has abstracted away from the programmer.

    I think the ideal target audience for Rails is someone who more or less would have previously written a similar sort of framework along the way of writing their webapp. Now they can skip this laborious step and move directly to implementing their app with an architecture already in place.

    These people aren't phased by 'obscure' stack traces and interpreter errors but understand where they are likely to come from and will track down the problems pretty quickly.

  24. Greg says:

    I wish more developers, particularly in dynamic languages, particularly in javascript would pay attention to validating arguments.

    Many of these problems do not require static analysis- they can be checked either once when Rails loads the files or for every time the code is executed. Static checks would avoid runtime overhead. However, some of the possible checks would require a sophisticated type system.

  25. Greg says:

    I wish more developers, particularly in dynamic languages, particularly in javascript would pay attention to validating arguments.

    Many of these problems do not require static analysis- they can be checked either once when Rails loads the files or for every time the code is executed. Static checks would avoid runtime overhead. However, some of the possible checks would require a sophisticated type system to be able to check statically.

  26. eladmeidar says:

    i can't believe i wasted my precious time on this crap.

  27. dasil003 says:

    This is so true. It all started with the infamous 15 minute blog video from DHH back in '04. That pretty much guaranteed that for the rest of time the uninitiated would think of Rails as a super-easy toy framework for programming lightweights.

    But the reality is that Rails is hardcore Ruby–some of it very ill-advised Ruby for any project less ubiquitous than Rails–and it's written for serious web programmers. Unlike say PHP where you can muddle through a lot of applications without much training, writing any non-trivial app in Rails is going to through some curveballs your way. If you're not willing to roll up your sleeves and learn the finer points of Ruby meta-programming then all your productivity gains in Rails are going to be nullified the first time you see a weird error message (which happens frequently, especially before you are familiar with the language).

  28. Hi Zef,

    I agree with your sentiment – watching beginners work with Rails make typos and become unaware of how/when they made a mistake can be frustrating.

    What often happens is that beginners learn to recognize these fairly opaque errors and later dig a little deeper to realize what’s actually going on, along with picking up other web development debugging techniques like Firefox’s Firebug, or Safari’s Inspector where you can look at the requests and responses.

    It’d sure be nice if there were better stack traces and more transparent errors. Do you have some ideas about how you’d patch things or redesign the various DSLs Rails uses to accommodate newer web developers apart from static analysis?

  29. Zef Hemel says:

    For all those offering their help and tips on how to debug Rails programs, I thank you. However, I'm not a struggling Rails developer. I look at Rails because we are developing a Rails competitor called WebDSL (http://www.webdsl.org). One problem that we try to solve is the lack of static analysis that many frameworks, like Rails, have. To point out that this indeed a problem, I'm writing a series of posts like this one.

  30. Wrong. It's not the framework's job to do anything. Rails isn't for lazy people. If you want an error-checking system, you build one.

    That would be a ridiculously easy Rails plugin. Just use the ruby2ruby library and grep it.

  31. pk says:

    Hi Zoltan,

    This is so true. I did couple of web projects and when started with third one was frustrated so much rework needs to be done. Rails solves these problems. When you have done some website you know typical things needed and RoR provides them out of factory. You don't need to reinvent the wheel.

    Price of using such framework always comes at cost you don't understand all the code that goes into framework. This is true with any kind of framework be it RoR, DJango or PHP MVC. As long as you know how to fix issues with framework or can find answers to them on a well supported forum using a framework should not be issue at all. RoR is one of the best community supported frameworks I have come across.

    PK
    http://roorky.com
    Interactive Programming Books

  32. none says:

    lol

    you are a pathetic loser to even waste your time to write such a post. very funny! that really made my day.

    and good luck with webdsl.

  33. voidspace says:

    My goodness, the Rails crowd doesn't like any criticism does it. :-)

    Nice post. Any framework developer should consider error handling part of the API.

  34. luigimontanez says:

    Ryan, thanks for taking the time to craft a well-explained, reasonable response. Nice job.

  35. jgodse says:

    Let's see.

    1) When you used “homme”, it was an unknown symbol. Same as making a typo in C. Granted, in Rails, you have to know that “HommeController” is the Ruby class associated with “homme”. However, it is not vastly different from figuring out name mangling when you debug a C++ linking problem.
    2) Because Ruby is interpreted, the interpreter cannot say if a symbol should not be there. :controler may be a valid symbol. This program failed because a required symbol :controller was not defined.
    3) I agree with you on this one.
    4) Because Rails is interpreted, it cannot say if :confrm is required. Furthermore, I don't think that HTML requires the “confirm” attribute, so the Ruby wrapper cannot require it either. If you were coding straight HTML, you would have the same problem.
    5) This is a huge problem in Rails. The underlying issue is that ActiveRecord is actually only partially coupled to the underlying data model. (This is good because it allows easier interfacing with legacy data schemas). That is why you have to write migrations separately and keep them in sync with the attributes in ActiveRecord. Keeping knowledge of the data schema in two places is a bit of a kludge, but I'm not sure how to fix it without forcing all data definitions to reside in one place such as ActiveRecord.
    6) This is not a huge problem. Although Rails is flexible in which MIME types it supports, it should generate some kind of warning if the format is not well-known (such as html, xml, json, text). The lower-case uppercase thing is there for visual “beauty” I think, but it throws off people trying to parse error messages.
    7) Rails is pretty powerful when it comes to naming conventions. However, I find it confusing when one is able to create objects by passing params[:post] and not even knowing what was passed in. Also, if you try to include other parameters on your form, it is hard to get them in using the Rails naming convention, and you are effectively back to coding raw HTML forms. I find that doing a “view source” on the form page will quickly tell me what Rails generated as parameter names.

    Rails is very powerful, but in some places the abstractions leak and you need to know how it works under the hood. Overall, I find that Rails abstractions and “convention over configuration” let me get a system up & running quickly. When I was working with Perl/CGI, I had to understand raw HTML forms, and when I was with ASP.Net, I had to learn that framework (which IMHO is much more cryptic than Rails or Perl/CGI).

  36. Geraldo says:

    Ryan Bigg don't worry. Your advice is going to be useful coz the site is indexed.
    One sign that rails got there is the amount of bashing it gets.

  37. Jesse Dailey says:

    Engineering explanations aside, the fact remains that #4 is a game-breaking, absolutely should never happen, serious flaw in a web application. To dismiss it as 'you should notice', is like saying, if you want to code with Rails, 'hire more testers'.

  38. pradkris says:

    Thanks for your valuable comments. I personally feel the author is explaining about improving error messages and not trying to reach the community for help. I mean trying to debug a typo should not take a longer time like posting on SO, etc. Also in a team setup or a large scale system you might sometimes wonder if there is actually a class with name homme.. This is jus my humble opinion.

  39. pradkris says:

    Thanks for your valuable comments. I personally feel the author is explaining about improving error messages and not trying to reach the community for help. I mean trying to debug a typo should not take a longer time like posting on SO, etc. Also in a team setup or a large scale system you might sometimes wonder if there is actually a class with name homme.. This is jus my humble opinion.

  40. Lenary says:

    Jesse, it remains that you should test everything that is 'game-breaking'. If a peice of javascript is a 'game-breaker', write some Behaviour Tests that run in something like selenium so that can simulate and test the javascript correctly.

    If you are only relying on doing a once-through of the app for your testing, especially for 'game-breaking' logic and behaviour, you only have yourself to blame. There are many good and viable BDD tools avaliable, as well as normal TDD. (I'm not sure if conventional TDD will cover this particular behaviour)

    I am almost 100% sure there will be tutorials online for getting selenium setup, or you can find help on stackoverflow.com or irc as you see fit.

  41. Armando Fox says:

    I have to agree. I’ve been running a software engineering course at Cal Berkeley where we use Rails, and we do indeed use both BDD and TDD.

    Zef – your complaints are well founded, but we may disagree on the best solution. In particular, while static typing would probably address a lot of these, it would also make it more difficult to do some of the very things Rails does to minimize the amount of code one has to write. Dale’s point, with which I agree, is roughly “with great power comes great responsibility”, and in the Rails community that responsibility takes the form of doing BDD & TDD and making testing and test coverage a first-class element of your develpment cycle from day one.

    Email me if you want to continue the thread!

  42. I actually agree with a lot of this. Too many Rails developers grew up with Rails and have either forgotten what it’s like to experience these kinds of things, or are in positions where they teach Rails and can hand-hold students through these issues.

    The thing I took away from tis is that there really are some cases where Rails could be improved a bit to display better messages. I’ve seen places where that happens. If there’s a ControllerNotFound exception, ake the message give suggestions as to what happened as part of the exception.  Little things go a long way. 

    Rails gets so many thing right. There’s no reason to take criticism like this as anything other than “hey, that’s something we should think about.” It’s not 2007 – we don’t need to be defensive.

Trackbacks for this post

  1. uberVU - social comments
  2. rofrol's status on Thursday, 12-Nov-09 03:22:07 UTC - Identi.ca
  3. Caffeine Driven Development » Blog Archive » L33t Links #44
  4. A ‘When Rails Fails’ Follow-up « I am Zef
  5. When JBoss Seam Fails « I am Zef
  6. Static Verification: An External DSL Advantage « I am Zef
  7. The Third Bit » Blog Archive » Bend It ‘Til It Breaks
  8. Framework Failures for Beginners | Thought Clusters
  9. When Scala DSLs Fail « I am Zef
  10. Models are Programs « I am Zef
  11. Changing the World, One Paper at a Time « I am Zef

Comments are closed now.