Code Is Cheap, Developers Still Aren't: Joanna Wang on Rails, Workflow Orchestration, and Going Fully Agentic at Sixfold

Open on YouTube ↗
Overview

In this episode of On Rails, host Robby Russell talks with Joanna Wang, a senior software engineer at Sixfold, which builds AI underwriting tools for insurance teams. Wang came to Rails from Java and other languages and recently joined a team that has, by Wang's account, stopped writing code by hand altogether. The conversation covers why a homegrown workflow system built on state machines and callbacks became brittle, what the team gained by moving to Hatchet, the costs of relying on an unmaintained gem, and why agentic coding tools may make Rails' "magic" easier to live with. Wang's central claim is that code is now cheap, but the value of a software developer lies in architecture and problem-solving rather than in writing code.

18 min read

Coming to Rails from Java

Asked what keeps Wang on Rails, Wang points to the maturity of the ecosystem. It has many maintainers and a large community and developer base, so whatever problem you hit, you are probably not the first to encounter it.

Rails was new to Wang at Sixfold. Wang studied computer science, started with Java in college, spent about a year on Python, and then worked in Groovy on Grails, which Wang describes as essentially another JVM language stack. Russell asked whether Wang had ever used JRuby. Wang hadn't. Russell explained it as a Java-based Ruby interpreter that let many organizations 15 to 20 years ago "sneak Rails into their Java environment."

What Sixfold Does

Wang describes Sixfold as "an AI underwriter." Insurance underwriters gather many data points, review documents sent by brokers, and then judge how risky a company or person is to insure. Sixfold replaces much of that human process. A case might start with just a name and a few documents, or with an email forwarded to the system. Sixfold then carries it through to a final assessment of how much risk the company represents.

On the AI side, the team uses different models for different evaluations. They evaluate against ground truth to confirm that questions are answered correctly and that a given model performs well, and they tune prompts through evaluation to make results as accurate as possible.

Where Rails Sits in the Stack

The main application runs on Rails. At the time of recording the team was in transition: workflows had been managed with a state machine gem in Ruby, and they were moving to Hatchet, a workflow orchestration tool.

Wang explained the difference in approach. The old system orchestrated workflows through code: the state machine plus Rails callback hooks such as on-create. With Hatchet, the team defines a DAG (a directed acyclic graph) of steps. It specifies when one workflow triggers another, when a workflow ends, and what conditions let the next step run. Hatchet then acts as the engine that executes the orchestration.

Engineering has roughly 20 to 30 people, and a significant share work in Python. Wang says the Rails and Python sides work "very closely." The Rails app serves as the web application, the worker that orchestrates jobs, and the only entry point to the database. The Python codebase is effectively a microservice for AI interactions. It sends requests to OpenAI or Anthropic and reports results back to the Rails app. No other microservice touches the database directly. Rails also serves the REST API. The frontend is built in React, not Rails views.

Writing Less Code, and Living With the Magic

Wang says Rails' biggest benefit was its lack of verbosity: you can write very little code. Wang called this "a double-sided sword" because the same property that saves effort had historically caused problems too.

Coming from Java, Wang experienced Rails as "a lot of magic." Early on, understanding code meant jumping from line to line, sometimes into library code, to discover that a helper existed under the hood even though nothing in the app defined it. Only then would it become clear where a method call came from.

The effect on colleagues was uneven. Some knew the internals well. Wang says the Python-focused engineers struggled whenever they touched the Rails side, at least before agentic coding, because "almost nothing is explicitly defined."

Wang still appreciates Ruby's dynamism. For example, you can construct a class dynamically and then call a class method on it, and conveniences like calling compact on an array do the work for you. Wang again described this as two-sided. It helped the team a lot, but it also caused confusion and frustration for people unfamiliar with the codebase or with Rails.

For the company's growth, Wang says writing less code mattered most. With very few people, and in an era when "you actually had to handwrite code," the team could stand up an initial app quickly and start selling AI underwriting to customers. Not having to list every database column or spell out every frontend response was "actually really like acceleration."

When Homegrown Orchestration Got Brittle

Before Hatchet, the team used the state machine gem together with ActiveJob and Faktory, which Wang described as basically Sidekiq but usable from other languages. That mattered because the team also started using Faktory in Python for job handling.

Wang described two separate problems. The first was Faktory itself: under heavy load, the team had trouble scaling. Queues would back up, and when they did it was hard to see what was happening or to fix it quickly.

The second, and more serious, problem was the in-house orchestration built on state machines and callbacks such as after-create hooks. As workflows grew more complicated, the system became very brittle. State management was scattered. Transitions depended on database transactions completing properly, and each transition triggered further side effects to start the next workflow. Wang says this setup created race conditions. The team added more logic to handle them, which scattered the logic even further.

Fan Out, Fan In, and Race Conditions Everywhere

Russell asked for a concrete example, and Wang described web ingestion. For a given company, the system searches for information online, scrapes the relevant websites, turns them into documents, and processes the web content.

The team found that scraping often returned uncertain results. The same company may go by different names, and unrelated companies may share the same name. So they added a step: after scraping and ingesting documents, they check whether each document is actually relevant. That check fans out across documents and then fans back in. Meanwhile, user-uploaded documents for the same company may be processed in parallel.

The fragile moment came when everything had to fan back in, so the system could declare document or web ingestion complete and move on to analysis. Jobs were spread out, fanning out, calling the Python microservice to judge relevance, and then fanning back in. Wang called it "a nightmare," with a race condition "at every possible place."

Buy Versus Build, and Piloting Hatchet

Russell asked whether the team had hesitated to hand this problem to an outside platform. Wang said both instincts were present. The team generally prefers buying over building when a better tool exists. It also seriously discussed rewriting the whole system, partly because, as Wang acknowledged, the team may not have thought the architecture through well as it added complexity. A rewrite of these workflows had been under discussion for a while, even before Hatchet was available.

Adoption was gradual. Some other internal tooling used Hatchet first as a pilot to see how well it handled workflows. The team then moved smaller pieces onto it to address specific problems. Wang says it proved possibly more efficient and easier to maintain, partly because of its dashboard, where you can view and cancel jobs. Wang said the decision drew on both considerations: the long-standing desire to rewrite and the availability of a better tool.

JSON API Resources: Empty Controllers and Rigid Errors

Another key piece of the stack is the JSON API Resources gem. It produces JSON:API-spec responses for REST endpoints and connects "almost directly" from the database to the controller layer. Wang says the team's controllers are "almost 100% empty." The files have to exist, but the gem handles the rest. For a new model, the team barely writes anything and gets a standard JSON:API response back, with no serializers, queries, or ORM calls to write.

Authorization sits alongside the gem. The team uses Pundit, defines read and write roles, and checks for the right access. JSON API Resources also lets them declare which operations a resource supports, such as create, read, or index.

The downside, in Wang's words: "With the magic comes the rigidness." Error handling is the clearest example. The team can define ActiveRecord validation errors as usual, but the gem has its own fixed list of error types and its own way of populating them. That makes it hard to surface errors the way the team wants, and customization is painful. On top of that, the gem is no longer maintained, so its issues won't be fixed upstream. The team keeps its own fork with some fixes. Russell checked the gem's GitHub during the conversation and noted that the most recent update, about three months earlier, fixed a Ruby 2.6 logger compatibility issue for Rails 6.0.

Dependencies and Supply Chain Security

Russell asked whether the team's approach to external dependencies had changed with agentic development. Wang said the philosophy is largely unchanged. Hatchet illustrates how they treat paid vendors, and for open-source gems the team still looks first for existing tools. As Wang put it, the company's value lies not in building small tools but in building the overall system for insurance underwriting.

Russell raised security, mentioning that upcoming versions of Bundler and RubyGems were announced to include a cooldown feature to avoid upgrading too quickly into a compromised package. Wang said security had been a real concern, prompted not by Ruby incidents but by the npm attacks, since the frontend uses TypeScript. The team runs automated tools that monitor for new vulnerabilities and prioritizes fixing them quickly. For major upgrades, such as Rails itself or a major ActiveRecord update, they skip routine Dependabot upgrades and do a proper migration with a person verifying that everything works, "even in the age of AI."

Testing and Observability

For testing, the team uses RSpec, FactoryBot for test data, and Capybara for some end-to-end tests. Datadog handles essentially all monitoring.

Going Fully Agentic

Asked about AI tools in daily work, Wang said the team uses "basically every MCP." At one point the organization deliberately worked to bring everyone up to speed on agentic coding, and Wang considers the migration to fully agentic coding now complete.

Wang named two frustrations with the tools. First, they sometimes don't do what you ask, or you have to phrase the request very specifically to get what you need. Second, they can be "overly defensive." Wang's example: Claude might add around 20 extra lines to validate that a string isn't empty and contains no odd characters, even though the string comes from upstream code where that case can never occur.

When Russell asked what "fully agentic" means in practice, Wang was direct: "we don't write code anymore." Practices vary in reading code. Some people still read what agents produce, while others have reached the point where "if it works then it's good" and no longer care to read it. Across the organization, nobody writes code, and people mostly don't review each other's PRs either, because agents review PRs too. Much of the engineering work has shifted to building harnesses for agents.

Harnesses for Agents

Wang described two parts of that harness. First, CodeRabbit reviews every PR. The team has tuned it and given it guidelines about what to watch for. Second, everyone is asked to write documentation, including an agent.md file. It records the pitfalls encountered, how a feature is designed, the general architecture, and the patterns in use, so an agent extending a feature later has guidelines to follow.

What the Job Becomes When Code Is Cheap

Wang is excited about the shift: "what a time to be alive," and a remarkable time to work in the industry, because it has "completely changed what it means to be a software developer." Writing code once carried an almost elitist status, Wang said, but "nowadays code is cheap."

Wang doesn't think this erases the developer's value. It relocates it. The value now lies in thinking through a solution to a problem, designing an architecture that is robust rather than brittle, and knowing how to scale it. Wang added that they never personally believed the job was about writing code, since syntax and library documentation were always available online. What's different now is that this has become explicit.

Why Agentic Tools Pair Well With Rails Magic

Wang argues agentic coding is especially useful for Rails because of the magic. In more explicit languages, a global search finds a method's definition. In Rails, the method may be constructed dynamically or come from a non-obvious framework helper. With Claude Code, Wang says, that is no longer a problem, because you can ask it to trace where things come from.

This has changed how the Python engineers work. Previously, they would describe what they needed on the Rails side and hand it to Rails engineers. Now, Wang says, "they can actually take it to done" themselves.

CLI Tooling and Claude Skills for Dev Setup

For developer experience, the team has long had clear instructions and commands for starting a dev environment. It has also built CLI commands for production-specific tasks, so engineers don't have to open the Rails console. Many originated in customer support requests, such as a particular kind of export. Early on these were written with Thor. When a customer request kept recurring, the team would turn it into a CLI command.

The team later built a separate hosted internal tool to handle most customer support requests. Wang described it as a kind of playground for customer support, and it goes through Rails rather than connecting to the database directly. More recently the team has added a set of Claude skills, including one that sets up the dev environment for you.

Russell mentioned that other teams he has spoken with build Claude skills that let customer support staff run natural-language requests against a protected Rails console. He also recalled a recent Rails addition, whose name he couldn't remember, that wraps Rails runner functionality to generate queries.

Multi-Tenancy, Compliance, and Regional Data Residency

Sixfold is multi-tenant. Wang cited the requirements of the insurance domain: HIPAA compliance, SOC 2 compliance, and some clients whose regional rules require their data to stay in a specific region. Combined with a heavy data-processing load, Wang says, a multi-tenant setup made more sense.

Infrastructure is split by region. Each region that needs it, such as the EU, gets its own hosted instance of the components, including Hatchet. The team can deploy to regions selectively but generally deploys to all of them at once.

Feature Flags and Moving QA to an Agent

Feature flags tie into multi-tenancy, because different clients want different features enabled. The team started with PostHog, then moved to its own approach: environment variables plus an internal tool for flipping them. Most feature flags live on the Rails side. Features specific to the AI side, such as a particular prompt, are controlled by an environment variable on that microservice. The internal platform can also apply a feature to a defined set of customers.

The team doesn't do percentage rollouts, such as enabling a feature for 50% of customers. Instead it rolls out 100% to a group of alpha or beta customers, or to UAT and sandbox environments first, and then to production.

QA is partly automated through frontend tests and Capybara. Full QA was once entirely human, and the team at one point had a dedicated QA person. After a few iterations, they now use QA Wolf.

Advice on the Way Out, and an Open Question

Russell asked what Wang would tell a successor in five minutes on a last day. The answer was short. The current workflows have a lot of race conditions, so watch out. The goal is to move things to a better state. Most things are documented somewhere in Notion, and if you have Claude, you'll be fine.

For a book recommendation, Wang said they have historically recommended Clean Code and Clean Architecture by Robert C. Martin ("Uncle Bob"). Wang then questioned whether that still holds in the age of agentic coding: "maybe messy code is okay as long as it works."

Russell left the question open. He described the tension between knowing when to look under the hood and when to rely on agents, and the need to steer agents toward coherent, maintainable code. Otherwise, he suggested, teams may burn tokens in the future the way they once burned human hours on messy codebases, and he noted that the industry is currently in "a slightly subsidized token era." Neither offered a definitive answer on whether clean-code discipline still matters when agents write the code.