The other day Nate Berkopec asked this question on Twitter:
What is your least favorite part about Ruby?— Nate Berkopec (@nateberkopec) October 8, 2020
The thread had some thought provoking responses. I wanted to gather up some of the highlights for posterity and add my own commentary.
- Class variables are confusing
- Standard library is too low-level
- Meta-programming complexity
- Block vs proc vs lambda
Class variables are confusing
class variables! I can never figure out how they work— Aaron Patterson (@tenderlove) October 8, 2020
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
Much of the standard library is too low-level, like Net::HTTP, which leads to every Rails app bundling five different third-party HTTP clients.— George Claghorn (@georgeclaghorn) October 8, 2020
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
require 'open-uri' body = URI.open(url).read
For anything more complex, you’ll want to turn to a third-party library.
Meta-programming magic.— Chris Mytton (@chrismytton) October 8, 2020
Makes it awkward to discover where a called method is actually defined. Particularly frustrating for people just learning Ruby.
Hopefully tools like sorbet and rbs will help solve the discovery problem to some extent.
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
Block vs proc vs lambda is one of the least elegant bits with the most complexity compared to expressiveness.— Noah Gibbs (@codefolio) October 8, 2020
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.