When code is cheap, Sixfold's Rails engineers get paid for architecture

When code is cheap, Sixfold's Rails engineers get paid for architecture

Sixfold's engineers barely write or review code anymore. Joanna Wang explains how agents made Rails magic legible, and why race conditions from homegrown orchestration pushed the team to Hatchet.
Ruby on Rails
24.9K Subscribers
9 min read

Key Findings

  • • Once agents write your code, your real job becomes building harnesses like AGENTS.md files, review guidelines, and docs so the next agent doesn't wander.
  • • Callbacks and state machines handle linear lifecycles well, but when your workflow fans out and joins back, give it an explicit DAG before race conditions show up everywhere.
  • • Every magic-heavy gem you adopt is code you may inherit, so decide early which dependencies you're willing to fork and own.
  • • Rails magic stops hurting newcomers when an agent can trace generated methods for you, so conventions now mostly pay off in less code.
  • • Messy code still costs you when agents do the work, because you pay in tokens, and those token prices are subsidized for now.

Joanna Wang's team at Sixfold has mostly stopped writing code. They've also mostly stopped reviewing each other's pull requests. Agents do both, and a senior engineer's day now goes into building the harnesses that keep those agents on track.

"Nowadays code is cheap."

Wang said this on Robby Russell's On Rails podcast without sounding worried. She's a senior software engineer at Sixfold, which builds AI underwriting tools for insurance companies, and she thinks the change has made the parts of the job she always cared about more visible. Her conversation with Russell ended up being about two failure modes. One is the kind of code a small team writes quickly and later regrets. The other is the kind agents will write if nobody gives them structure. Rails has a stake in both.

A Java developer's view of Rails magic

Wang didn't come up through Ruby. She studied computer science with Java, spent about a year in Python, and later worked in Groovy on Grails, another JVM stack. Sixfold was her first Rails job, and her first reaction was the one you'd expect from someone used to explicit code.

"It was a lot of magic," she said. She'd follow a method call line by line, then into library code, before finding the helper that Rails had defined behind the scenes.

She sees both sides of that magic. Rails let a small, early team write very little code. They didn't list every database column or hand-write every API response. That speed helped Sixfold ship a working underwriting product while engineers were scarce and, as she put it, "you actually had to handwrite code." She likes Ruby's dynamism too: you can build a class at runtime and call methods on it, or call compact on an array and move on.

She calls it "a double-sided sword." Terse code is hard to follow when you don't know the conventions, and that hurt Sixfold's Python engineers most. The company runs two codebases. The Rails app is the web application, the REST API behind a React frontend, the job orchestrator, and the only entry point to the database. A Python microservice handles every AI call to OpenAI or Anthropic and reports results back to Rails. With 20 to 30 engineers and a lot of coordination between the two sides, Python developers kept landing in Rails code where, in Wang's words, "almost nothing is explicitly defined."

Callbacks and state machines turned fan-in into a minefield

Sixfold's hardest Rails problem was one the team built for itself. Workflow orchestration ran on the state_machine gem plus ActiveJob, with ActiveRecord callbacks (after_create and similar) triggering transitions. Background work went through Faktory, a Sidekiq-style job server that also works in other languages, so the Python side used it as well.

Wang described two separate kinds of pain. Under heavy load, Faktory queues backed up, and it was hard to see the backlog or clear it fast. The bigger issue was the orchestration design. State management was spread across models. Each transition assumed the previous database transaction had committed cleanly. Transitions fired side effects that started the next workflow. Every race condition got patched with more logic, and that logic spread things out even further.

Her example is web ingestion. For a company being underwritten, Sixfold searches the web, scrapes the pages, and turns them into documents. Scraping turned out to be unreliable: one company can go by several names, and unrelated companies can share one. So the team added a relevance check. After ingestion, every document goes to the Python service, where an AI model decides whether it belongs. User-uploaded documents are processed in parallel at the same time.

That produces a fan-out, fan-in pattern. Work splits across many parallel jobs, some in Rails and some in the Python service, and then everything has to join back together before analysis can start. Nothing tells you when "everything" has finished, so the join point is where callback-driven orchestration breaks.

"It became a nightmare," Wang said. "At every possible place there is a race condition."

The general lesson applies to plenty of Rails apps. Model callbacks and state machine gems handle linear lifecycles well. Once a workflow becomes a directed graph with parallel branches and cross-service dependencies, the code needs an explicit representation of that graph. Otherwise the graph ends up encoded in side effects nobody can see all at once.

Buying a workflow engine instead of rewriting one

The team did consider a rewrite. Wang says they did it honestly: the workflows had grown more complex than their original design, and "maybe we didn't think through the architecture that well." A rewrite had been on the table before anyone had heard of Hatchet.

Hatchet, an open-source workflow orchestration engine, won through a cautious pilot. An internal tool moved to it first, then some smaller Sixfold workflows. With Hatchet, engineers declare a DAG of steps, the conditions for each next step, and the points where one workflow triggers another, and the engine handles execution. It also comes with a dashboard for inspecting and cancelling jobs, which callbacks never provided. Wang said the pilots showed it was "possibly more efficient and easier to maintain."

She describes Sixfold's default as buy over build, and the reasoning is short: "Our value is not in creating those smaller tools but in creating this overall system for insurance underwriting." The migration is still going on. Her advice to a hypothetical successor on her last day was blunt: there are a lot of race conditions in the current workflows, things are moving toward a better state, and you should watch out.

The cost of a gem nobody maintains

Relying on outside dependencies has a downside, and Sixfold is dealing with it through JSONAPI::Resources. The gem produces JSON:API-compliant REST responses and connects straight through to the model layer. Sixfold's controllers are "almost 100% empty": the files exist, but there are no queries, no serializers, and no hand-written create actions. Authorization goes through Pundit with read and write roles, and each resource declares which operations it allows.

When a new model arrives, that's a big productivity gain. Wang points to two costs.

The first is rigidity. JSONAPI::Resources builds error responses its own way, so custom ActiveRecord validation errors are hard to return in the shape the team wants. Any customization means working against the gem.

The second is abandonment. The gem is no longer actively maintained. Russell checked during the recording and found the most recent commit, from three months earlier, was a compatibility fix for Ruby 2.6 and Rails 6.0. Sixfold now maintains its own fork to patch problems upstream won't fix. That's the usual endpoint for magic-heavy gems: when the maintainer leaves, the magic becomes your code.

The same caution applies to upgrades generally. The npm supply-chain attacks, which affected Sixfold's TypeScript frontend, made the team more alert. Automated tools watch for new vulnerabilities and fixes get priority. Major upgrades to Rails or ActiveRecord skip the normal Dependabot path, and a person verifies that everything still works, even now. Russell noted that RubyGems and Bundler are adding a cooldown feature so projects don't pull in brand-new releases right away.

Going fully agentic moved the work into harnesses

The rest of the stack is conventional: RSpec, FactoryBot, Capybara for end-to-end tests, and Datadog for monitoring. How the team works on top of it has changed.

Sixfold went through a deliberate, company-wide move to agentic coding, and Wang says it's finished. Some engineers still read the code their agents write. Others have reached the point of "if it works, then it's good." CodeRabbit reviews every PR, tuned with guidelines about what to look for. Every engineer is asked to maintain documentation and an AGENTS.md file covering architecture, patterns, and the pitfalls they've run into, so the next agent that touches a feature has something to follow.

"A lot of the work is actually putting in the harnesses for the agents instead of just writing code," she said.

The agents have flaws. Sometimes they ignore instructions unless the request is phrased very precisely. Wang also sees them being overly defensive. When a string comes from upstream code that guarantees its format, Claude will still add 20 lines of validation for cases that can't occur. A small annoyance. Over time, though, it's how an unreviewed codebase turns into sprawl.

Custom tooling has moved in the same direction. Sixfold used to build Thor-based CLI commands for production tasks and recurring customer-support exports, so engineers didn't have to open a Rails console. Most support work later moved to a separate hosted internal tool that still goes through Rails. Setting up a development environment is now a set of Claude skills that handle the whole thing.

Why agents and Rails conventions work well together

Wang's most useful observation for Rails teams is that agentic tools remove much of the cost of Rails magic that she struggled with as a newcomer.

In a verbose language, a global search finds a method's definition. In Rails, the method might be generated at runtime or come from a framework helper you'd never think to search for. "With Claude Code that's not a problem anymore," she said. "You ask it, it can actually trace for you."

That has changed how work crosses the language boundary at Sixfold. Python engineers used to write down what they needed from the Rails side and hand it to Rails specialists. Now, Wang says, "they can actually take it to done."

This changes the old argument over convention and explicitness. Rails magic had two costs: someone had to learn the conventions, and the code was hard to trace. An agent that already knows Rails conventions and can trace the code on request covers most of the second cost. What remains is the benefit: less code, less boilerplate, and shared patterns an agent can follow.

Cheap code raises the price of bad architecture

Wang isn't sentimental about typing code. She says she never thought the job was about syntax, since documentation and libraries were always available. What agents have done is make the real value obvious: thinking a problem through, designing a system that holds up under load and stays understandable, and knowing how it will scale.

Sixfold's recent history supports her. Agents didn't cause the fan-in race conditions, and agents writing callbacks faster wouldn't have fixed them. That took an architectural decision to represent the workflow as an explicit DAG in a dedicated engine. The unmaintained gem is also a judgment problem, a question of which dependencies you're prepared to eventually own.

Asked for a book recommendation, Wang named Uncle Bob's Clean Code and Clean Architecture, then questioned her own answer: maybe messy code is fine now, as long as it works. Russell disagreed. If agents can't work with a codebase cleanly, the cost comes back as tokens instead of engineer hours, and he pointed out that token prices are currently subsidized.

That argues for the harness work Sixfold is already doing. Clean architecture and explicit orchestration now benefit agents as much as human readers, because an agent can only follow the structure a codebase actually has.

Create articles like this

Start Free →
Mr. Article

Share Article

Share this article with anyone. No login required to view.

Share via
or copy link directly
https://mrarticle.blog/shared/kHC3d1K6wy4B9DTZx4aBUw7uh5RJb0aX

Anyone with this link can view a read-only version of this article.

Link copied to clipboard!