Replacing a business-critical legacy system does not have to mean committing to a risky, all-at-once rewrite. The strangler fig pattern offers a practical alternative: migrate one capability at a time, run the old and new systems side by side, and gradually retire the legacy code as each replacement is validated. This post walks through how it works, where it fits, and a practical Rails-based example of migrating one piece of functionality without stopping the business to do it.
Read more of Repay Tech Debt with the Strangler Fig PatternAll Articles
It’s common to see a Rails app using React to handle front-end interactions, with a Redux store that mostly mirrors the database and react-router re-declaring routes Rails already knows about. React does its job well enough, but every user-facing feature now costs twice, once in Ruby and once in JavaScript.
So eventually the question will appear: what would it take to delete all of this and use a Rails-way solution like Hotwire ? The honest answer is “it depends.” In this article, we will go through what the code difference actually looks like, what you can expect from bundle size, which pain points to plan for, and how to scope the work before committing to it.
Read more of What Replacing React with Hotwire Really CostsHere at FastRuby.io we work with test suites and test coverage reports every single day, given our specialty. In the Ruby world that means we interact with SimpleCov every day. Not only that, we explicitely rely on SimpleCov reports in two of the open source projects we maintain, RubyCritic and Skunk .
Which is why, after more than a decade at 0.x, we were super excited to see SimpleCov ship its first stable release on July 12, 2026. In this article we’d like to share the details on the breaking changes, the deprecations and how to address them, if you’re impacted by them.
Read more of SimpleCov is now version 1!When I first started thinking about private gem distribution, I approached the problem the way I always do, backward from bundle install. What does a developer need to make that command succeed for a private library? Three things: a source (URL or repo), credentials that work in CI and locally, and a reliable way to receive updates without breaking deployments.
Not all code belongs on RubyGems.org , and that’s okay. Sometimes the goal is internal reuse- sharing an SDK or auth client across services. Sometimes it’s a business model: you ship a private gem behind a license. Either way, the constraints are the same: authentication, delivery, and trust.
In this blog post, we’ll give an overview of the options for distributing private gems.
Read more of How to Distribute Private GemsDocker has made it easy to use the same environment everywhere, from development to production. But the most basic Dockerfile, where all your dependencies are lumped together in one image, has hidden costs. In this article, we’ll learn the advantages of multi-stage Dockerfiles both from a security and a performance standpoint, primarily for production images.
Read more of Why To Use A Multi-Stage DockerfileWhen we think about Ruby code coverage, our go-to gem for this is SimpleCov , which works great when the test suite uses Minitest , RSpec , Cucumber , Capybara , and all these tools that are integrated with Ruby and Rails. But many applications also use other tools like Playwright or Cypress to run e2e tests, and we can’t use SimpleCov the same way.
Most of the time, what we have seen is that the Ruby code executed when running these tools ends up left behind and not being counted for the total code coverage, even though we know the code is actually being tested.
Read more of Measuring Code Coverage For Non-Ruby RunnersIf you work on Rails for a living, you clone repositories you didn’t write all week long: a gem you’re debugging, a client’s application you’re about to audit, a bug reproduction attached to an issue. For years, opening one of those in your editor was the safe part. You were reading someone else’s code, not running it, and the only real rule was to not run anything until you had looked.
That rule quietly stopped being enough. AI coding editors like Claude Code and Cursor read project-local configuration the moment you open a repository, and some of that configuration is executable. A repo you cloned five seconds ago can hand your editor a command to run before you have read a line of it, and this is not hypothetical: the npm worm that hit keyv this week (Socket , Aikido , and Microsoft all covered it) weaponized exactly this, writing itself into .claude/settings.json so it runs again for anyone who opens the project.
In this post, we’ll look at why opening a repository in an AI editor is now an execution event, how the keyv worm turned that into a live attack, and what to check in a cloned repo’s .claude/ and .vscode/ directories before you point your editor at it.
You run bundle update, kick off a build, and asset precompilation stops on this:”
cssbundling-rails: Command install failed, ensure bun is installed
Tasks: TOP => assets:precompile => css:build => css:install
Except your application uses Yarn. It has always used Yarn. Nothing in the project references Bun, and nobody on the team added it.
This is not an exotic edge case. It can happen to ordinary Yarn applications, and it lingers because the fix has been merged upstream but never released. In this post, we’ll walk through why cssbundling-rails misidentifies your package manager, and how to unblock your build.
In a previous post, AI Assistant for Our Blog Writing Process , I introduced the assistant we built to help with our blog writing. At the core of that assistant is an MCP server, which serves as the source of truth for both of our blogs. It exposes that knowledge through tools the client can call and documentation the client can read.
Getting an MCP server running is the easy part. Every quickstart, in every language, gives you a server that runs as a subprocess on your own machine and disappears when the client exits. That’s enough to experiment locally, but it’s a long way from something a team can rely on. Once you want to deploy it, questions about where it runs, state management, authentication, and security become your responsibility. The Ruby SDK’s defaults don’t solve most of those problems, and one of them even comes with a published security advisory.
In this article, we’ll cover what changes when a Ruby MCP server stops being a subprocess: the two shapes it can take in a Rails shop, why session state breaks down when running behind multiple Puma workers, the DNS rebinding vulnerability the transport shipped with, and what the specification asks of you once a shared token is no longer enough.
Read more of Running a Ruby MCP Server in ProductionYou saw the advisory for CVE-2026-66066 , the Active Storage vulnerability in the way Rails processes image variants with libvips. You bumped activestorage to the patched version, ran your tests, deployed, and moved on. That is the responsible thing to do, and for most Ruby vulnerabilities it would be the whole job.
This one is different. The patched version of Active Storage will not run on an old copy of libvips. It raises an exception during boot and refuses to start:
/usr/local/bundle/gems/activestorage-8.1.3.1/lib/active_storage/vips.rb:36:in '<compiled>': libvips's unfuzzed operations are not safe to use with untrusted content, and Active Storage cannot disable them. Disabling them requires libvips 8.13 or later and ruby-vips 2.2.1 or later. Please upgrade libvips and ruby-vips, or remove the ruby-vips gem from your Gemfile. (RuntimeError)
libvips is a system library that lives in your container image, not a gem in your Gemfile, so bumping the gem does nothing on its own until you rebuild the image. If your image still ships the old library, you are in one of two states, and neither is the one you think you are in: either your app won’t boot, or it never received the patched gem at all and is quietly still vulnerable.
In this post, you’ll learn why upgrading activestorage doesn’t close CVE-2026-66066 on its own, how a stale container image both hides the vulnerability and blocks the fix, and why rebuilding on the wrong base still leaves you exposed.
For those who have been coding CSS since the Internet Explorer 6 era, when aligning divs on the web was an art, and there was no way to use partials or variables, the arrival of SCSS was a gift to life. At that moment, creating partials and reusing variables for your primary colors was a delightful experience.
Now in 2026, for the real fans, it’s possible you’re still using sass-rails and you’re full of these files around your project.
So if you still want to keep using it like me, I recommend migrating to Dart Sass, which is, in fact, a simple migration,
to avoid headaches in the future and, most importantly, to start using the latest features.
Wiring an LLM into a Rails app takes a handful of lines. Understanding what it actually costs you (feature by feature, user by user) is harder. Most providers and SDKs already report tokens, latency, and even cost, but that data lives in their dashboard. It’s disconnected from your requests, your users, and the feature that made the call. And it sits apart from the APM and logs where you already watch the rest of your app.
In a previous post, we introduced Rails.event.notify(...) , the tool-agnostic Event Reporter shipping in Rails 8.1. In this post, we’ll put it to work on a real problem: instrumenting every LLM call in your app so token usage, latency, and cost become structured events you can log, graph, and forward to any APM or data warehouse.
Most Rails developers have crossed paths with Rake (usually to run a migration, seed a database, or clear out test data). If you’re like me, you may have quietly filed Rake away under “that place where Rails keeps database tasks.” But Rake is far more powerful than that.
It isn’t just a Rails helper: it’s a full-blown, general-purpose build system, sitting quietly in your project, ready to automate almost anything. And the best part? It speaks Ruby. That means, as a Rails developer, you’re already fluent in the language your build tool understands. In this post, I want to open your eyes to the wider world of Rake, beyond migrations and seeds, into using it as a central hub for building, scripting, and orchestrating your entire workflow.
Read more of Rake Beyond Rails: A Build Tool You KnowToday I’m excited to share a new open source project: A Claude Code skill to assess technical debt in a Ruby on Rails application. It leverages some of the libraries that we have open sourced and maintained for a long time.
Over the years, we’ve written about many of the tools we use: Skunk for combining code quality and code coverage data, bundler-audit for security vulnerabilities in your dependencies, libyear-bundler for measuring dependency freshness in a Ruby application, and RubyCritic for churn vs. complexity analysis.
The challenge? Running all these tools manually takes time and interpreting the results across multiple reports can be tedious.
What if we could automate the entire audit process and generate a comprehensive report with a single command?
In this article, I’ll show you how we built a Claude Code skill that does exactly that, in minutes!
Read more of Automate Tech Debt Audits with Claude CodeRails 8.1 includes support for marking associations as deprecated right in your models. In this post, I’ll explain how the feature works, why it matters, and how you can use it to safely remove outdated associations from your application.
Read more of Rails 8.1: Deprecated Associations