The other day Nate Berkopec asked this question on Twitter:

The thread had some thought provoking responses. I wanted to gather up some of the highlights for posterity and add my own commentary.

In summary:

Class variables are confusing

Class variables in Ruby look a bit like instance variables, but they begin with @@.

To confuse matters further Ruby also has class instance variables, which makes searching for documentation confusing if you don’t know what you’re looking for.

This is one of those Ruby features that’s best left alone unless you have a very convincing use case that you can’t possibly solve using anything else.

Standard library is too low-level

Ruby is definitely showing its age a bit with the standard library. With Ruby 3.0 the standard library is going to become default gems. That might encourage some modernization in the interfaces provided so that more apps can rely on an out-of-the-box solution, rather than bundling their own.

Choice is a good thing. The fact that there are many alternatives to most parts of the standard library is a sign of a healthy ecosystem. But at the same time it would be nice if the libraries Ruby shipped with were good enough for simple scripts without having to pull in external libraries.

For simple HTTP requests when you don’t care about anything other than the body you can always use open-uri.

require 'open-uri'
body =

For anything more complex, you’ll want to turn to a third-party library.

Meta-programming complexity

My own contribution to the thread.

Ruby has some incredibly powerful and ergonomic metaprogramming features for creating DSLs. Yet this power and ease-of-use leads people to overuse and abuse metaprogramming, leading to hard-to-follow and hard-to-debug programs.

It seems enticing writing a DSL that captures the essence of the problem you’re solving. In reality code that isn’t written in a standard way (i.e. with classes and methods) is harder to understand because you have to understand the DSL before you can understand what the code in the DSL is doing.

Block vs proc vs lambda

Ruby has too many ways of creating and calling code closures. There are some subtle differences between the various forms. When faced with a situation where I want to use them I can never remember what the trade-offs between them are.

This isn’t an easy problem to solve, since there’s code out there using all the different forms. It’s one of those things that you get used to, but is a definite sharp edge for people that are new to the language.


Ruby fared pretty well in this thread, considering it’s a 25 year old language. There are definitely some rough edges, but I still find it to be one of the quickest and most fun ways to go from idea to working code.

I’m starting a newsletter!

If you’ve enjoyed reading this post then you might also enjoy my newsletter, where I’ll be writing about Ruby, software, tech and life.