Why Ruby Still Feels Like Home After All These Years | Caio Bianchi
← Home
I have been writing Ruby for more than fifteen years now. Started back when Rails 3 and 3.1 were current and everyone was still figuring out how to deploy with capistrano. Back then I chose it mostly because it felt pleasant to type. All these years later, after switching between languages for work and side projects, I keep coming back. Ruby is not the fastest or the trendiest, but it still feels like the one I reach for when I actually want to enjoy the work.
There are pieces of the language that rarely get mentioned in the usual pitch, yet they quietly remove friction every single day.
Take the way Ruby handles method visibility and refinement. It is not just private and public. You can open a class in a very contained way with refinements and only affect the code inside a single file or block. It is perfect for those moments where you want a little syntactic sugar without polluting the whole namespace. I use it often for test helpers or internal DSLs in larger codebases.
module QuotingRefinement<br>refine String do<br>def quote<br>"\"#{self}\""<br>end<br>end<br>end
using QuotingRefinement
puts "hello".quote # only available where we called using
Another hidden gem is how delegation works with Forwardable or the SimpleDelegator in the standard library. You get clean delegation without manually writing wrapper methods or pulling in extra gems.
require "forwardable"
class UserRepository<br>extend Forwardable<br>def_delegators :@connection, :query, :transaction
def initialize(connection)<br>@connection = connection<br>end<br>end
Or the even more ergonomic approach with delegate in Active Support when you are already in Rails, but the core version keeps things light for plain scripts.
Ruby also has Object#then and Kernel#tap that let you chain operations in a way that reads top to bottom without intermediate variables.
User.new(params)<br>.tap { |u| Log.info("Created #{u.id}") }<br>.then { |u| u.normalize!; u }<br>.save
Numbered parameters in blocks from Ruby 2.7 onward remove noise in short callbacks.
items.map { _1.price * 1.1 }
Or the fiber scheduler that landed more recently, letting you write concurrent code that looks sequential when you plug in an event loop.
Fiber.schedule do<br># cooperates with other fibers<br>end
Then there are the tools around the language that rarely get talked about outside Ruby circles. The Ruby LSP from Shopify gives excellent editor integration with minimal setup and works great alongside Steep or RBS for gradual typing. RuboCop keeps the style consistent without the ceremony you see in other ecosystems.
Ruby LSP
Steep
RuboCop
The standard library is another quiet strength. Instead of pulling in half a dozen small gems for common tasks, you often find what you need already there. Need a queue? Thread::Queue has been solid for years. Want to parse JSON with some streaming? The json and csv libraries handle most real world cases without ceremony.
On the performance side, Ruby 3.x brought YJIT, and now we have ZJIT landing in the 4.x series. ZJIT is a more aggressive JIT that builds on the same foundation but compiles hotter paths even more effectively. Early numbers show it closing a meaningful gap with languages that used to leave Ruby in the dust on CPU heavy tasks.
I ran a simple benchmark comparing a recursive fibonacci implementation across a few languages on the same machine. Ruby with ZJIT was within 2-3x of Go and not far behind optimized Rust in this particular case, while Python with pypy still trailed behind. Of course microbenchmarks are limited, but the trend line is real. Real applications see bigger wins on warm code paths once the JIT has time to optimize.
CRuby ZJIT (ZJIT development lives in the main repo)
Compared to Rust, Ruby wins on iteration speed for business logic. In Rust you often spend time fighting the borrow checker on things that are obvious at runtime. Go gives you excellent concurrency primitives out of the box, but the lack of generics until recently and the somewhat rigid error handling can make simple scripts feel heavier than they need to. Python is the closest spiritual cousin, yet it still requires more boilerplate for the same high level ideas, especially around classes and decorators.
The token efficiency point is real too when you are feeding code to models. Ruby’s syntax is dense in a good way. You express blocks with do/end or curly braces without needing extra keywords. Method calls rarely require parentheses when readability allows it. Hashes and keyword arguments are first class and compact. All of that means you can show a model more actual logic in the same context window compared to languages that force more structural noise.
A typical Ruby method ends up looking like this, tidy yet expressive:
def fetch_dashboard(user_id, include_drafts: false, limit: 20)<br>User<br>.where(id: user_id)<br>.includes(:posts, :profile)<br>.then { |scope| include_drafts ? scope : scope.published...