Ruby: How Can Something So Beautiful Become So Ugly


TL;DR: this is a rant.

I’ve been using Ruby since 2013 and for most of my study and career, Ruby was may main, go-to programming language for almost everything.

Ruby’s syntax decisions make so much sense than most other languages. Although, it’s often argued against making programming languages like natural languages, Ruby programs flow like natural English.

(1..10).each do |number|
  print number * number
end

Ruby is also very well suited to be a tool for teaching programming as well as a very good pseudo-code language. I know Python has the lead in that role, however; I do not think Python syntax would make much sense to beginners; it would just raise so many unnecessary questions. Ruby on the other hand, does not have that problem. It has other kinds of problems.

Although I have wanted to write about this for sometime, the main motivation behind writing this post, finally, is the latest Ruby 3.0 release which was on the 25th December, 2020. This release is considered the most important release of Ruby so far and it’s been looked forward to for a long time. However, to me, it is the most disappointing.

Well, my biggest pet peeve with Ruby is that it is bloated and is getting even more bloated with every release by the addition of useless functions and operators to match Rails' ActiveSupport utilities ¯\_( ͠° ͟ʖ ͠°)_/¯.

The straw that broke the camel’s back was when types were introduced in the 3.0 release. This feature is the one I personally was most looking forward to. Adding static type analysis to the language is a great step to help find and mitigate type related errors in Ruby programs.

However, unlike PHP and Python (Ruby’s most famous contenders) where types are built into the language directly via type annotations, types in Ruby were added in the form of a new language called RBS, meaning; one would have to maintain separate .rbs files for type definitions. And now instead of writing the structure of a program once, it has to be written twice!

Here’s a small example of how it looks like:

# messenger.rb
class Messenger
  def initialize(medium)
    @medium = medium
  end

  def deliver(msg)
    "You have a new message: " + msg
  end
end

# messenger.rbs
class Messenger
  @medium: Symbol

  def initialize: (Symbol medium) -> void
  def deliver: (String msg) -> String
end

Maintaining the same structure twice is not an easy task in large software specially that static analysis is not enforced nor required by the language; thus, making writing types an annoying afterthought. The issue is, I don’t see why this is the chosen solution except that maybe to avoid rewriting Rails and instead, add new files for type definitions.

Some have already voiced their opinions on this matter, however; I find it weird that the Ruby community is not widely talking about this.

I know that no language is perfect and that each language has its use cases, however; I see that Ruby is tiptoeing around Rails; thus, it is cornered in the Rails web development area.

EDIT: here are parts of a comment I made replying to the backward compatibility point and other points made:

I see the backward compatibility point of view and it’s valid and actually an easy solution now. But my concern is maintaining software on the long run and having to keep track of two points of truth.

And the talk about using IDEs doesn’t make sense to me. I don’t need to use a specific tool to use the language.

I write C more than Ruby nowadays and I use header files but in this case, this is the best we’ve got, otherwise let’s use Go or Rust or whatever newer xD.

My point in the end is that Ruby deserves better tooling and more sane choices.

I want to end this with a quote by /u/mashatg on Reddit:

It is not only about this particular decision but disconcerted development and language’s direction in general. Incapability to learn from past mistakes and get inspired from more successful.

It is so sad to watch how an original and elegant design with lot of progressive ideas got butchered and ended up in current diffuse mess. It does not look like an attitude to fight back declining relevance. It looks like towel thrown into a ring and waiting referee to notice.