A few weeks ago, we ran into an interesting problem in one of our projects. We had a reports table with a reason column that used the classic Rails enum approach:
# db/migrate/XXXXXXXXXXXXXX_create_reports.rb
class CreateReports < ActiveRecord::Migration[7.1]
create_table :reports do |t|
t.string :title
t.integer :reason, default: 0, null: false
end
end
# app/models/report.rb
class Report < ApplicationRecord
enum :reason, {
spam: 0,
harassment: 1,
inappropriate_content: 2,
copyright: 3,
misinformation: 4
}, prefix: true
end
Everything worked fine until the client requested the ability to select multiple reasons for a report. A user could report…
We’re excited to welcome Planning Center as a Contributing member of the Rails Foundation - the first new member in 2026!

Founded in 2005 and headquartered in Carlsbad, CA (with a fully remote team), Planning Center builds church management software used by more than 90,000 churches around the world. Their platform helps churches organize the practical details of ministry - service planning, volunteer scheduling, donations, event registrations, check-ins, small groups, and more.
On Rails since day one
If it runs at Planning Center, it runs on Rails, and has for nearly two decades. That includes:
- Customer-facing web applications used daily by tens of thousands of churches
- API…
Joël and Sally cover all the bases as they look at improving their test suite performances times.
Our hosts lay out some spicy takes on various different test suites, comparing the key differences across the different forms of testing, where you might encounter pitfalls in each method, and how to make the most of each test.
—
Interested in exploring different test suites to see if they could improve your projects? Check out these articles on everything our hosts discussed today, as well as Joël’s talk on slow tests.
Avoiding Factory Bot - Why Factories? - Parallelisation in Testing - Joël’s Talk
Your hosts for this episode have been thoughtbot’s own Joël Quenneville and Sall…
If you…
The one where Falcon is powering Shopify at scale, where we find out about Herb Linter will support fix and where we found about Ruby 3.4.7 release.
The one where Rails announced 8.1.0RC1, Hanami announced v2.3.beta2 and Ruby Core assumes stewardship for RubyGems and Bundler
The one where Rails 8.1.0 is released, where Ruby 3.3.10 is patched, where Scott Harvey launched Rails Pulse project and Brad Gessler launches Phlex on Rails course
The one with Rails 8.1.1 release, where Jean Boussier does a deep dive into frozen string literals, where San Francisco Ruby is two weeks away, and Tropical On Rails launched the tickets.
The one where Ruby 4.0 is announced, where Namespace is renamed to Ruby::Box and deep_freeze is under discussion
The one where Ruby releases 4.0.0.preview2, Maedi proposes a new supported syntax for defining types, and 12 podcast episodes are published
The one with BFCM deals, where Xavier Noria and Kevin Newton shared insights about how constants work in Ruby and where Alessandro Rodi introduced DevToolsController for Rails.
37 Signals launches Fizzy, Bridgetown announces 2.1 beta 1, Bundle 4.0 is released, Wired wrote a strange article about Ruby, and Errol Schmidt published their Survey Results with Ruby/Rails leaders.
The one where the date was announced for Rails World 2026, where Aaron Patterson showcased the performance of object allocation in Rails 4.0, where Cookpad share how Rails help them scale, and Fizzy got API support
The one where Ruby gets a new homepage, Ruby 4.0.preview3 and Ruby 3.4.8 are released, Ryan Davis announced Minitest 6.0 and where Marco Roth gets the Rails Luminary award
The first edition of 2026: Where we look at Ruby 4.0, mruby announced the major release this year v4.0, Rails launched 8.1.2 and two old (pre Ruby 1.0) and stable gems were updated
The one where Ruby 4.0.1 is released, TruffleRuby 33 is released, Programming Ruby 4 enters beta, Google Summer of Code invites for Ruby projecs and where we found Ruby is token efficient
The one where Roadmap launched a Roadmap for learning Ruby, where Rails 8.1.2 is officially released, Devise reaches the 5.0 mark and where Intercom shares data about how they ship to production.
The one where RubyConf launched The Ruby Runaway - startsup pitching, where tiny ruby conf announced their event and where Joel talks about Phlex::TUI
Then one where Garry Tan president of YCombinator talks about Rails and Claude Code, Ruby Central launches organisations feature, Inertia Rails has a new website and JRuby 1.0.0.3.0 is released
The one where we get Hotwire Skills from Hotwire Club, Peter Cooper launches Planet Ruby, Evil Martians launches Tutorialkit.rb, Yuri Sidorov launched Ruby Community and new features approved to be implemented in Ruby
The one where 37 Signals launches Upright open source monitoring tool, Rubocop gets an experimental MCP server, Rails Designer shows us how to use Stimulus to record videos in Rails and RSpec launches v.4.0.0.beta1
Authors: Andrey Novikov, Backend Engineer, and Travis Turner, Tech Editor
Topics: Rails, Ruby

A deep dive extending Flipper in Rails: friendly actor IDs, team-wide flags, percentage rollouts, analytics events, and admin auditing.
Our client StackBlitz already had an in-house solution for feature flags in the admin panel. But as the product and team grew (alongside the launch and success of Bolt.new), so did the requirements for this feature: not only the need to enable flags for arbitrary groups of users, but for gradual rollouts, integration with analytics, audits, and so on. They decided on Flipper. Then came the interesting part: bending it to fit all of their different use cases. This is…
After picking up Minitest to complement the QA process of one of my retainer clients, I’ve had confusing errors in our test suite for a while. We were moving fast to cover critical parts of the application, so we never had the time to investigate the flakiness thoroughly.
While preparing my upcoming talk – Lost in Minitest, the missing guide for Minitest tourists – I dug into how Ruby on Rails pulls in Minitest. And there I found the answers to my burning question: why on Earth does the following test fail?
require "test_helper"
class CustomerIdentificationTest < ActionDispatch::IntegrationTest
before { @customer = customers.buffy }
it "checks proofs of identification" do
get …
From the moment RubyGems was first created in 2004, Ruby Central provided governance without claiming ownership, to support the Ruby community. Providing governance meant creating processes to provide stability and predictability. Avoiding ownership meant allowing the community to contribute, to the point where unpaid volunteers created and controlled the entirety of RubyGems.org for many years.
Last year, Ruby Central flipped that successful formula on its head. They now claim ownership of both Bundler and RubyGems, but refuse to provide governance. Ruby Central now claims sole control over all code and decisions, despite paying for only a few percent of the work required to create and…
Last month, we finished a big upgrade for a client. The client had 2 main pain points for their app. The first one was that they were using Rails 2.3 LTS with Ruby 2.5. The next big issue was that the test suite took 40 minutes to run, blocking engineers from merging code into the main branch, and also slowing down the whole feedback loop for every code change.
After finishing the upgrade (we got the application to Rails 8.1.1 and Ruby 3.4.7), we focused our attention on improving the test’s speed and we reduced the time it took to run the whole test suite (over 10k tests) from 40 minutes to around 4!
The Different Causes of Slow Tests
The Test Runner
The most popular test runners for…
Kaigi 2026 Is Approaching — Why the Global Ruby Community Should Pay Close Attention March 2, 2026 With April approaching, RubyKaigi 2026 is about to take place in Hakodate, Japan — and for the global Ruby community, this is not just another date on the calendar. It is a moment that often defines the technological … Continue reading
Kaigi 2026 Is Approaching — Why the Global Ruby Community Should Pay Close Attention →
This week I finished a particularly satisfying piece of work: adding request body parsing to Hanami Action. This finishes the story we started back in Hanami 2.3, where we significantly improved Hanami Action’s formats handling. Now you can also use formats to ensure your bodies are appropriately parsed for your expected media type handling.
With this change, Hanami Actions can now serve as fully standalone mini Rack apps, along with everything you’d expect from params handling. It also means we can do away with the one-step-too-far-removed-and-awkward-to-configure body parser middleware that until now we’ve needed in full Hanami apps. Step by step, things get cleaner and better…
I had the pleasure of reviewing a range of things…
Rails models default to using their ID in URLs: /articles/42. The to_param method lets you customize this—use a slug, hide the ID, or combine both for readable URLs. Exposing IDs isn’t dangerous if you scope access properly, but you might want cleaner paths.
Instead of…
…exposing IDs in your URLs:
redirect_to article_path(@article)
# => "/articles/42"
Use…
…the to_param method to return a slug instead:
class Article < ApplicationRecord
before_save { self.slug = title.parameterize }
def to_param = slug
end
redirect_to article_path(@article)
# => "/articles/understanding-rails-extensions"
Then find records by slug in your controller:
def set_article
@article = Article.find_b…
For…
In this episode, we look at a few different ways of improving the speed of a page. There are many paths to take. Some of them leaves a lot of optimizations on the table, whereas others are premature and adds complexity.
Direct link to podcast audio file
Sure feels like some combination of AI, the US military, and the AI military could bring an end to the world any day now, so I figured I'd better record one last show for posterity. Welcome me on this version's speedrun to the apocalypse!
So long as the EMP blasts don't nuke all our ham radios, write in to podcast@searls.co and I'll read it on the next release of the program. Over.
Be sure to click all these links while the clickin's good:
Pagination is always a feature you have to tackle in web application development in one form or another. A common approach is to use a gem, like Pagy, to provide the following capabilities:
-
Load records in batches (a.k.a. pages).
-
Previous and next links.
-
Numbered links to specific pages.
-
Bookmarkable links complete with browser history.
Definitely useful to decrease server load since you’re only loading a small subset of records per request. The problem with this approach is:
-
Most people bail after reading through the first couple of pages and not finding what they want.
-
Includes a lot visual information, design-wise, that not only consumes…
We can do better. With infinite scrolling, we…
Building a minimalist breathing app: no ads, no subscriptions, no mysticism. Just science-based calm, open source and free.
A flaw was found in rubyipmi, a gem used in the Baseboard Management
Controller (BMC) component of Red Hat Satellite. An authenticated
attacker with host creation or update permissions could exploit this
vulnerability by crafting a malicious username for the BMC interface.
This could lead to remote code execution (RCE) on the system.
Hey there, it’s Emmanuel Hayford. A fresh batch of Rails changes just dropped, let’s get into it.
Action Text to_markdown generates markdown links for blobs
When a rendering context is available, blob attachments now produce real markdown links ( for images, [title](url) for others) instead of plain-text placeholders.
Add to_markdown to Action Text, mirroring to_plain_text
Introduces to_markdown across the Action Text stack (Content, Fragment, RichText, Attachment), converting rich text HTML into Markdown with support for inline formatting, block elements, lists, links, tables, and more. The underlying BottomUpReducer has been extracted into its own class for reuse by both…
Render MissingAttachable as “☒” in plain text
MissingAttachable now implements …
This post is mostly a reaction and reflection on Jon Sully’s “Generative AI and Tech Writing: The Right Answer is to Disclose.” Strong agree! I wanted to zoom in on one part of Jon’s post: “Just Give Me the Prompt”
Seriously. If you are blogging, a prompt is good enough. It’s great! If you’ve got a thought or idea that you want to share with the world, just post that idea. It’s enough.
Jon’s post caused me to reflect on my own writing process. Over the course of the nearly 20 years (!) I’ve been writing here, I’ve become much more comfortable incorporating my initial “gosh, I have this thing to say in my head and I want to get it out”-compulsion into the the thing I…
LiveComponent with Cameron Dutro
Cameron Dutro returns to the show to introduce LiveComponent, a new library that adds client-side state and targeted re-rendering to Rails ViewComponent using Hotwire + Stimulus with minimal JavaScript. Chris, Andrew, and Cameron dig into why he built it, how it serializes component state and models, how updates flow from events to fast server-rendered HTML morphs, where it shines compared to plain Turbo/Stimulus, and how optional React support can help with migration and interoperability. Hit download now to hear more!
Links
HoneybadgerHoneybadger is an application health monitoring tool built by developers for…
One of my priorities this quarter was running a few AI pilot experiments. This was one of them.
When I mentioned the project to a teammate, he said, “You should write this up.” So here we are.
Others on our team had already been exploring embeddings, vector databases, and RAG. I’d been watching from the sidelines… until it was time to roll up my sleeves and build something myself.
The Problem
At Planet Argon, we manage several client projects. We live in Jira (I know… I know…). We keep decisions in Confluence. We ship code from GitHub. Over years a lot of institutional knowledge piles up across those systems… past bugs, old tradeoffs, and the “we tried that once” stories.
The problem…
In a move that surprised many of us — and one which I still can’t determine the business sense in making at all — Salesforce officially announced last week that Heroku will be moving into a “sustaining engineering model”. That’s essentially giant-software-corporation-speak for, “we’re putting this into maintenance mode”. The platform that taught a generation of developers to “push to deploy” has reached its investment limit from its owners 😕.
"Our work here is done"
Now, before you jump straight to “abandon ship!!”, there are real questions we should think about when looking ahead. Heroku is still an excellent platform, runs very stably,…
In this episode of RECORDABLES, we dive into the infrastructure journey behind Fizzy. Lead Programmer Kevin McConnell walks through the ambitious plan to give every customer their own SQLite database and the challenges the team ran into along the way. What started as a unique way to support both self-hosted and SaaS models evolved into a performance experiment, pushing multi-tenant design further than we had before.
But as launch day approached, the tradeoffs became harder to ignore. Kevin shares what worked, what got complicated, and the pivotal decision — days before release — to unwind months of work and revert to a more conventional setup. This conversation is a candid look at…
We're excited to announce that Ruby will participate in Google Summer of Code (GSoC) 2026! This program offers new open source contributors the chance to work on impactful projects supporting the Ruby ecosystem, such as RubyGems.org, RubyGems, and Bundler, with guidance from experienced mentors.
Google Summer of Code is an annual program run by Google that pairs open source organizations with new contributors (18+ years old with less than two years of open source experience). Contributors receive a stipend from Google to work on a defined project over roughly 12 weeks.
How to get involved
Our contributor wiki has everything you need to get started: project ideas, guidance on writing a strong…
I had never personally worked with embeddings, vector databases, or retrieval-augmented generation before this project. I knew the words. I did not know where the sharp edges were. Folks on our team do… but I felt it was time to wrap my own head around it.
What I did have was a real problem, a team that loves Ruby, and enough curiosity to see where things broke.
This is the story of that experiment… what worked, what surprised me, and what I’d tell another Ruby developer who’s considering something similar.
The Problem
At Planet Argon, we manage several client projects. We live in Jira (I know… I know…). We keep decisions in Confluence. We ship code from GitHub. Over years a lot of…
The first time I visited GitHub's HQ2 in 2012, they had a TV showing off their first animations of Mona and were using it to push their new tagline: Social Coding. The phrase certainly captured the moment we were living in, so in 2014, I borrowed it for the title of one of my more popular conference talks, The Social Coding Contract. The goal of that presentation was to warn audiences of the long-term risks of all these tools making it so trivially easy to publish and consume open source dependencies. Sure enough, what followed was a decade defined by the productive and destructive chaos of a ceaseless deluge of useful, but poorly-understood and under-maintained dependencies.
Thanks to the…
Authors: Victoria Melnikova, Head of New Business, and Travis Turner, Tech Editor
Topics: Developer Products, Open Source, Developer Community, Case Study

Konstantin Vinogradov, an open source and infra VC investor, and his co-founders are building the first permanent funding model for critical open source infrastructure. We dive into why OSE stands a chance at battling the Nebraska problem, and how Evil Martians contributed as a pro-bono donor.
If you're building a developer tool, your product almost certainly depends on open source code you did not write, do not control, and have probably never thought much about.
Konstantin Vinogradov, an open source and infra VC investor, along with his…
February 26, 2026 In recent years, Ruby and Ruby on Rails have quietly entered a phase of rapid, multidimensional evolution. Rather than a single disruptive change, what we are witnessing is a coordinated advance across the runtime, the framework, infrastructure tooling, and application capabilities. This shift has been especially visible in talks from RubyKaigi 2024–2026 … Continue reading Ruby 4 & Rails 8: A Multi-Front Acceleration of the Ruby Ecosystem →
(this post was written by hand btw - because I despise AI Slop rhythm, emojis and structure sooooo much)
I was recently asked to give a short AI-Coding Workshop for another company, which is outside my usual work. That made me recollect my thoughts on the whole matter, because there is kind of a rift in the industry, and also in the developer community.
On the one side, we have principled opponents and skeptics (especially on Mastodon!), on the other side we have the proverbial Vibe-Coders who use Agents without any supervision. I wouldn’t comment on the latter, but want to address the issues the former group usually has.
1.) Coding-performance related: results are worthless/the code is…
I often use timestamps, like completed_at as a boolean flag. It offers just a bit more (meta) data than a real boolean.
But of course on the UI you want to show a checkbox that a user can toggle instead of a datetime field.
I have done this often enough, that I created a simple concern that I use throughout my apps. Given above completed_at example, it gives you an API like:
@resource.completed?
@resource.complete!
@resource.complete=
So in your form, you can simply use form.check_box :completed and you’re off.
The concern is simple enough:
# lib/boolean_attributes.rb
module BooleanAttribute
extend ActiveSupport::Concern
class_methods do
def boolean_attribute(*fields)
fields.…
In January 2024 I published the first article on Rails Designer’s blog. It listed some of my best practices; most of which I still use today.
Over those two years there were almost 200 articles published on topics ranging from Hotwire, to Rails and Tailwind CSS. Next to that I’ve launched various tools and dozen open source projects. With all those articles, tools and other pages, the site slowly started to feel incoherent. Less consistent in style, feel and branding. So it was time to lose some weight and put on some fresh clothes! 🎩💅
I spent some time recently to build the site from scratch. Not coincidentally this was a good time to build it using Perron, the Rails-based static site…
Rails 8 (released November 2024) is the smoothest upgrade yet.
The focus?
Simplicity and performance - removing external dependencies while boosting speed.
Solid Queue, Solid Cache, and Solid Cable eliminate Redis for many use cases.
Built-in authentication removes the need for Devise.
Ruby 3.4 (latest stable) with continued YJIT improvements delivers excellent performance.
Note: This is Part 5 of our Rails Upgrade Series. Read Part 1: Planning Rails Upgrade for the overall strategy.
Before We Start
Expected Timeline: 1-2 weeks for medium-sized applications (easiest upgrade!)
Medium-sized application: 20,000-50,000 lines of code, 30-100 models, moderate test coverage, 2-5…
Authors: Yuri Mikhin, Frontend Engineer, and Travis Turner, Tech Editor
Topics: React, TypeScript

Transform your React development with contract-first APIs. Generate TypeScript types, build type-safe clients, and develop with mocks while backend implements—no more waiting, no more integration chaos.
Most React apps have a problem: frontend types and backend reality drifting apart. You copy API shapes into TypeScript files, backend changes something, and you find out in production. This guide fixes that by making your OpenAPI spec the source of truth—generating types, clients, and validation schemas automatically so contract mismatches break builds instead of production. You'll also set up…
RubyMine enhances the developer experience with context-aware search features that make navigating a Rails application seamless, a powerful analysis engine that detects problems in the source code, and integrated support for the most popular version control systems.
With AI becoming increasingly popular among developers as a tool that helps them understand codebases or develop applications, these RubyMine features provide an extra level of value. Indeed, with access to the functionality of the IDE and information about a given project, AI assistants can produce higher-quality results more efficiently.
To improve AI-assisted workflows, since 2025.3, RubyMine has also been able to…
Getting nondeterministic agent into deterministic guardrails
AI agents don’t reliably follow your instructions. Here’s how I made it hurt less.
My context:
- I currently work on a 12-year-old Rails legacy code base
- The code base is undergoing modernization. Some of the large Active Record classes have been split into smaller ones, each into its own bounded context. Events are becoming a first-class citizens in the code. We also pay close attention to keep direction of dependencies as designed by context maps.
- The client has a GitHub Copilot subscription. I mostly use Sonnet and Opus models.
The basic setup
Initially I started with the basics. I was curious where it would get us. There’s…
Oops, nearly missed these weeknotes. Let me make this a quick one just to sneak it in and keep the streak alive (6 months and counting!)
My big achievement this week was getting Hanami Minitest ready for feedback. Check out my post for a preview of the generated files and where I’m looking for help. This has already generated a whole lot of great feedback and discussion. Thank you everyone for sharing your thoughts!
I had a couple of very old hanami-rspec preview releases yanked from RubyGems.org (thank you Colby!), so now they no longer confuse the bundle outdated command.
Aaron added a nice new feature to Hanami CLI: a --name option to allow the app name to be customised. I reviewed…
RubyGems 4.0.7 includes enhancements and documentation and Bundler 4.0.7 includes enhancements, bug fixes and documentation.
To update to the latest RubyGems you can run:
gem update --system [--pre]
To update to the latest Bundler you can run:
gem install bundler [--pre]
bundle update --bundler=4.0.7
RubyGems Release Notes
Enhancements:
- Add Gem.disable_system_update_message in setup.rb. Pull request
#9020 by hyuraku
- Print message when signing in with an existing API key. Pull request
#9312 by hsbt
- Installs bundler 4.0.7 as a default gem.
Documentation:
- Document gemspecs must be deterministic. Pull request
#9321 by fxn
- Remove “##” from a comment to require. Pull…
Bundler Release Notes
Enhancements:
We helped a global sportswear brand modernize its archive by strengthening its integration, not rewriting it.
Continue Reading
Sally and Aji flick through thoughtbot’s guide to best practices in a bid to brush up on their coding habits.
Our hosts discuss key ideas from the guides that stand out to them the most, why they’re considered to be good practice, as well as reviewing the cons of complex writing and the benefits of simple coding.
—
Be sure to check out Sally’s new repo Michel if you’re looking to create an appointment database, and check out the thoughtbot guides for more general coding advice.
If you’ve got some spare time and want to hear Aji’s talk on breaking the enigma code you can watch that here.
Your hosts for this episode have been thoughtbot’s own Sally Hall and Aji Slater.
If…
The one where 37 Signals launches Upright open source monitoring tool, Rubocop gets an experimental MCP server, Rails Designer shows us how to use Stimulus to record videos in Rails and RSpec launches v.4.0.0.beta1
February 23, 2026 Ruby has traditionally shipped with a single, built-in garbage collector tightly coupled to the VM. With Ruby 3.4, that assumption begins to change. Feature #20470 introduces an experimental Modular Garbage Collector API, allowing CRuby to load alternative GC implementations at runtime. This marks one of the most significant architectural shifts in Ruby’s … Continue reading
Pluggable Garbage Collectors in Ruby: Exploring the New Modular GC API →
## Summary
`Rack::Directory`’s path check used a string prefix match
on the expanded path. A request like `/../root_example/` can escape the configured
root if the target path starts with the root string, allowing directory listing
outside the intended root.
## Details
In `directory.rb`, `File.expand_path(File.join(root,
path_info)).start_with?(root)` does not enforce a path boundary. If the server root
is `/var/www/root`, a path like `/var/www/root_backup` passes the check because
it shares the same prefix, so `Rack::Directory` will list that directory also.
## Impact
Information disclosure via directory listing outside the configured root
when `Rack::Directory` is exposed to…
## Summary
`Rack::Directory` generates an HTML directory index where each file entry is rendered as a clickable link. If a file exists on disk whose basename begins with the `javascript:` scheme (e.g. `javascript:alert(1)`), the generated index includes an anchor whose `href` attribute is exactly `javascript:alert(1)`. Clicking this entry executes arbitrary JavaScript in the context of the hosting application.
This results in a client-side XSS condition in directory listings generated by `Rack::Directory`.
## Details
`Rack::Directory` renders directory entries using an HTML row template similar to:
```html
%s
```
The `%s` placeholder is populated directly with the file’s basename. If…
## Summary
Nokogiri's CRuby extension fails to check the return value from
`xmlC14NExecute` in the method `Nokogiri::XML::Document#canonicalize`
and `Nokogiri::XML::Node#canonicalize`. When canonicalization fails,
an empty string is returned instead of raising an exception. This
incorrect return value may allow downstream libraries to accept
invalid or incomplete canonicalized XML, which has been demonstrated
to enable signature validation bypass in SAML libraries.
JRuby is not affected, as the Java implementation correctly
raises `RuntimeError` on canonicalization failure.
## Mitigation
Upgrade to Nokogiri `>= 1.19.1`.
## Severity
The maintainers have assessed this as **Medium**…
Co-author: Praveen Awasthy

Processing documents into structured data
Introduction
If you work anywhere near operations, compliance, payroll, benefits, or support, you already know: documents are everywhere. Forms, notices, letters, PDFs, scans, uploads, emails — each carries critical information, each follows a different format, and each requires someone (or entire teams) to read, interpret, and act.
For years, the industry has solved this the same way: manual review, brittle templates, one-off parsers, or vendor tools that work great — until they don’t.
There’s no shortage of players in this space. Some excel at OCR (Optical Character Recognition). Some are great at classification. Some shine…
The death of a software craftsman
Well, it happens a lot ‘round here
You think quality is a common goal
That goes to show how little you know
Developers work hard over the years to cultivate tools and techniques to improve the quality of the construction of their software. These tools and techniques are slowly becoming even more useless than they may already be. We must adapt by either going all-in, totally opting-out, or finding a middle ground by tracking the AI code-generation craze to fill the gap as a hand-coding artisan.
All content on this page was
created without any assistance from a Generative AI
(including the Graphviz diagrams!). My
em-dashes are my own. I would also…
Authors: Vladimir Dementyev, Principal Backend Engineer, and Travis Turner, Tech Editor
Topics: Performance, Rails, DX, Performance & scalability, Ruby, Docker, PostgreSQL, Node.js

An exhaustive and documented Docker configuration for developing Ruby and Rails applications
This post introduces the Docker-based development runtime configuration I use for building Ruby on Rails projects, both in an old-school way (by writing) and in a modern way (by instructing LLM agents). This configuration has been helping Evil Martians and many other teams across the globe to ship amazing products without worrying about local setup intricacies. Read on to learn all the details, and feel free to use it, share…
I was testing for SQL Injection on a target the other day, and after a little fuzzing indicated that there might be a vulnerability, I wanted to use SQLMap to make data exfiltration easier. But this vulnerability was part of a websocket request, and unfortunately, SQLMap doesn’t support websockets.
One solution to this problem is to introduce a little proxy between the websocket endpoint and SQLMap and convert HTTP requests into websocket ones.
Welcome to Hotwire Weekly!
Welcome to another issue of Hotwire Weekly! Happy reading! 🚀✨
📚 Articles, Tutorials, and Videos
Drifting Ruby: Debounce - David Kimura shows how to build a debounced live search in Rails using Stimulus and Turbo, cutting down unnecessary requests while keeping the UI responsive.
Remote Ruby: Bridge Components, Swift UI and more with Joe Masilotti - Joe Masilotti joins Remote Ruby welcomes to talk about bridge components, how Hotwire Native interacts with SwiftUI, and practical patterns for mixing native UI with Turbo/Rails apps.
Record video in Rails with Stimulus - Rails Designer shows how to build a video recorder UI in Rails using a lightweight Stimulus…
My daughter got me a new guitar stand I have set up in the living room. This lets me see my guitars more easily, which I enjoy. Inspired by that, I wanted to make my own stand to support two guitars in the office area of my bedroom.
Build 🔗
I used a piece of hickory wood that’s been waiting to be put to good use for this project. Armed with an idea of the visual look I wanted, I drew the shape I wanted to end up with. I used my pull saw to make the initial cuts, with the hickory secured in a vice.

With my vertical lines cut, I need to connect them horizontally. I drilled a hole with a forstner bit in the corners with a wide enough diameter to use a jigsaw to connect the lines.

I cleaned up…
It’s 2026. Everything’s awful. The only news you hear is bad news. I figured I’d spend a few hours trying to make some things sound a little better. So I installed new pickups in some of my guitars.
Single Coils 🔗
I recently came into a Squier Affinity strat. I would confidently describe the stock instrument as “fine”. Particularly for an entry-level guitar. It’s not even Classic Vibe level! The body is thinner, but that makes it interestingly light. The neck profile isn’t to my liking, but the fretwork is better than I’ve seen on much more expensive guitars. The tuning machines aren’t great, but I like the vintage style.

All that aside, I got it for one reason.

It’s a really good starting…
Creating an AI blog editor with Claude Skills—like having a code reviewer for your writing when you don't have a human editor.
Coming from a JavaScript background, I’m used to handling permissions with simple if statements. Maybe a global “roles” object, or a quick check on the frontend that hides buttons if the user isn’t an admin. Working with Rails and Pundit forced me to think differently about authorization as a system.
Understanding the request flow and where helper methods come from would’ve saved me a lot of time upfront.
Philosophy of Pundit
At its core, a Pundit Policy answers the question:
Can \$USER take \$ACTION on \$RESOURCE?
A Policy Scope answers:
Scope down \$COLLECTION to only the ones \$USER can access.
These two layers keep authorization clean and explicit:
What belongs in…
In just a few months, AI coding assistants went from autocompleting lines to shipping entire features in minutes.
Everyone who knows how to utilize AI can produce several times more code than before.
However, AI is not perfect and we, as humans, still need to review the code it produces.
Reviewing someone else’s code is not the most exciting part of the job - it’s just boring.
With thousands of lines of code, it’s easy to unconsciously skim through them and miss the important parts.
Let’s figure out how we can make the process less painful and time-consuming.
The review problem
When writing code yourself, you have a mental model of what the code does.
Unfortunately, AI-generated code…
If your goal is to ship AI applications in 2026, Ruby is the best language to do it.
The AI Training Ecosystem Is Irrelevant
Python owns model training. PyTorch, TensorFlow, the entire notebooks-and-papers gravity well. Nobody disputes that.
But you’re not training LLMs. Almost nobody is. Each training run costs millions of dollars. The dataset is the internet!
This is what AI development today looks like:
curl https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{"model": "gpt-5.2", "messages": [{"role": "user", "content": "Hello"}]}'
That’s it. An HTTP call.
The entire Python ML stack is irrelevant to achieve this. What matters is…
Working with maps usually means working with numbers — lots of numbers. If you want to render a map of a country, region, or continent, you normally need to know its exact bounding box: bbox = [-73.6, -55.1, -53.6, -21.7] # Argentina Not exactly readable. Not memorable. Not friendly. What if you could just say: … Continue reading
Rendering Maps by Name: Symbolic Geographic Extents in Ruby →
Bridge Components, Swift UI and more with Joe Masilotti
Andrew and David hold down the fort without Chris and catch up on what they’ve been watching and reading, before welcoming back Joe Masilotti, the show’s most listened to guest from last year. They talk about Hotwire Native’s momentum, why “Bridge Components” are the unlock for truly native features, Joe’s push toward SwiftUI compatibility, the messy reality of in-app purchases, and how his “PurchaseKit” aims to simplify the whole Apple/Google webhook maze. We also hear about Joe’s new podcast with Colleen, the hosts’ AI tool usage (Claude, Augment, Codex), and Joe’s intent to submit a CFP to speak at RubyConf in Vegas. Hit download now to hear more!
Links
Hi, it’s Vipul.
Let’s explore this week’s changes in the Rails codebase!
Don’t filter StructuredEventSubscriber payloads
Previously, StructuredEventSubscriber was filtering out certain keys from event payloads.
This PR removes that filtering, so subscribers receive the full, unmodified payload giving
developers access to the complete event data they expect.
Optimize calculating remote IP address
Improves the performance of ActionDispatch’s remote IP calculation logic, reducing
unnecessary processing on each request when determining the client’s IP address.
Fix in_batches(use_ranges: true) with predefined limit
Fixes a bug where calling in_batches with both use_ranges: true and a…
…
Two honest conversations about maintenance, stewardship, and why rewrites aren’t the answer.
Continue Reading
It was only once I read Andrew Yang's "The End of the Office" post the other day that I realized how few political leaders are seriously grappling with this question: what will happen to civilization if all this AI investment actually pays off? Sitting with this thought led me to a dark place, if I'm being honest—not because society might be doomed, but because I'm left quoting Andrew Fucking Yang of all people:
Expect the Starbucks in your local suburb to become occupied with middle-aged former office workers who want to get out of the house. That's a benign portrait, but a lot of these families still owe mortgages on their houses that they won't be able to maintain. If I were a homeowner…
Rails 7 represents a philosophical shift: No Node.js required for modern JavaScript.
Import Maps, Hotwire (Turbo + Stimulus), and encrypted attributes make Rails 7 the most developer-friendly version yet.
This is also the first Rails version that requires Ruby 2.7+ and encourages Ruby 3+ adoption.
Note: This is Part 4 of our Rails Upgrade Series. Read Part 1: Planning Rails Upgrade for the overall strategy.
Before We Start
Expected Timeline: 1-3 weeks for medium-sized applications (smoothest upgrade yet!)
Medium-sized application: 20,000-50,000 lines of code, 30-100 models, moderate test coverage, 2-5 developers. Smaller apps may take 1-2 weeks, larger enterprise apps 6-12 weeks.
…
Prerequisites:
- Currently on Rails 6.1 (upgrade from 6.0 first)
- Ruby 2.7.0+ installed (Ruby 3+ strongly recommended)
- Test coverage of 80%+
- Understanding of…
Step 0: Upgrade Ruby First (Required)
Rails 7 requires Ruby 2.7.0 minimum, but Ruby 3.0 or 3.1 is strongly…
Important: Upgrade…
Software developers who think critically write better code. Here's how I've incorporated an excellent set of Claude Code skills to bake critical thinking into my agentic workflow.
This is Part 2 of the "Karafka to Async Journey" series. Part 1 covered WaterDrop's integration with Ruby's async ecosystem and how fibers can yield during Kafka dispatches. This article covers another improvement in this area: migration of the producer polling engine to file descriptor-based polling.
When I released WaterDrop's async/fiber support in September 2025, the results were promising - fibers significantly outperformed multiple producer instances while consuming less memory. But something kept nagging me.
Every WaterDrop producer spawns a dedicated background thread for polling librdkafka's event queue. For one or two producers, nobody cares. But Karafka runs in hundreds of…
Early last year I helped a team move from jQuery to Hotwire (you will be surprised how many teams still use jQuery! ❤️ jQuery 4 was released recently; did you know?). It was a fun time (no, really!). One of the more interesting parts was moving from a jQuery plugin for video recording to Stimulus. Today I want to show the outline of how I did that.
The MediaRecorder API captures video directly in the browser. Webcam, screen sharing or both at once (picture-in-picture style). No external services, no complicated setup. Just modern browser APIs and a well-organized Stimulus controller. Exactly what I like.
Here is what you get:
- record from your webcam;
- record your screen;
- record both in…
February 18, 2026 Based on a talk from Kaigi on Rails 2025 by Hayato Okumoto (TwoGate CTO) Because life is too short to watch bundle exec rspec scroll by like the credits of an extended director’s cut.
The Universal Pain: Waiting for CI Every Rails developer eventually reaches enlightenment — not through meditation, but … Continue reading
From 30 Minutes to 2: Speed-Running Rails CI (Without Cheating… Much) →
The industry tends to celebrate beginnings.
New repositories. Clean architecture diagrams. The excitement of choosing tools before real constraints show up. First commits get attention because they feel like authorship.
Most developers, though, don’t spend their careers starting from scratch. They spend them stepping into systems that already exist, already have users, and already have decisions embedded in them.
You open the repository and there are hundreds of thousands of lines of code waiting for you. Patterns layered over time. Workarounds that solved real problems in earlier moments. Comments that hint at context you weren’t there for. You didn’t choose the framework version. You…
Containerize Stripe CLI with Docker Compose to forward webhooks to your app. No local setup, consistent tooling, and instant onboarding for new devs.
Authors: Yaroslav Kurbatov, Backend Engineer, and Travis Turner, Tech Editor
Topics: DX, Ruby

Write and ship modern custom RuboCop cops in 2026: learn the new plugin system, requires_gem API, InternalAffairs best practices, safe autocorrection nuances, and design trade-offs for building robust, future-proof lint rules.
Five years ago, we published a blog post about writing custom RuboCop rules (called "cops"). Since then, RuboCop has had a major release with new features that can make your custom rules even better. Let's continue onward from that post to see what's changed and also uncover items left on the cutting room floor the first time, too.
Hello! After spending some time working on the Git man pages last year,
I’ve been thinking a little more about what makes a good man page.
I’ve spent a lot of time writing cheat sheets for tools (tcpdump, git, dig, etc)
which have a man page as their primary documentation. This is because I often
find the man pages hard to navigate to get the information I want.
Lately I’ve wondering – could the man page itself have an amazing cheat sheet
in it? What might make a man page easier to use?
I’m still very early in thinking about this but I wanted to write down some quick notes.
I asked some people on Mastodon for their favourite man pages, and here
are some examples of interesting things I saw…
an…
I am doing LLM “RAG” with rails ActiveRecord, postgres with the pgvector extension for vector similarity searches, and the neighbor gem. I am fairly new to all of this stuff, figuring it out by doing it.
I realized that for a particular use, I wanted to get some document diversity — so i wanted to do a search of my chunks ranked by embedding vector similarity, getting the top k (say 12) chunks — but in some cases I only want, say, 2 chunks per document. So the top 12 chunks by vector similarity, such that only 2 chunks per interview max are represented in those 12 top chunks.
I decided I wanted to do this purely in SQL, hey, I’m using pgvector, wouldn’t it be most efficient to…
Coming from a JavaScript background, I’m used to handling permissions with simple if statements. Maybe a global “roles” object, or a quick check on the frontend that hides buttons if the user isn’t an admin. Working with Rails and Pundit forced me to think differently about authorization as a system.
Understanding the request flow and where helper methods come from would’ve saved me a lot of time upfront.
Philosophy of Pundit
At its core, a Pundit Policy answers the question:
Can \$USER take \$ACTION on \$RESOURCE?
A Policy Scope answers:
Scope down \$COLLECTION to only the ones \$USER can access.
These two layers keep authorization clean and explicit:
What belongs in…
Joël and Sally examine the simpler components of programming and why using basic data structures may not always be the best approach to solving a problem.
Our hosts cover all the telltale signs and symptoms of primitive obsession, what it is, it’s drawbacks and limitations, and how to avoid it creeping into your own work.
—
Want to learn more about primitive obsession and readability in programming? Check out these links for some wider reading, including a talk from Joël! - Design Patterns and Null - thoughtbot blog on primitive obsession - Define User
Your hosts for this episode have been thoughtbot’s own Joël Quenneville and Sally Hall.
If you would like to support the show,…
We’ve built several AI features in Rails by now: image classification, image upscaling, similarity search, etc. And every time, the same question came up: which model and prompt should we actually use? The image classification project made this especially painful: a pricing change blew up our budget, smaller images proved to work better than larger ones, and every model switch required re-running the entire evaluation from scratch.
Every change on a prompt opens up a tree of choices. Which provider should we use? Which model? How detailed should the instructions be? Would more samples in the prompt work better? How much context per message? Should we use a reasoning model? Or augment the…
The one where we get Hotwire Skills from Hotwire Club, Peter Cooper launches Planet Ruby, Evil Martians launches Tutorialkit.rb, Yuri Sidorov launched Ruby Community and new features approved to be implemented in Ruby
We’re open-sourcing Upright, the synthetic monitoring system we built to watch over Basecamp, HEY, Fizzy and our many other services.
Upright runs health checks from multiple geographic locations and tells us when something breaks. It’s a Rails engine that you deploy to cheap VPS nodes around the world using Kamal. Each node runs probes against your services and reports back via Prometheus metrics, so you can alert with AlertManager and visualize with Grafana.
“Agent” might be the most overloaded word in tech right now. Every startup claims to have one. Every framework promises to help you build them. The discourse has gotten so thick that the actual concept is buried under layers of marketing.
So let’s start from first principles.
What’s an Agent?
An agent is an LLM that can call functions.
That’s it. When you give a language model a set of tools it can invoke – a database lookup, an API call, a file operation – and the model decides when and how to use them, you have an agent. The model reasons about the problem, picks the right tool, looks at the result, and continues reasoning. Sometimes it calls several tools in sequence. Sometimes none.
…
From Delayed Job to Solid Queue: How a 10-Year Rails App Finally Achieved Linear Scaling February 16, 2026 Lessons from Kaigi on Rails 2025 — Shohei Kobayashi In large Rails systems, background jobs are not a detail — they are the system. Email delivery, AI processing, document generation, data cleanup, notifications, analytics pipelines — everything … Continue reading From Delayed Job to Solid Queue: How a 10-Year Rails App Finally Achieved Linear Scaling →
If you’ve worked with Celery in production with real traffic, you’ve probably hit one of its many sharp edges. Maybe you’ve watched a simple background job silently pile up in an unmonitored queue.
Or maybe you’ve built out a tidy set of queues only to find your high-priority jobs are getting stuck behind slow (and unimportant) ones. Celery gives you powerful tools, but few guardrails.
These pain points usually stem from queue planning problems. Most teams slap labels like high_priority or emails on queues without defining what those mean.
If you plan your Python task queues around latency, you’ll have more predictable (and scalable) results. Ready to get started?
The basics of…
I keep hearing the same story in conference hallway tracks. An engineer leans in and tells me their old boss or client still texts them every four to six months with a “quick question.” There’s usually a slight eye roll. They’ve moved on. They’re not being paid anymore. It feels like a boundary issue.
Then I ask what the question was.
It’s rarely random. It’s about the custom annual report they used to run before board meetings… whether it’s safe to delete a specific SPF value in DNS… why three staging hostnames are still sitting in the load balancer and the new DevOps person is afraid to touch them. Sometimes it’s a background job chain that behaves strangely under load. Sometimes it’s a…
We’re excited to announce that the Organizations feature for RubyGems.org has entered private beta!
A long time coming
We started the Organizations work back in 2024 as announced in our June 2024 RubyGems update, where we shared our plans to bring organization accounts, memberships, and more precise gem permission controls to the platform. Since then, the team has been steadily building out the feature from refactoring our permissions models to introducing ownership roles, building the organization onboarding experience, and updating our guides to cover how Organizations work. After more than a year of development, Organizations is ready for real-world feedback.
Join the Private Beta
W…
I’m thrilled to be part of the program for Blue Ridge Ruby 2026 in Asheville, North Carolina.

I’ll be speaking about…LLMs? I hope you can join us!

InstiLLMent of Successful Practices in an Agentic World 🔗
Congrats on joining Hours Unlimited. The Math and Numbers team is excited to have you join us on our journey to redefine the importance of numerals. This introductory session will provide tips and tricks for best interacting with powerfuLLMachine, the next-level platform we use to unlock productivity and effectiveness.
Direct link to podcast audio file
I feel like I'm getting saltier, and it's concerning. If you want me to tone it down and/or up, let me know. It'll go a lot better if you just write to podcast@searls.co instead of yelling at your phone.
As usual, I brought the goods. Now here are the receipts:
This week I merged Hanami’s i18n support. Thanks to some good feedback from Trung Lê, I added support for fallbacks before merging. Thank you Trung!
This is a big step for our i18n feature, but it’s not the last. To round it out, we’ll need to add i18n awareness to our view layer, plus generators for new apps and slices. Hopefully we can get them sorted across the remainder of February.
I also merged a nice improvement to Dry Operation: a shiny new validation extension from Aaron Allen. Aaron did a great job getting all the essentials in place, and I spent some time in the last week getting everything just so before calling it done. Thank you Aaron!
While I was in working in Dry…
In this episode of RECORDABLES, we dig into our ambitious goal of moving from a commingled database to separate SQLite databases for every Fizzy customer. Lead Programmer Mike Dalessio walks through what multi-tenancy actually means in practice, why Rails doesn’t make it easy, and how the open sourced Active Record Tenanted gem makes it more seamless.
During the conversation, Mike does a live demo showing what it takes to convert an existing Rails app to multi-tenanted and the safeguards built in to prevent accidental data leaks. You’ll also hear about the edge cases of globally replicated SQLite, and why we ended up launching Fizzy without it.
Watch the full video episode on YouTube.
…
We released props_template over a year ago and it’s exciting to see it being adopted by folks. For us, its been the workhorse behind Superglue and powers its most dyanamic features. Today we’re finally releasing 1.0.
Fast
props_template is among the fastest JSON builders available today. The Props::Base class that powers props_template can also be used standalone.
props_base_class: 1439.9 i/s
panko: 1287.6 i/s - 1.12x slower
props_template: 998.8 i/s - 1.44x slower
turbostreamer: 912.9 i/s - 1.58x slower
alba: 871.0 i/s - 1.65x slower
jserializer: 668.7 i/s - 2.15x …
Hi, it’s zzak. Let’s explore this week’s changes in the Rails codebase.
Add extension point to customize transaction for persistence methods
Extracts the implicit transaction creation in persistence methods into an overridable private method, giving models full control over transaction behavior for save and destroy operations.
Load hook guard
Adds a new guard_load_hooks method to Rails::Railtie that checks if the Rails app is loaded when a load hook is called, with options to log or raise on early loading.
Avoid combinatory explosion of filter parameters
Fixes a performance degradation where using Active Record encryption with many models caused filter_parameters to grow into thousands…
Kisses From Andrew, the Ruby Gala & Conference Workshops
On this episode of Remote Ruby, Chris, Andrew, and David dive into the newly released Claude Opus 4.6 and share their frustrations and solutions for debugging a turbo stream issue in Rails. They discuss a range of debugging challenges they've faced, including Rails credentials decryption errors and handling unexpected URL parameters in Pagy. The conversation shifts to the Ruby Gala, a fundraising event tied to RubyConf, highlighting its purpose, structure, and some notable people being honored. They reflect on their experiences with Hack Days at conferences, the challenges they pose, and suggest transforming them into structured workshops to maximize learning and engagement. Hit download…
We released props_template over a year ago and it’s exciting to see it being adopted by folks. For us, its been the workhorse behind Superglue and powers its most dyanamic features. Today we’re finally releasing 1.0.
Fast
props_template is among the fastest JSON builders available today. The Props::Base class that powers props_template can also be used standalone.
props_base_class: 1439.9 i/s
panko: 1287.6 i/s - 1.12x slower
props_template: 998.8 i/s - 1.44x slower
turbostreamer: 912.9 i/s - 1.58x slower
alba: 871.0 i/s - 1.65x slower
jserializer: 668.7 i/s - 2.15x …
Over the past few months, I’ve been begrudgingly coming around to something I didn’t expect to admit publicly: AI is getting legitimately useful at building software. Not magical. Not autonomous. Not “paste in requirements and press BUILD.” We’re far from that. But the tooling has crossed a threshold where it meaningfully lowers friction in ways I can no longer dismiss.
What surprised me most isn’t that it can generate code. It’s that the cost of context has dropped. Exploring an idea. Scaffolding a feature. Writing tests. Refactoring awkward logic. Documenting decisions. Iterating without feeling like you just signed a six-month commitment.
For someone who has spent over two decades…
#787 — February 12, 2026
Read on the Web
Ruby Weekly
Minitest fast, RSpec slow? Not so fast!
Last week, I included a post about an RSpec to Minitest migration that reduced test run time by 200x. My summary glossed over the reasons for the speedup, and a "RSpec slow, Minitest fast" implication was easy to take away. Thanks to Tyler Ritchie for pointing this out. Mea culpa.
Big tooling migrations force you to drop cruft and rethink things. That causes major speedups, not merely switching frameworks.
In Don't Throw the Specs Out with the Factories, Brad Gessler nails this distinction and, more importantly, takes steps…