Recently, I accepted a new opportunity within Amazon Web Services to build out the open source tooling story for AWS Serverless Applications. I’ve had an amazing ride for 5 years and 10 months building tools and generally driving improvements to the Rubyist experience on AWS, but this new opportunity was too good to pass up. One side effect of this transition is that Ruby will no longer be my everyday working language. In part because this role focuses a developer space that cuts across programming language, and in part because the existing tools on this team were written in Python. Python is a fine language, but I’ve been deep in the Ruby world for 6 years - it’s defined a lot of my career.
Yukihiro Matsumoto designed Ruby to maximize developer happiness. Using Java for my first ~3 years as a professional developer, I was more than ready to cast aside my AbstractSingletonProxyFactoryBean objects and write concise, expressive code. It delivered pretty quickly, and for a long time has remained my favorite language to use for side projects as well.
There’s talk of a sort of “bolt of lightning” moment where you “get” Ruby. I don’t think I had a single such moment…I had a few.
Ruby on Rails and the Ruby on Rails Tutorial
rails new hello-rails
I think in 2019 we take simple stand-up of a working web application a bit for granted, but having written Java-based websites at work, and trying (failing) to do the same at home for far more hours than I’d like to admit, the fact that Rails “just worked” was little short of a miracle. It wasn’t perfection, but the fact I could stand up an application skeleton from nothing, and make simple changes to logical parts of that code (and even have a CLI do most of that work for me) with instant results…it’s special.
Michael Hartl’s excellent Ruby on Rails Tutorial completed the picture. Rails isn’t magic, it’s a tool, and it has its own idiosyncracies and gotchas. But once I felt confident in my ability to build meaningful things in Rails…well I think my side projects got out of hand quickly. But in the best possible ways.
Building a Complex Library from Scratch
Building a new library is intimidating. When I was tasked with creating a full-featured data mapping library for Amazon DynamoDB, which became aws-record, the things I wanted it to be able to do felt pretty intimidating. It needed to be concise to write, performant, and provide an intuitive interface for creating/reading/updating/deleting items. You can accomplish the necessary features (dynamic attributes, for example) in any language, but Ruby made it relatively easy. Define an attribute using an easy to understand DSL, and all the relevant methods can be inferred and generated automatically:
require 'aws-record' class Table include Aws::Record string_attr :uuid, hash_key: true map_attr :metadata end
The amount of expressiveness that Ruby enabled when I dove into this project blew me away. Minimal boilerplate, minimal ceremony, powerful enumerators, even the proper dirty tracking for mutable objects like hashes and arrays was reasonable to implement. It felt right, and still does. (“Dirty Tracking” involves tracking changes to an object, whether or not the object reference has changed - some libraries only track changes to the object reference, which for basic types is often enough for most cases, but it works poorly for collection types. You can see examples of what the library can track in the documentation).
What’s special beyond the expressiveness is the feeling that Ruby was staying out of my way and giving me the tools to design the experience I wanted. Creating a Block DSL for separating table configuration from its definition made sense and was easy to implement, building an enumeration abstraction over querying and scanning that respected pagination on large tables was as easy as implementing the #each method, and Ruby was able to help significantly cut down the code footprint of performing transactional operations.
Ruby Support for AWS Lambda
Writing and launching native Ruby support on AWS Lambda is probably the proudest moment of my career so far (there’s a reason I’m jumping into serverless tooling). But in truth, what stands out to me about that project is Ruby itself. Specifically, what’s happened since Ruby 3x3 was announced - Ruby 2.5 clocks in somewhere over 150% faster than Ruby 2.0. I’ll admit that, for a time, I was starting to believe the hype that Ruby was slow, but thinking that it was okay because it was “fast enough” - developers aren’t free so it’s a net win until you reach a huge scale.
Well, don’t assume. It turns out that Ruby is actually pretty performant these days and just getting better. Which means that in the year 2019, Ruby still wins for me when it comes to the balancing act of expressiveness (time to ship) versus performance (cost to run). Whether that’s Ruby on Rails, an app running on AWS Lambda, or the script that represents all of your infrastructure as code, Ruby wins in my book.
It’s Not Over
The occasional claim of “Ruby is Dying” makes my eyes roll so hard that I’m rather amazed I haven’t suffered physical harm from it. Is Ruby the “flavor of the week” language getting shine and hype? Not anymore. Does it check all the boxes of “rich standard and third-party libraries”, “easy to learn and use”, “easy to test”, and even a large pool of developers that know it? Obviously it does. I’ll still be using it on a regular basis. It works and brings me joy.
If I had to quit my job and start a company tomorrow, I’d use Ruby to build out my product. I don’t know of another language that’s going to give me the same expressivity and agility, which translates pretty directly to “time to market”. It’s a first-class language. Matz and everyone else who made it what it is today deserve our deepest gratitude.
(Thanks to Olivier Lacan and Michael Hartl for reading drafts of this.)