I don't want to see your backtraces

Seriously. People. If you’re writing a ruby application, at the top level, do some basic exception handling. Please. If I’m not actively debugging your code, don’t let me see your backtrace.

Interrupts

A prime example of this is dealing with interrupts. Let’s say I’m using your tool, and I hit ctrl-c. which raises an Interrupt which is a descendant of Exception, so you can raise and rescue it. Perhaps I started running something and then realized I made a mistake. Maybe something’s taking too long. But when I hit ctrl-c, I want the application to clean up and exit in an expedient manner. I don’t want to see 40 or so lines of back trace, none of which I care about.

Vagrant and Veewee are two nasty culprits. Both Vagrant and Veewee are really excellent tools, and I can’t live without them. However, I’m very indecisive and wind up killing startup of the application so I can go correct a config file, for example. What happens when I hit ctrl+c?

% vagrant status
^C/Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require': Interrupt
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/net-ssh-2.2.2/lib/net/ssh/prompt.rb:81:in `<module:SSH>'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/net-ssh-2.2.2/lib/net/ssh/prompt.rb:1:in `<module:Net>'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/net-ssh-2.2.2/lib/net/ssh/prompt.rb:1:in `<top (required)>'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/net-ssh-2.2.2/lib/net/ssh/key_factory.rb:2:in `<top (required)>'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/net-ssh-2.2.2/lib/net/ssh/authentication/key_manager.rb:2:in `<top (required)>'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/net-ssh-2.2.2/lib/net/ssh/authentication/session.rb:4:in `<top (required)>'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/net-ssh-2.2.2/lib/net/ssh.rb:11:in `<top (required)>'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/net-scp-1.0.4/lib/net/scp.rb:4:in `<top (required)>'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/veewee-0.2.3/lib/veewee/session.rb:3:in `<top (required)>'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/veewee-0.2.3/lib/veewee/command.rb:1:in `<top (required)>'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/veewee-0.2.3/lib/veewee.rb:2:in `<top (required)>'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:59:in `require'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:59:in `rescue in require'
        from /Users/adrien/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:35:in `require'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/veewee-0.2.3/lib/vagrant_init.rb:2:in `<top (required)>'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/vagrant-1.0.2/lib/vagrant/plugin.rb:68:in `load'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/vagrant-1.0.2/lib/vagrant/plugin.rb:68:in `block (2 levels) in load!'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/vagrant-1.0.2/lib/vagrant/plugin.rb:42:in `each'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/vagrant-1.0.2/lib/vagrant/plugin.rb:42:in `block in load!'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/vagrant-1.0.2/lib/vagrant/plugin.rb:37:in `each'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/vagrant-1.0.2/lib/vagrant/plugin.rb:37:in `load!'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/vagrant-1.0.2/lib/vagrant/environment.rb:514:in `load_plugins'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/vagrant-1.0.2/lib/vagrant/environment.rb:97:in `initialize'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/vagrant-1.0.2/bin/vagrant:36:in `new'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/gems/vagrant-1.0.2/bin/vagrant:36:in `<top (required)>'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/bin/vagrant:19:in `load'
        from /Users/adrien/.rvm/gems/ruby-1.9.3-p0@global/bin/vagrant:19:in `<main>'

OH MY GOD.

What would it take to prevent this horrible abomination from being spawned?

begin
  My::App.do(things)
rescue Interrupt
  warn "\nAborted!"
  exit 1
end

Notice that this won’t interfere with signal handling, so if you choose to attach a signal handler for ‘INT’ then a top level rescue of Interrupt won’t prevent this. You can do both, actually - attach a signal handler for a specific exception and then provide a top level rescue to catch anything missed.

Seriously, interrupts are not a bad thing, but they should be handled appropriately. If you need to do cleanup, then do it and get out quickly enough so that I don’t have to keep mashing ctrl+c. If there’s nothing to do, then quietly exit instead of dumping 45 lines of backtrace into my terminal.

In the wider case, throwing stack traces and verbosely displaying all errors in a system that’s been released and has active users is taking the wrong approach. Users should be able to turn on verbose output and error notifications but this shouldn’t be standard. If your project is remotely successful, you’ll have more users than developers; the user case is the one that you should optimize for.

At the end of the day, we all want to write and use applications that are well designed and helpful, and decent error handling and reporting seems to be a component that’s somewhat neglected. If some sort of internal programming logic failed horribly then by all means throw a stack trace, but this shouldn’t be the default behavior under other conditions.