Things I've written down.
Ruby, Rails, performance, security and applied machine learning. Updated when something feels finished.
The callback that fires in a test you didn't write
Active Record callbacks are convenient until a fixture, a seed script or a bulk import triggers side effects nobody expected.
Indexes that earn their keep
Adding an index is the reflex fix for a slow query. Column order, cardinality and write cost decide whether it does anything at all.
Fat models, skinny controllers, and the third option
Both halves of the old advice produce the same result at scale: one enormous class. Naming the operation is what actually helps.
Three threads, not five: what the GVL actually costs you
Rails ships a default most people never revisit. Here's how CRuby's global lock decides your thread count for you, and the arithmetic for picking workers.
SQLite in production, honestly
It is a real option for real applications now — with one constraint that decides your entire deployment shape.
Four caches, and knowing which one you need
HTTP, fragment, low-level and query caching solve different problems. Reaching for the wrong one hides the bug instead of fixing it.
Where Rails memory actually goes
Your app is not leaking. Ruby freed the memory and the allocator kept it. Arenas, fragmentation, and the two environment variables that fix most of it.
Your connection pool is probably wrong in one of two directions
ActiveRecord::ConnectionTimeoutError and "too many connections" are the same mistake made in opposite directions. The arithmetic is simple.
What belongs in a job, and what belongs in the request
Moving work to a background job doesn't make it faster. It moves who waits, and sometimes that's the wrong trade.