ArticlesRuby programmingEngineering

Making my first Ruby gem

An experience report of writing and publishing a Ruby gem

A couple nights ago I was overcome with the urge to publish a Ruby gem. I have been writing Ruby for over 5 years and realized that it was time to give Ruby development outside of Stripe's Ruby infra a shot.

The package is eclair which I've wanted to build on and off for about three years now. It's worth its own article and context, so that's all I'll say about the gem itself here.

Requirements

I wanted to put a strong foot forward when publishing the gem. I do a lot of advocacy for writing high-quality Ruby code, mostly internal to Stripe, but also in public. Check out Ruby style guide and Effective Sorbet for most of the advice.

However, I haven't shared a whole Ruby library in public before. While I didn't expect to do a perfect job with the new stuff, I did want to get some things right.

Requirements:

  • Sorbet types, strict typing as the minimum.
  • Format code using rubyfmt.
  • Follow the Ruby style guide and Effective Sorbet articles.

Plus the stuff I would do in any design:

  • Provide a minimal API surface
  • Favor composition over inheritance
  • Make it hard to misuse by default

What I liked

  • I learned much more about the Ruby language's built-in tooling. It's cool to see ruby, irb, bundle, and gem all coming as a unit. Never really knew they flocked together. Getting Ruby to a newer version got all these things to a newer version too.

  • I'm a big fan of Clojure. In that ecosystem the mantra is everything is data. You often write configuration files as Clojure data structures. It was cool to see Ruby doing a similar thing, even if it forgot the data part. Gemfile, Gemspec, Rakefile are all Ruby code. I've definitely touched Stripe Gemfiles before but working with the RubyGem registry made it so much clearer how everything was fitting together.

  • The Sorbet VSCode extension can be slow at Stripe scale. On a few hundred billion lines less of Ruby it was amazing.

  • The minitest style of tests were nice. Having really only written RSpec-like tests, minitest feels a lot less magical and much smaller. Definitely reminded me of Go tests.

Where I deviated

I wanted to follow Effective Sorbet precisely but there was one thing that I could not do because I felt it was not appropriate for an open-source project.

Despite recommending them heavily, I didn't use T::Struct. I had used T::Struct initially but switched to plain old Ruby objects (POROs).

The reasoning: too many methods and too many bad methods, e.g. with, from_hash, etc. I can't feel comfortable shipping a gem with that much baggage of unexpected behavior.

Friction log

Stripes write friction logs. A friction log is basically a stream of conscious document that details everything and anything that went wrong or felt off about doing something. As a platform team lead, I get friction logs from folks across the company all the time. Those logs feed into what we choose to prioritize. It's a good system.

Here's a distillation of my frictions:

Ruby on Mac pains

By far I wasted the most time trying to get Ruby set up on my Macbook. Stripe-issued laptops solve this problem so I hadn't felt the pain before.

Apple ships an old version of Ruby (~v2.6) but all the tooling I worked with expected Ruby 3.0. In retrospect, had I got the Ruby version issue fixed earlier in the process so many things would have been smoother.

I felt this pain most when setting up Sorbet, in particular initializing my project with Tapioca. The exception thrown running bundle exec tapioca init wasn't super helpful but luckily enough to search me up a lead on how to fix it.

Once I eventually got rbenv setup and working, and had rebooted everything to use it instead of the Mac-default Ruby version things got better.

RubyGem yanks

In terms of wall-clock time spent, RubyGems caused me the greatest time loss. RubyGems is great but the search page results were a bit all too easy to misread. When I started my gem-ing, the first thing I did was see if the name "eclair" was available. I got so excited because I saw it was.

It was only after a long night of working on the gem itself that I finally went to publish my gem and it turned out eclair was not available. The gem had been recently "yanked," meaning taken down by its author. The gem was unused but I wouldn't be able to claim it for 3 months.

Betrayal, the worst of all.

Luckily only a little more than a day later after reaching out to the current gem owner, they graciously transferred the gem over to me. Thanks again, Cyrus!

Minor: VSCode and rubyfmt

The Ruby formatter rubyfmt doesn't seem to integrate with the Ruby LSP extension. It probably works with the old "Ruby" extension but VSCode steers people away from downloading that one.

This isn't a big deal, I just didn't manage to get format-on-save working.

Overall publishing a Ruby gem was a pleasant experience. I learned a good amount from it and it was nice learning how to set up Sorbet, rubyfmt, and other things I'd only previously used because someone else installed them for me. Thanks for reading!