How AI Coding Agents Helped Us Track Down a Nasty Memory Leak in Rails
- Introduction to the problem
- How coding agents can help
- Using derailed_benchmarks
- The investigation process
- The TailwindMerge case
- Lessons learned
Every developer knows the dread of watching your server's memory usage climb steadily over time, like a slow leak in a tire you can't quite locate. You redeploy, the numbers reset, and life goes on—until it doesn't. At some point, your container hits its memory ceiling, the OOM killer kicks in, and you're scrambling to figure out what went wrong.
We ran into exactly this scenario. Our server memory would steadily increase across deployments until either a fresh deploy or a container restart pushed the problem down the road. It wasn't a critical issue—we deploy frequently—but it was definitely a sign that something was wrong under the hood.
The breakthrough came when we started using coding agents to automate investigation tasks. These AI-powered tools can spin up multiple background investigations, run reproducible tests, and iterate until they find the root cause. It's like having a junior developer who never gets tired and always follows your exact instructions.
We tasked these agents with profiling our application using derailed_benchmarks, a powerful tool for simulating production load in a local environment. The challenge? Setting up a true production-like test environment can be tedious, and nobody wants to break their local development setup. But for autonomous agents, this friction disappears—they can experiment freely without affecting your workflow.
The initial investigation pointed to several plausible culprits. Some agents suggested preloading N+1 queries, others recommended switching to jemalloc, and a few flagged our performance instrumentation as potentially excessive. One interesting suggestion involved TailwindMerge::Merger objects being allocated on CurrentAttributes, though we initially dismissed this since CurrentAttributes clear between requests.
The problem was that results were inconsistent. With too many variables in play, it was hard to distinguish real issues from development-only noise. Most agents blamed memory fragmentation rather than a true leak, and without clear evidence, we set the investigation aside temporarily.
Then we simplified our test case. Instead of profiling complex authenticated pages, we focused on a simple static splash page—and this is where things got interesting. With a much cleaner target, four out of five agents converged on the same diagnosis: retained memory in TailwindMerge, complete with benchmark evidence to back it up.
The fix was elegant: move the TailwindMerge::Merger off CurrentAttributes and reuse it through thread-local storage instead. We tested this in staging, and the results were dramatic. Memory utilization that previously climbed steadily across requests now stabilized after just a few cycles.
But we didn't stop there. We pointed our coding agents toward the tailwind_merge gem itself to find the root cause. The agents discovered that the gem's configuration merging code was mutating a global defaults constant and holding references to every instantiated object in that global configuration—definitely a memory leak, not fragmentation.
We worked with the upstream maintainers to ship a proper fix, complete with regression tests and a benchmark script to prevent future breakage. It's a win for the entire Rails ecosystem.
The lesson here is that AI coding agents excel at objectively measurable tasks. Memory profiling, benchmarking, and reproduction scripts are perfect candidates for automation. The key is to start simple, isolate variables, and use multiple agents to cross-validate findings. What seemed like a complex production mystery turned out to be a reproducible, measurable problem that autonomous agents could tackle while we focused on building features.
If you're fighting unexplained memory growth in your Rails app, derailed_benchmarks combined with AI coding agents might just be the debugging workflow you've been missing.
Read in other languages: