On my last job I tried to go back to Ruby programming language. Not really by choice – but just because it was a language and was familiar with, and my last two jobs in Clojure didn’t really work out for me (not because of the language, really). I even imagined that some things would be easier in Ruby, specially while working with relational databases (something I really did miss while I was working in the Clojure language).

At the time, in my opinion, Rails was still a great framework – maybe a little too complex. And I imagined that somehow there could be better ways of doing Ruby code that could use the best parts of Rails and avoid the worst (like ActiveRecord, for example, maybe even substitute it with Sequel or ROM).

No a year and a half later I’m back to Clojure – and I will probably never go back to Ruby. And the reason is quite simple: I was working with Ruby doing exactly the same code that I did about three years ago. Rails didn’t change that much, but still was a huge mess when you had to upgrade from one version to another. The same problems I had in the past kept happening again and again, and it felt like a huge step backwards for me.

In the past I was one of the people saying that Ruby was a multi paradigm language, and in my last job I tried to do functional programming in Ruby. Now, I know it is completely absurd! Ruby is an object-oriented programming, the same as Java and Python (and very, very different from Smalltalk for example).

High order functions are great ways of composing different behavior, but the Ruby answer for that was… blocks. Instances of class Proc. A different syntax that allows you to pass a single block into a method, and maybe transform symbols in Procs, but that’s about it. There’s no way to receive two or more blocks in a function, and when you return a proc, it’ll be an object – not a function – and it’ll capture all current bindings (so you can leak memory in some cases).

This by itself disqualifies Ruby as a functional programming language, but it becomes worse – it’s almost impossible to use immutable data structures. Both Sinatra and Rails seems to prefer that you would mutate your record before passing it to the view; and also lots of libraries expect Ruby Arrays and Hashes instead of, let’s say, Hamsters objects (one of the best implementations of immutable data structures in Ruby). Even worse – it is common in Ruby to mutate the arguments you passed through functions with .delete or something else so if you’re trying to .freeze your hash before passing it to a function you can get an exception:

# This is common in Ruby. Why?
def find_people(args={})
  id = args.delete(:id)
  name = args.delete(:name)
  if id
    #....
end

But the worst part is that there are constructions in Ruby that could be using to get a more functional approach – for example :symbol.to_proc could be augmented to allow more parameters but it was never done; Refinements meant you could augment objects in a safe way (the same way you implement a type class for an already existing type in Haskell, for example, maybe even better because it was limited to a specific scope), but it seems that people preferred doing Monkey-Patch, pushing the boundaries of mutability to the limit! After all, now even your source code, your data structures, classes and implementations are mutable. Ah and let’s not forget the pipeline operator fiasco…

But I maybe could live without it. I mean, I programmed in object-oriented languages all my life right? But then come the worst – it is almost impossible to use Ruby without Rails. For example, I tried to use Sequel in a project where I would only need to consume from SQS, and had lots of specific problems that I had to be aware of – for starters, there’s too little documentation. Also, I didn’t want to follow the active-record pattern, and there are some APIs that I suppose I could use, but it seems like a private API or implementation detail, so it would not probably be a good idea to bet on it.

But the worst part is that besides Sequel and ActiveRecord, there’s no other way of doing SQL queries! There’s the ROM-rb project that I wasn’t able to make it work (the documentation is confusing, sparse, outdated, and by reading it I felt it uses a lot of “magical features” like dynamic class generation and so on…). The alternative is to use the database drivers directly – something that is completely impossible to do because this adapters have absolutely no documentation at all and they are all different from each other, besides being way too low level.

So, how bad could it was to return to Ruby? To be honest, was bad – worse than I thought. It seems that Ruby is trying to solve the problems in a “hacky way” all the time (I mean, look at the implementation of WebSockets for Rails – it needs REDIS!). There’s also the issue with parallelism (the proposed solution on Ruby 3.0 uses something called Guilds, and it is incredibly complicated specially comparing it with Clojure, where everything just works by default). Also, lets not forget that if you don’t use Rails (or MRI, for that matter), you’ll be faced with immature libraries, strange problems, different implementation details or strange defaults, and so on…

Also, let’s not forget the big picture: almost every library implements its specific DSL. It means, for example, that it’s hard to integrate things like “Sequel” and “Dry-Validations” without lots of “glue code”; also, Ruby (and Ruby libraries) decides for you things that you probably would want to break with an exception (like, what happens when you project a date/time to a moment in time that don’t exist, like at 00:00 on the day that changes to Daylight Saving Time, or how to serialize BigDecimal to JSON, and so on), which translates to “strange inconsistencies happening in production that are hard to reproduce because they only happen once a year”.

irb(main):024:0> Time.new 2019,2,29
=> 2019-03-01 00:00:00 -0300

So, yeah… I never though that I would prefer Javascript to Ruby…


2 Comments

PotHix · 2019-12-17 at 10:21

It’s interesting to read your opinions on that.

I’m currently working with Python and investing in Rust, but I still keep Ruby in my heart.
When I decide to use it, I just use the best parts: Ruby + Rails. I get the defaults and use it in the way people are using it. Guess what? It’s awesome. 🙂

Is it free of problems? Of course not! Many of the problems you described here are still there and I’m aware of it. I know how to deal with many of them while keeping my peak productivity and delivering software.

I’m also a big fan of Elixir and the way it borrowed some good parts of the Ruby community. I would totally recommend you to try it out. The problems will mention will mostly be solved and the good parts we know will be there in their own way (not the same, of course).

    Maurício Szabo · 2020-01-16 at 17:01

    Kinda 😀

    Clojure sees things very differently than other languages. For example, frameworks like Rails / Phoenix doesn’t exist. It is strange for people that are used to frameworks, but also liberating: everything needs to integrate with other libraries, without knowing then beforehand.

    Elixir does it a little better, but libs vary from immature (lots of time I wasn’t solving my problems, but making PRs to external libs) to things that didn’t work at all (SOAP is a big example on my project). I ended up preferring Clojure (Phoenix did inherit some of “catch-all, global state, callback” from Rails, and I wasn’t able to make simple APIs without it). Just a matter of personal taste, really 🙂

Comments are closed.