Crash-landing into Ruby


In my new job at JT I’m very fortunate of working in a team in which different technologies, disciplines and experience levels are melted together. This is a great opportunity for learning if you don’t mind biting the bullet of going out of your comfort zone.

The first opportunity for doing so was to take ownership of the Ansible scripts that deploy the services of our team. It is quite fulfilling to see how we are getting closer to the devops ideals.

This crash-course on Ansible was intense but having used Puppet in the past not so traumatic. A bigger challenge lies on the fact that 80% of JT’s codebase is written in Ruby and RoR culture permeates everything. This includes a non-trivial amount of the code owned by my team so I decided to speed-learn Ruby to improve the communication and cohesion with my pals.

Tattoo of the Ruby logo

I won't be surprised if someone at the office is tattooed this way.
Source: the tattoo world convention.

You can pick a new language blazingly fast if you have a solid background in development including experience on the paradigm of the target language (single-dispatch, single-thread OOP, check), its main features like higher order programming (things like sandwich code are just the loan pattern, check), reflection (check), etc.

What else you need to master? Syntax, semantic subtleties and what is idiomatic in this new language1. Fortunately you can get all of that by deliberately practicing some tutorials and koans. I got a stupendous return on the Neo Koans and the Metaprogramming Koans. I tried hard to understand every new piece of syntax the koans presented, got immediate feedback and created flashcards on the go to commit those knowledge pills to my memory during the following days2.

I know kung-fu... I mean ruby

Speed-learning Ruby!

Apart from being able to better empathize with my colleagues I took a small issue related with a Ruby service. Slightly changing how ElasticSearch was invoked from the service didn’t look daunting. What could go wrong?

As the relevant piece of code had no test, I tried to add an RSpec. To my surprise, the query showing up at the test was incomplete, with no reference to the most important details (this was a more-like-this query and the text to be similar to was missing). I was puzzled because the code had been in production for a long time without reported issues and the more I looked at how Rails Jbuilder was used to generate the JSON query the most convinced I got of its correctness.

At this point some coworkers tried to help me. After some cold sweating, a coworker (thanks Jose) confirmed that the query was correctly generated in production boxes. Other colleagues taught me useful thinks like Pry-powered debugging (thanks Francesc) but the mystery remained inscrutable for hours.

Did I finally give up on the test and implemented the change right away? Nope. At some point I realized that the magically disappearing part of the query had "should" as key. If you are familiar with Jbuilder, you will know that it is a DSL based on dynamic methods in which whatever you call gets transformed in the attributes of a JSON.

Jbuilder.new do |company|
  company.name name
  company.president president.to_builder
end

In the previous example you are calling the undefined methods name and president but instead of miserably failing, method_missing intercepts the calls and the attributes are happily populated.

The reason this was working perfectly in production but failing in my test was RSpec itself! It was monkey-patching every object to enable the should matchers syntax with the unfortunate result of silently disabling the generation of half of the JSON query3.

Monkey patching vs dynamic methods

Language features clashing in the wild

Too much for my first Ruby use story! I finally rolled out a minitest test and successfully implemented the issue. My takeout: expressive and flexible languages are great but unsafe mechanisms can bite you hard. Monkey patching is extremely dangerous because it erodes your ability to reason about already written code4.

  1. And, to be fair, the frameworks, libraries and coding standards in use in your company. 

  2. Space-repetition systems like Anki feel like cheating. Don’t tell anyone. 

  3. You can disable this monkey patching by configuration

  4. There are safer alternatives such as Scala’s pimps or Ruby’s refinements.