LaunchKit

Writing & video

Yield

Ruby craft, one idea at a time.

#rails #http #performance Latest
· 6 min read · 1 views

Server-sent events in Rails freeze the process they run in

ActionController::Live gives you SSE in six lines, and one open stream blocked every other request in that Puma process for as long as it stayed open. Measured on Rails 8.1.4: 3.0 seconds of blocking from a 4 second stream, while a plain sleeping action blocked nothing at all.

Read more
Guide

Rails and AI

What ruby_llm actually puts in your Rails app: six tables behind one acts_as_chat line, an error hierarchy worth failing over on, and seventeen providers reached through one code path that stops being uniform at the credentials.

3 articles
#rails #active-record #forms
· 5 min read · 1 views

accepts_nested_attributes_for, and the children it throws away

One line gives you a parent and its children from a single form, in four queries. Measured on Rails 8.1.4: reject_if all_blank discarded two of three submitted children with no error anywhere, and passing another parent's child id raises rather than letting you edit it.

#rails #active-record #background-jobs
· 5 min read · 1 views

after_commit, and the job that runs before the row exists

after_save fires inside the transaction and after_commit fires outside it, which decides whether your background worker can see the record it was told about. Measured on Rails 8.1.4: the real callback order, what a nested transaction does, and why raising in after_commit leaves the row behind.

#rails #action-view #ruby
· 6 min read · 1 views

How Rails picks a template engine, and what a handler really is

A template handler returns Ruby source code, not HTML. Measured on Rails 8.1.4: the five extensions registered out of the box, the one that silently catches your typos, and a working custom engine in a single lambda.

#rails #active-record #database
· 6 min read · 1 views

Single table inheritance in Rails, and the day a class gets renamed

STI stores your class name in a column, which is fine until it isn't. Measured on Rails 8.1.4: loading a row whose class no longer exists raises SubclassNotFound while count keeps working, and becomes changes the Ruby object without touching the type.

#rails #active-record #performance
· 6 min read · 1 views

find_each, in_batches and insert_all, priced in queries

Three ways to touch a lot of rows, measured on Rails 8.1.4 over 2,500 records. find_each walks an id cursor rather than paging with OFFSET, in_batches hands you a relation so update_all is one query a batch, and insert_all did in 1 query what 500 create! calls did in 1,000.

#rails #active-record #authorization
· 6 min read · 2 views

User roles in Rails, and what each design costs you

A role column, a roles table, or a gem. Measured on Rails 8.1.4: user.admin? costs zero queries on a column and nine on a join table across eight users, what includes gets that back, and the enum renumbering that silently promotes everybody.

#rails #active-record #database
· 6 min read · 3 views

Soft delete in Rails, and the four things default_scope breaks

A deleted_at column and default_scope look like a twenty minute feature. Measured on Rails 8.1.4: the uniqueness validator ignores your scope on purpose, belongs_to quietly returns nil for every child of a discarded parent, and a plain unique index locks the name forever.

#rails #routing #seo
· 5 min read · 3 views

Pretty URLs in Rails, and the slug that turns out empty

to_param plus parameterize gives you /posts/12-hello-world in two lines. Measured on Rails 8.1.4: a Japanese title parameterizes to an empty string, find accepts any text you append to the id, and nothing stops two rows sharing a slug.

#rails #seo #routing
· 5 min read · 3 views

Generating a sitemap in Rails without a gem

A sitemap is one controller action and a builder template. Measured against this site's own live sitemap: 190 bytes per URL, a 14.8x gzip ratio, and arithmetic showing the 50,000 URL cap binds roughly five times sooner than the 50 MB one.

#rails #authentication #security
· 6 min read · 5 views

Your Rails magic link doesn't expire when it's used

generates_token_for builds a magic link in four lines, and the token it hands you is reusable and byte-identical every time you generate it. Measured on Rails 8.1.4: three sign-ins from one link, what actually invalidates it, and the one-line block that makes it burn.

#rails #http #security
· 5 min read · 3 views

CORS in Rails isn't a security control

A cross origin POST reaches your action and runs it whether or not rack-cors is installed. Measured on Rails 8.1.4: the request executed and returned 200 for a disallowed origin, rack-cors withheld the headers and nothing else, and the browser is the only thing doing any blocking.

#rails #email #active-storage
· 5 min read · 3 views

Receiving email in Rails with Action Mailbox

Action Mailbox routes inbound mail to a class the way the router routes requests. Measured on Rails 8.1.4: the five statuses an email moves through, why a bounce is a status and not an exception, and the thirty day incineration nobody reads about until the storage bill arrives.

#rails #ruby #tooling
· 8 min read · 9 views

How many times has Rails been downloaded

About 800 million for the rails gem, and the number is softer than it looks. Measured against the RubyGems API on 2026-10-12: activesupport is 1.81x higher, one 2014 version holds 5.1 percent of the total, and asking from Ruby instead of curl returns a different answer because the CDN varies on Accept-Encoding.

#rails #upgrades
· 6 min read · 9 views

When is Rails 8.2 coming out

No 8.2 gem exists on RubyGems, not even a prerelease, checked on 2026-10-12. What the release history does say: seven minors since 6.0 with gaps from 90 to 659 days, 355 days elapsed since 8.1.0 against a median of 371, and a beta that landed 48 days before the last one and has no counterpart yet.

#rails #performance #deployment
· 9 min read · 13 views

What Puma is doing in your Rails app

Rails ships three threads and one process, and whether that is right depends on something you can measure in an afternoon. Benchmarked on puma 8.0.2: threads take an IO bound endpoint from 9.4 to 103.3 req/s and take a CPU bound one from 57.6 to 57.7, which is the GVL to the decimal place.

#rails #security #debugging
· 8 min read · 6 views

Can't verify CSRF token authenticity

The error prints three different messages and two of them never mention a token. Traced through actionpack 8.1.4 with every message captured verbatim: the four term predicate that decides, why a correct 86 character token still fails when the session cookie is gone, why two forms on one page carry two different tokens, and why ActionController::API never raises it at all.

#rails #http #security
· 8 min read · 11 views

Setting a cookie in Rails

Rails guards signed and encrypted cookies at 4096 bytes and does not guard a plain one at all. Measured on actionpack 8.1.4: an 8000 byte plain cookie ships without a word, the largest encrypted payload Rails accepts lands at 4310 to 4394 bytes on the wire depending on the run, and cookies.delete is a no-op when the cookie was not in the request.

#ruby #rails
· 11 min read · 31 views

Ruby vs Ruby on Rails

The rails 8.1.3.1 gem ships two files and neither is Ruby. What the boundary between the language and the framework actually is, how to find which side any method in your code is on, what Active Support does to String, and what loading the whole thing costs in files and megabytes.

#rails #autoloading
· 15 min read · 48 views

Rails autoloading with Zeitwerk

The two loaders a Rails 8.1 app runs, why a misnamed file serves HTTP 200 in development and refuses to boot in production, the error message you only get once, and the constant captured at boot that keeps answering the code the file used to hold.

#rails #security
· 16 min read · 16 views

Rails XSS, and the four places the escape does not reach

Rails escapes five characters in every <%= %> and that is enough for a text node and not enough for a URL attribute, an unquoted attribute, a <script> body or a translation key ending in _html. Measured on Rails 8.1.3.1, with the Brakeman 8.0.6 output and the 22 tests.

#rails #active-record
· 15 min read · 24 views

What database Rails uses

Rails ships four Active Record adapters and rails new picks SQLite. What that choice costs, measured on one machine against SQLite, PostgreSQL 17.7 and MySQL 26.7.0: throughput that does not separate them, one failed migration that only rolls back on two of the three, and the lock that ends the SQLite story.

#rails #action-cable
· 14 min read · 30 views

Ruby on Rails and WebSockets

Rails already mounts a WebSocket server at /cable and a fresh 8.1 app has no app/channels directory at all. The real frame trace, the 404 that is an Origin check, what the async adapter silently drops, measured delivery latency on Solid Cable, and what 500 open connections cost a 3-thread Puma.

#rails #hotwire #comparison
· 14 min read · 18 views

Ruby on Rails and Vue

Three ways to put Vue 3 in front of Rails, all three built and measured on one laptop: importmap with no build step, Vite with real .vue files, and Inertia with no API at all. What each one puts on the wire, the Turbo lifecycle that breaks all of them, and the two failures that leave a green test suite.

#rails #ruby #tooling
· 14 min read · 18 views

Rails in VS Code: one extension, and eight you can uninstall

What the VS Code marketplace serves for "ruby on rails" against what Ruby LSP 0.10.6 actually answers over the wire: the schema in the hover, the route code lens, the 4.8 second index, the 493 MB, and the two places it silently returns nothing.

#rails #wordpress #comparison
· 21 min read · 24 views

Ruby on Rails vs WordPress

Both stacks installed on one laptop with the same 5000 products in the same MySQL: where a custom field lives, the meta_query that answers 4460 instead of 500, the transaction API wpdb does not have, the back office Rails answers 404 to, and 12 queries against 1 for the same ten-row list.

#rails #hotwire #comparison
· 14 min read · 27 views

Ruby on Rails vs React

The same filterable 600-row table built twice on one laptop, once with Rails and Hotwire and once with Rails serving JSON to a React 19.3 app: what arrives in the first response, what the browser downloads, the CORS 200 that renders zero rows, and where the milliseconds actually go when you filter.

#rails #phoenix #comparison
· 15 min read · 25 views

Ruby on Rails vs Elixir Phoenix

Both frameworks installed on one M2 Max, the same scaffold, the same PostgreSQL, the same three endpoints under ApacheBench. Where Puma's 3 threads put the ceiling, what the GVL costs on a CPU endpoint, why 300 requests to a Rails counter never get past 35, and what Ecto refuses to let you write.

#rails #laravel #php #comparison
· 19 min read · 25 views

Ruby on Rails vs Laravel

Both frameworks installed on one laptop and pointed at the same PostgreSQL: the per-request boot PHP pays and Rails does not, why Eloquent writes a blank title that Active Record refuses, the lazy-loading guard that skips single-row results, what php artisan optimize buys and what it breaks, and the same JSON endpoint benchmarked on both.

#rails #javascript #node #comparison
· 16 min read · 42 views

Rails or JavaScript, and what the Odin Project paths actually differ by

Both Odin Project paths read off the site and counted course by course, then the same CRUD feature built twice on one laptop: a Rails 8.1.4 scaffold against 112 lines of Express 5.2.1 and pg. The bytes of JavaScript a Rails page ships, the escaping test that fails on one side, and ApacheBench on both.

#rails #django #comparison
· 14 min read · 21 views

Ruby on Rails vs Django

Both frameworks installed on one laptop and driven at the same PostgreSQL: where each one keeps the schema, what makemigrations refuses that Rails lets you run, the admin Rails does not have, Django 6.1's background tasks with no worker, and the same JSON endpoint benchmarked on both.

#rails #deployment
· 18 min read · 17 views

How much VPS a Rails app needs

A Rails 8.1 process measured under a hard memory cap: 180 MB resident before it serves anything, 55 MB for each extra Puma worker, OOM-killed at 160 MB and alive at 192. With the cgroup numbers, the ApacheBench output, and a jemalloc result that went the wrong way three times.

#rails #active-storage
· 13 min read · 14 views

Rails video streaming

The one request a browser makes for a video, why Active Storage answers it with 416 above 100 MiB, what the disk route does differently, and what three slow viewers do to a three-thread Puma. With the Chrome error string, the ab runs and the RSS numbers.

#rails #upgrade
· 14 min read · 23 views

The Rails upgrade path out of a legacy app

Why bumping Ruby first is the wrong move, measured: the gemspec declares a Ruby floor and no ceiling, Bundler resolves rails 7.0.8.7 against ruby 4.0.5 and exits 0, and then the app dies on a missing Logger. With the one rung that runs on both Rubies.

#rails #testing
· 17 min read · 19 views

Rails testing tools, priced by what each one can see

The eight test classes Rails 8.1 ships, what bin/ci actually runs, the per-example cost of each layer measured on a 1665-example suite, the authorisation hole Brakeman scores clean, and the two ignore files that give the same gem audit two different answers.

#rails #tailwind #assets
· 14 min read · 18 views

Ruby on Rails and Tailwind CSS

What rails new --css=tailwind actually installs, which files the v4 scanner reads and which it silently skips, why a class built with string interpolation never gets a rule, and the vendor/bundle directory that took one build from 39ms to 985ms. With the probe table and the passing test.

#rails #infrastructure #saas
· 11 min read · 13 views

The Rails startup stack, counted

What one rails new gives a startup in 2026, counted on a fresh app: 16 gem lines, 114 gems, no Redis, no Node. Then the three things the generated app does not do, including the password reset that answers 302 and lands in solid_queue_failed_executions with nobody watching.

#rails #views #performance
· 15 min read · 15 views

Rails server side rendering against client side rendering, measured

What is actually in the first response from a Rails 8.1.3.1 app against a client-rendered page of the same data, what ERB costs per row, the one view helper that was 94 percent of the render, and why the default ETag on every page with a CSRF token is worthless. ApacheBench output and a passing Minitest file.

#rails #active-record
· 17 min read · 22 views

Rails 8 and SQLite in production

What Rails 8 actually configures on a SQLite connection, the one line that made it survivable, how many writes per second a generated app really does through the full stack, and the two failures that cost real requests: a long write transaction from a second process, and a schema cache that is loaded lazily.

#rails #api
· 15 min read · 19 views

A Rails REST API, from rails new to the errors nobody shows you

Building the five REST actions with rails new --api on Rails 8.1.3.1: the routes resources actually draws, why params.expect silently swallows unknown keys, the 400 that came back before the 201 did, and the empty text/html body every error returns in production.

#rails #active-record #postgresql
· 15 min read · 54 views

Resetting a Rails database

What db:reset, db:migrate:reset, db:truncate_all and db:prepare each drop, rebuild and forget on rails 8.1.3.1, the unrun migration that turns a reset into a plain wipe, and the failed db:migrate:reset that overwrote a 519 line schema.rb with 17. Every transcript pasted from the run.

#rails #views
· 15 min read · 26 views

render partial in Rails, printed

The four call shapes of render, why a bare partial name resolves against the controller prefixes and not the directory of the calling template, what the collection form actually buys in milliseconds, and the two errors strict locals turn into exceptions. Measured on Rails 8.1.3.1.

#rails #api
· 16 min read · 15 views

render json, and the bytes it actually sends

The five lines of actionpack that turn render json: into a response body, why only: works on a record and is ignored on a String, the two Rails 8.1 changes that alter the bytes without touching your controller, and where the time in a JSON response really goes.

#rails #views
· 11 min read · 16 views

Rendering HTML in Rails

render html:, render plain:, render inline: and render template: are four different objects inside Action View, and they disagree about escaping, layouts and Content-Type. Measured on Rails 8.1.3.1, with the injection that html_safe opens and the cost of an inline template.

#rails #caching #infrastructure
· 15 min read · 15 views

Rails and Redis: where it plugs in, and what goes wrong

Rails 8 needs no Redis, so if you are adding one anyway, this is what it is actually wired to: the cache store config line, the pool of five, the failsafe that turns every Redis error into a cache miss, the counter that read returns nil for, and the maxmemory policy that is wrong out of the box.

#rails #performance
· 21 min read · 23 views

Ruby on Rails profiling, four tools on one slow request

A 60 ms Rails page taken apart by ActiveSupport::Notifications, StackProf, Vernier and rack-mini-profiler, with what each one could name, what each one cost, and the two that gave a wrong answer on this machine. Every number from one laptop, with the conditions.

#rails #performance
· 17 min read · 18 views

Rails performance improvements, ranked by what they actually moved

One real page profiled from 42.2 ms down to 11.3 ms, a missing index measured at 36.390 ms against 0.053 ms, and the Puma thread count that changed throughput by less than 3 percent. Every number from this laptop, with the conditions.

#rails #performance
· 16 min read · 31 views

Ruby on Rails performance comparison

Rails measured against itself on one laptop: a bare Rack app, an empty Rails endpoint and a real page, with ApacheBench output and the conditions. Where the 34 milliseconds of a real request actually go, what four Puma workers bought, and why the framework benchmark numbers everybody quotes answer a question your application does not ask.

#rails #views
· 16 min read · 15 views

Generating PDFs in Rails

Rails ships no PDF renderer, so the choice is Prawn drawing primitives or headless Chrome printing your ERB. Measured side by side on the same invoice: 102.8 ms against 716.8 ms, 40114 bytes against 111038, a total that prints on all three pages, and an ETag that changes 15 times out of 20.

#rails #active-record #mysql
· 17 min read · 23 views

Connecting Rails to MySQL

What rails new -d mysql actually writes, why host: localhost under mysql2 opens a unix socket and ignores your port, why caching_sha2_password refuses the connection after the password is right, and the four Rails behaviours that change the day the adapter is not pg. Run against MySQL 26.7.0.

#rails #saas #indie-hacking
· 9 min read · 15 views

Rails MVP: the twenty-eight lines the generators skip

A generated Rails 8.1.4 app with authentication and one scaffold has a red test suite before you write a line, and no way for anyone to sign up. The seven failures and one error, both one-line fixes, the twenty-eight lines that close the registration hole, and the duplicate address that 500s if you forget one of them.

#rails #postgresql #active-record
· 22 min read · 38 views

Rails multitenancy, with the boundary in the database

Why default_scope is not a tenant boundary, what a PostgreSQL row level security policy stops that it cannot, the two reasons the policy silently did nothing on the first run, and the pg_class and lock table numbers where schema per tenant stops. With a passing Minitest file.

#rails #architecture
· 12 min read · 19 views

The Rails monolith, and the boundary it does not give you

What one process actually buys: a transaction that rolls back both domains, measured against the same write over HTTP. Plus the directory under app/ that silently defines the wrong constants, why isolate_namespace let this site's own engine reach into Setting nine times, and the two things packwerk cannot see.

#rails #performance
· 16 min read · 20 views

Rails monitoring, and the 645 ms it does not measure

The same page, the same machine: Rails reported a 63 ms median while the client waited 787 ms. What process_action.action_controller actually carries, the queue time middleware Puma's own docs tell you to write, and the instrumentation subscriber that 500s the request it was measuring.

#rails #ruby #performance
· 21 min read · 18 views

Finding a Rails memory leak

The measurement that tells a leak apart from a process that merely grew, three ObjectSpace dumps that name the line, what allocation tracing costs in RSS and throughput, and why clearing the leak left RSS at exactly the same 115.7 MB. With every GC.stat line pasted from the run.

#rails #ruby #tooling
· 11 min read · 16 views

Which Linux distro for Rails

Ruby comes from a version manager on every distribution, so the distro decides something else: which OpenSSL your Ruby is compiled against, whether headless Chrome exists for your system tests, and how old libpq and libvips are. Built Ruby 4.0.5 from source on Ubuntu 24.04 and Fedora 43 and got two different crypto libraries, with a 15-example Minitest suite over every claim.

#rails #hotwire
· 13 min read · 15 views

Ruby on Rails and Hotwire

Hotwire is not a gem you add, it is two gems rails new already wrote. What a default Rails 8.1 app actually ships, the one HTTP rule Turbo imposes that Rails did not have before, why the same 200 response is fine inside a turbo-frame and discarded outside one, and what each rung of the ladder costs in bytes. Seven browser tests, all of them run.

#rails #deployment
· 14 min read · 14 views

Ruby on Rails hosting

Where a Rails 8.1 app can actually live, measured: the container replacement that took Post.count from 1 to 0 with no error anywhere, the three minutes of sleep that skipped three cron runs and delayed a job by 2 minutes 23 seconds, and what a quarter of a vCPU does to a cold start.

#rails #active-record
· 15 min read · 23 views

has_and_belongs_to_many, expanded

What the macro actually declares on activerecord 8.1.3.1: a private join model, a hidden has_many, and three options it accepts and throws away. With the SQL, the orphan rows nobody deletes, and the two-line move to has_many :through that needs no migration.

#rails #api
· 15 min read · 17 views

A GraphQL API in Rails, and what the generator leaves you holding

What rails g graphql:install actually writes, the 61 queries it takes to render 60 posts and their authors, the 145-character query that returns 1.3 MB while passing every default limit, and the measured cost of the endpoint against a plain Rails JSON action.

#rails #ruby
· 11 min read · 29 views

Ruby on Rails for beginners, and the tutorial that no longer runs

Four lines from older Rails tutorials that raise NoMethodError on Rails 8.1.4, three that look just as old and still work, the three paths that date a tutorial without reading a word of it, and the three errors of a first hour quoted in full. Run on a freshly generated application, with the passing test that records it.

#rails #ruby #postgresql
· 16 min read · 28 views

Excel export in Rails

A zero padded SKU leaves a caxlsx export as the number 37, and the reason is one regex in cell.rb. What the two xlsx writers put in the file, what 200000 rows cost a Puma worker, what comes back through roo, and the Zlib::BufError that only appears when something else in the process shells out.

#rails #deployment #docker
· 17 min read · 26 views

The Rails Docker image

There is no Rails image on Docker Hub and there has not been one since 2017. Rails writes you a Dockerfile instead: 77 lines, three stages, 565 MB. Built, measured layer by layer, run until /up answered 200, pushed multi-arch to a registry, with an 11-example Minitest suite against the built image and the secret that ships inside it.

#rails #docker #performance
· 16 min read · 40 views

Rails development inside a Docker container

What a bind-mounted Rails app actually spends its time on, the one configuration line that gives most of it back, why postgres:18 refuses the volume every tutorial writes, and the DATABASE_URL in compose that lets bin/rails test drop your development database. Measured on a scratch app, with a 6-example Minitest suite that runs in the container.

#rails #infrastructure #postgresql
· 22 min read · 13 views

Rails and Postgres in Docker Compose

Why the Dockerfile rails new writes cannot run your development environment, the one flag in compose.yaml that silently turns off db:prepare, what depends_on does and does not wait for, and the measured cost of a bind mount on macOS. Built from a scratch app, with a 12-example Minitest suite that runs inside the container.

#rails #deployment
· 16 min read · 14 views

Deploying Rails 8 to Render

The two files Render needs from a Rails 8 app, the build failure that happens before the database is touched, and why the four connections in a generated config/database.yml turn one Render Postgres into a green deploy with no Solid tables in it.

#rails #deployment
· 17 min read · 16 views

Deploying Rails 8 to Heroku

What the heroku/ruby buildpack actually does to a Rails 8.1 app, why it ignored the .ruby-version in the repo, and the release phase line that has to exist because db:prepare silently creates none of the Solid tables when Heroku gives you one database.

#rails #active-record
· 17 min read · 33 views

Deleting a record in Rails

destroy and delete printed side by side on activerecord 8.1.3.1: the SQL each one sends, why destroy_all is 3001 statements and not one transaction, what every dependent: option emits, the two orders of magnitude a foreign key buys, and the delete button that quietly does a GET.

#rails #active-record
· 13 min read · 25 views

Rails database.yml, resolved

What config/database.yml actually becomes at boot on Rails 8.1: the shared: block, the DATABASE_URL merge that keeps your file's password, why pool: is now max_connections:, which of the 50 libpq keys reach PostgreSQL, and the misspelling that reports as a credentials error. Printed from a scratch app with a 12-example Minitest suite behind it.

#rails #active-record
· 15 min read · 19 views

When a Rails database query actually runs

A Ruby on Rails database query does nothing until something asks for rows, and which method you call decides whether that is one COUNT or ten thousand objects. With the per-request query cache serving a stale row, the development default that turns prepared statements off, and the select string that runs a DROP TABLE.

#rails #deployment #infrastructure
· 16 min read · 16 views

Ruby on Rails on AWS Lambda

A Rails 8.1.3.1 app booting inside the official ruby4.0 Lambda base image: the nine-line handler, the Bundler::ReadOnlyFileSystemError raised at config/boot.rb:3 before Rails loads, init duration measured at seven CPU allocations against the documented 10-second Init cap, and what the billed milliseconds actually cost.

#rails #postgresql
· 17 min read · 35 views

Active Search, and the index it does not write

Basecamp's rails-active_search 0.1.0 run against PostgreSQL 17.7 and SQLite FTS5 on the same 50001 rows. The SQL it generates, why the two GIN indexes in its own migration never get used, the one expression index that takes the endpoint from 18.79 to 281.51 requests a second, and where two backends return different first pages for the same query.

#rails #upgrades
· 13 min read · 54 views

Rails 8.2, read from the source

What the Rails 8.2 edge release notes actually list, checked against rails/rails main: the version is 8.2.0.alpha, there is no tag and no branch, and config.load_defaults 8.2 flips eleven behaviours of which the notes page names four. With Herb, has_json, DROP DATABASE WITH (FORCE) and Rails.app.creds measured in a generated app.

#rails #security
· 15 min read · 25 views

Rails 8.2 CSRF moves to Sec-Fetch-Site

The header-based CSRF check landing in Rails 8.2: the two strategies, the exact branch in request_forgery_protection.rb, the three deprecations that fire before you change anything, which clients never send the header, and why a local HTTP reproduction shows you nothing.

#rails #views #upgrades
· 17 min read · 27 views

Herb in Rails 8.2, run against 207 templates

What happens when the Rails 8.2 default ERB engine meets a real view layer: 7 of 207 templates rejected by bin/rails herb:check, all 7 of which still render, an omitted </li> that does not, and the attribute escaping Herb's README promises that Action View switches back off.

#rails #active-model
· 14 min read · 12 views

Argon2 in has_secure_password, and the variant nobody named

The Rails 8.2 edge notes say has_secure_password gains Argon2 via algorithm: :argon2. The digest it writes is argon2id at RFC 9106's second recommended parameters, it is an opt-in and not a default, the parameters cannot be configured, and switching a table of bcrypt digests to it raises rather than returning false. Measured on an M2 Max.

#ruby #rails #open-source
· 11 min read · 23 views

Omarchy Core, and the vocabulary open source borrowed from employment

The Omarchy announcement that ThePrimeagen joins Omarchy Core to lead Agentic QA says nothing about money, and readers assumed a salary anyway. Why the words joins, lead and Core do that, plus a count of how much of one Rails Gemfile is held by a single RubyGems owner.

#rails #action-text
· 17 min read · 31 views

Lexxy on an Action Text model that already has Trix in it

Both editors on the same has_rich_text column, on Rails 8.1.3.1: the HTML each one writes for the same input, what Lexxy does to rows Trix authored, what it adds to the sanitizer allowlist app-wide, the 999,642 bytes it pins, and the two lines of system test it breaks.

#rails #architecture #testing
· 14 min read · 34 views

DHH let designers vibe code Basecamp 5, and the PRs destroyed the architecture

The Lex Fridman episode where DHH describes the agent failure nobody quotes: PRs that were individually defensible and collectively wrecked the system. Then the review boundary he draws instead, priced against a real Rails codebase with bin/rails stats, bin/rubocop and a simulated merge.

#rails #active-record
· 17 min read · 22 views

Rails validations in practice

Contexts, conditions, custom validator classes and errors.add, with the uniqueness race reproduced against a real PostgreSQL. Including the two places the validation and the unique index disagree, and the method that stops working the moment you add uniqueness.

#rails #active-record
· 18 min read · 37 views

UUID primary keys in Rails

What id: :uuid gives you in Rails 8.1, why v7 keys index differently from v4, and the four ways to get a uuidv7() when your PostgreSQL has one and when it does not. With the leaf densities, the WAL, and the benchmark where v7 came out worse than v4.

#rails #active-record
· 20 min read · 32 views

Transactions and rollback in Rails

What a transaction block guarantees, the four ways work commits when you meant it to roll back, what requires_new buys you, when after_commit fires and in what order, and the three lock errors worth retrying. With the SQL and the error text from a real PostgreSQL 17.7.

#rails #active-support
· 19 min read · 20 views

Rails time zones

The three clocks a Rails process runs on, what Time.now is actually wrong about, why Time.zone.parse returns nil where Time.parse raises, and the daylight saving gap that makes a daily Solid Queue task skip a day. With the runner output and the cron walk.

#rails #testing
· 19 min read · 34 views

Rails system tests

What driven_by starts, the three ways a browser test goes flaky, and what sixty of them cost against sixty request tests on the same page. With the real Selenium error text and the worker count that made the suite slower.

#rails #active-support
· 17 min read · 11 views

Structured logging in Rails 8.1

What the default logger actually formats, the log_tags setting that silently does nothing, and the Event Reporter Rails 8.1 added next to the log rather than in place of it. With the JSON a real request emits and the health check that keeps emitting it.

#rails #active-record
· 21 min read · 17 views

State machines in Rails

An enum plus a transition table gets you most of a state machine in twenty lines. What AASM, state_machines and statesman add on top, measured on Rails 8.1, including the race every one of them loses by default.

#rails #active-record
· 14 min read · 21 views

Rails seeds that survive a second run

Why db:prepare seeds exactly once and never again, what find_or_create_by does to eight concurrent processes, the upsert_all statement that replaces it, and the import task this site actually runs. With the SQL, the savepoints and the transaction ids.

#rails #active-record
· 14 min read · 26 views

What a Rails scope actually returns

A scope is a class method that returns a relation, except when the body returns nil and Active Record hands back the receiver instead. With the merge rule that deletes half your WHERE clause, and why default_scope inverts an ORDER BY without raising.

#rails #testing
· 13 min read · 16 views

RSpec vs Minitest in Rails

What Rails already ships, what rspec-rails 8.0.4 adds on top, the failure messages that are not a matter of taste, and four measured timings that show the speed gap is the parallel runner and nothing else. With the verdict and what would reverse it.

#rails #jobs
· 14 min read · 11 views

Rails cron without a crontab

How Solid Queue 1.7.0 runs recurring jobs: the config/recurring.yml format, the four Fugit schedules that parse clean and run wrong, what a deploy does to the task table, and the unique index that makes two schedulers safe until it is cascaded away.

#rails #security
· 18 min read · 16 views

Receiving webhooks in Rails

Verifying an HMAC signature before the body is parsed, the three ways Rails takes the raw bytes away from you, idempotency as a unique index rather than an exists? check, and the replay window that does not dedupe anything. With the error text from stripe 19.6.2, actionpack 8.1.3.1 and PostgreSQL 17.7.

#rails #postgresql
· 21 min read · 11 views

Search in Rails, four rungs up

ILIKE, a trigram index, a generated tsvector column with GIN, pg_search, and a separate engine. What each rung finds, what index serves it, and where the plan stops being cheap. Measured on PostgreSQL 17.7 against 200000 rows.

#rails #active-record
· 16 min read · 26 views

Optimistic locking, and the row lock it is not

What lock_version does to the UPDATE, when StaleObjectError fires and the three methods that interact with it badly, and what SELECT FOR UPDATE buys instead. Measured against activerecord 8.1.3.1 and PostgreSQL 17.7 with four concurrent writers.

#rails #active-record
· 18 min read · 16 views

Rails N+1 queries, from includes to strict_loading

What includes, preload and eager_load each emit, the runtime branch that makes includes two different queries, and the detection Rails ships without a gem. With the two cases strict_loading watches go straight past.

#rails #active-record
· 20 min read · 21 views

Money and decimals in Rails

Why a float loses a penny, what a Postgres numeric column gives you instead, and the case for storing cents in an integer. With the casts Active Record 8.1 performs on the way in, the two rounding rules that disagree, and the currency that has no cents.

#rails #active-record #postgresql
· 18 min read · 35 views

Rails migrations that do not break

The four things a change method cannot undo, what adding a defaulted column really costs on two million rows, why a concurrent index needs its transaction turned off, and the deploy window where the new schema meets the old code. Measured on PostgreSQL 17.7 and activerecord 8.1.3.1.

#rails #i18n
· 19 min read · 16 views

Rails i18n: lookup, fallbacks, plurals, dates

What a leading dot resolves to, why the controller's version of it differs from the view's, the three different answers a default Rails 8.1 app gives to a missing key, and the fallback setting that renders an English date on a French page without a warning.

#rails #deployment
· 15 min read · 19 views

Rails health checks and what /up does not check

The 26 lines behind rails/health#show, what a green /up proves and what it cannot, and the arithmetic that turns a database check on that route into every server in the fleet failing at the same second.

#rails #testing
· 15 min read · 22 views

Rails fixtures vs factories

What the fixture loader emits, what factory_bot 6.6.0 costs per object, and the measured gap between them on a real Postgres. With the fixture that loads a row your model would reject, and the verdict.

#rails #active-storage
· 22 min read · 23 views

Rails file uploads with Active Storage

has_one_attached, direct uploads, the S3 service, and the content type check that most applications get backwards. With the Marcel results, the attach source, and the two ways a variant fails.

#rails #testing
· 16 min read · 28 views

FactoryBot: the factory that builds too much

Sequences, traits and associations in factory_bot 6.6.0, the four-row graph one create call produces, and the read that build_stubbed does not stub. With the SQL counts and the per-object timings from PostgreSQL 17.7.

#rails #active-support
· 20 min read · 26 views

Error handling in Rails, and the reporter you are not using

Where rescue_from belongs, what Rails.error.handle and .record actually do in Rails 8.1, the 400s your error service sees in development and not in production, and what a 500 page can say when the application that would render it has already failed.

#rails #configuration
· 12 min read · 22 views

Environment variables in Rails

ENV.fetch against ENV[], the mixed-type default nobody notices, Rails.env.local? and the environments it quietly excludes, credentials against env vars, and the .env file this site promises and never reads because dotenv was never installed.

#rails #active-record
· 16 min read · 22 views

Rails enum in practice

One enum declaration read out of a shipping Rails 8.1 app: what it generates, why prefix exists, and the integer against string decision measured on PostgreSQL. Including the query that silently becomes IS NULL.

#rails #ruby #postgresql
· 19 min read · 21 views

CSV import and export in Rails

Streaming 200000 rows out of a controller instead of building the file in memory, CSV.foreach on the way back in, the BOM rule that reverses depending on how you open the file, and why keeping 20000 invalid records keeps 20000 records. With measured peak RSS for every version.

#rails #security #deployment
· 12 min read · 26 views

Rails credentials, one file per environment

The encrypted file, the 32-character key, and the resolution order Rails uses to find both. Why a per-environment file replaces the shared one instead of merging with it, and why a missing key in production reports a problem with secret_key_base.

#rails #architecture
· 13 min read · 27 views

Rails concerns, and when not to write one

What ActiveSupport::Concern actually adds over a plain Ruby module, read out of activesupport 8.1.3.1, plus the three comparisons with a verdict each: against a module, against a service object, against a helper.

#rails #active-record
· 17 min read · 36 views

Rails callback order, printed

The real firing order for save, update and destroy on activerecord 8.1.3.1, what after_commit guarantees that after_save cannot, why a callback holding an HTTP call also holds a row lock, and every way throw :abort behaves that the guides do not print.

#rails #caching
· 18 min read · 22 views

Rails caching strategies, level by level

The four levels a Rails 8 request can be answered from, what cache_key and cache_version actually return, why touch: true is the only invalidation that holds, and the template dependency the digester guesses wrong.

#rails #caching
· 21 min read · 24 views

Cache keys in Rails

What Rails turns your cache key into before the store sees it, the stable key with a version beside it, why no store will list its keys for you, which four can delete by pattern, and the namespace your app already set. With the normalized keys printed and the rows counted.

#rails #active-record #postgresql
· 18 min read · 11 views

Rails audit log, and the writes that never reach it

The four columns an audit trail needs, what audited and paper_trail actually install, the six write methods that produce no audit row at all, and the Postgres trigger that catches them and cannot say who. Measured on activerecord 8.1.3.1 and PostgreSQL 17.7.

#rails #active-record #security
· 18 min read · 14 views

Active Record encryption and the query that finds nothing

What encrypts writes into the column, where each of the three keys is used, why a WHERE on a non-deterministic column returns zero rows without raising, and what happens to the index. With the ciphertexts, the EXPLAIN plans and the byte counts.

#rails #active-record
· 13 min read · 10 views

Polymorphic associations

The two columns, the index the migration writes, and the foreign key the database will not give you. Then the constraint that bites hardest: Active Record raises EagerLoadPolymorphicError rather than join a belongs_to polymorphic association, and the hand written join that is the only way past it.

#rails #active-record
· 13 min read · 21 views

jsonb columns in Rails, and the schema you gave up

Declaring one, the default that is not a Rails default, the containment query that needs a GIN index, what that index costs on write, and the typo nothing rejects. Measured on PostgreSQL 17.7 against 500000 rows, with the seventeen jsonb columns of a working Rails 8 app as the sample.

#rails #active-record
· 15 min read · 23 views

Counter caches by hand

What Rails' counter_cache hooks, the three questions it answers wrong (a condition, a scope, has_many through), and the one line of SQL two counters in this site use instead. With the UPDATE statements Active Record 8.1 actually builds.

#rails #views
· 13 min read · 13 views

View components vs partials

ViewComponent 4.15.0 and Phlex 2.4.1 against plain ERB partials, written from a codebase that has 54 partials and no app/components directory, with the line that would flip the choice.

#rails #hotwire
· 15 min read · 17 views

Turbo Stream actions, and the pair that breaks the page

The eight actions Turbo actually ships, read out of turbo.js: what replace does that update does not, why append silently deduplicates, where morph lives, and the broadcast that never arrives because the transaction had not committed yet.

#rails #hotwire
· 13 min read · 21 views

Turbo frame: Content missing

What fires turbo:frame-missing, the incident where one frame killed every article link on /yield, the difference between target on the frame and data-turbo-frame on the link, and the order to check things in.

#rails #hotwire
· 16 min read · 21 views

Stimulus controllers in practice

Targets, values, actions and the lifecycle, read out of the twenty-five controllers in a working Rails 8 app. Including the disconnect that Turbo's snapshot cache makes load-bearing, and what testing Stimulus controllers actually costs.

#rails #active-record
· 15 min read · 14 views

Pagination without a gem

limit and offset in plain Active Record, the two ways offset pagination fails, and the keyset query that fixes both. With the EXPLAIN plans, including the cursor form that silently stops using the index.

#rails #email
· 12 min read · 26 views

Action Mailer in production

default_url_options and why a mailer needs one, SMTP settings read from encrypted credentials, previews against letter_opener, what deliver_later really enqueues, and the email that renders perfectly and links to example.com.

#rails #jobs
· 14 min read · 24 views

Solid Queue vs Sidekiq

Solid Queue 1.7.0 against Sidekiq 8.1.7 on the axes that decide it: what you run, polling versus BRPOP, the dashboard, retries after a SIGKILL, and what the paid tiers cost.

#rails #caching
· 14 min read · 19 views

Solid Cache vs Redis: the case for a cache on disk

Rails 8 Solid Cache keeps cache entries as SQL rows instead of RAM. The argument is hit rate, not speed, and it holds only if you read how Solid Cache trims, what it encrypts, and where it is genuinely worse than Redis.

#rails #upgrade
· 12 min read · 27 views

Upgrading a Rails 7 app to Rails 8

What `rails app:update` edits and what it leaves alone, the load_defaults ladder, and the four changes that actually bite going from 7.x to 8: Propshaft, the Solid trio, the authentication generator and Kamal.

#rails #deployment
· 13 min read · 35 views

Deploying Rails with Kamal 2

What Kamal 2 replaced, what Thruster does inside the container, where the four Solid databases come from on a first deploy, and why kamal-proxy's health check can pass while production is already broken.

#rails #security
· 11 min read · 20 views

Active Storage CVE-2026-66066: are you affected, and what to run

CVSS 9.5, unauthenticated, and the exposed configuration is the one load_defaults 7.0 gives you. How to decide in a minute, what to upgrade to, and what the patch breaks.

#ruby #ruby4
· 9 min read · 29 views

Ruby Box: two gems, one monkey patch, no fight

Ruby 4.0 can run two incompatible monkey patches on the same method in one process. Here's the API, what's actually isolated, and the one call shape that silently breaks inside a box.

Parsing JSON straight into objects, with a hook almost nobody uses
Video · 5 min read · 37 views

Parsing JSON straight into objects, with a hook almost nobody uses

JSON.create_id, a json_create hook and create_additions turn JSON.parse into an object factory. One iteration instead of two, and it has been in the standard library all along.

A URL shortener in pure Rails, and the one line that decides who it redirects to
Video · 6 min read · 214 views

A URL shortener in pure Rails, and the one line that decides who it redirects to

One model, one controller, one route. Slugs that generate themselves with SecureRandom, a click counter that does not restamp the row, and why allow_other_host exists at all.

An HTML generator in one hook, and the Ruby method lookup path
Video · 8 min read · 299 views

An HTML generator in one hook, and the Ruby method lookup path

One method_missing hook generates every HTML tag. The Ruby object model, the ancestor chain, the method lookup path, and the two tag names Kernel already owns.

A palindrome checker in 7 lines, and what String.include really does
Video · 5 min read · 44 views

A palindrome checker in 7 lines, and what String.include really does

Seven lines of Ruby add a palindrome? method to String. What the POSIX regex, Module#include and Unicode accents actually do to it.

Operator precedence without eval, in 20 lines of Ruby
Video · 8 min read · 33 views

Operator precedence without eval, in 20 lines of Ruby

A Ruby interview question: evaluate 2+3*4 as 14 without eval. Precedence as an array of operator levels, leftmost-first reduction, and where it breaks.

#rails #infrastructure
· 7 min read · 247 views

Running Rails 8 without Redis

Solid Queue, Solid Cache and Solid Cable put jobs, caching and WebSockets in PostgreSQL. What that buys, and the one place Rails 8 rate limiting and Solid Cache do not compose.

#saas #rails #indie-hacking
· 1 min read · 40 views

Launch your SaaS in a weekend

A practical, no-fluff plan to go from idea to a paying-ready Rails app in two days - using the boilerplate's built-in auth, billing and onboarding.