Elastic Software: Calculating tech debt using LLM tokens
A talk by Michael Carroll, founder of Coolhand Labs, given at XO Ruby NYC in October 2026.
Flexibility is a known engineering trick
Skyscrapers sway on purpose. Sidewalks get a strip of soft putty at every expansion joint. A car's frame is designed to crumple. In every case, a little deliberate softness is what keeps the rigid parts from cracking under load. Softness and flexibility add resilience in engineering — that is not controversial once you say it about concrete and steel.
Now say it about tech debt. Ask a room whether tech debt is good or bad and you get a chorus of "bad." Ask about a credit card and the room splits. Ask about a mortgage and it usually flips to "good." The answer was never really about the debt — it was about whether it was leverage, taken on deliberately and paid down on purpose, or just debt, taken on carelessly. Tech debt can be an asset or a liability depending on what you do with it. But that just restates the real problem: it's hard to quantify the advantage tech debt gives you if you can't first quantify the cost of carrying it.
Health tech, 2010s: the parser that could not stay clean
Here is the shape of the problem, from my own history building health care
software at RubiconMD. You sign a clinical client. You need their patient
data in your system. There is a standard for this — FHIR. Easy peasy. Day
one, the parser is a single clean method: hand it the data, call
fhir_parser, done.
A hundred clients later, that one line has become a case statement with a branch per client, running on so long the slide itself becomes illegible — which is the point. You stop trying to read it long before anyone finishes writing it. And it does not matter whether a person or an AI coding agent wrote that case statement: it is terrible software architecture either way. It is hard to DRY, any data format change can break it, every breakage is a scramble, it adds cost to every new contract, and the more clients you sell the worse it gets. That is the worst kind of debt — uncontrollable, unavoidable, and unmanageable.
Swap the parser for an LLM call
Let's try this again. Same method, same signature — comment out
fhir_parser.call(data) and replace it with a RubyLLM call:
instructions describing the record, a schema describing what should come
back. The call site never changes; only what's inside it does.
The gut reaction is to recoil — handing parsing to a model feels wrong on first sight. But look at what you just bought: zero brittle code, instant integration, and the flexibility to handle external factors you can't change. Then the second objection lands right behind the first: won't this cost a lot of money in LLM tokens? The answer is the actual thesis of this talk — refactor the high-volume paths back into deterministic code, keep AI for everything else, and for the first time your tech debt has a quantity you can measure.
Enter squishling
Wouldn't it be nice if there were an elegant Ruby DSL for managing and tuning
exactly that code/AI hybrid? That's squishling
— my own gem, built directly on this talk's premise. Don't just use AI to
write code; use AI to not need code. A squishling class declares a
purpose and an output_schema and nothing else — no
call method at all. A method with no implementation is squished
straight to the LLM by default.
First real run against all 100 fixtures — real Synthea-generated FHIR patients, rendered into dozens of genuinely different wire shapes, from canonical FHIR and HL7 v2.3 down to a PDF, an OCR'd fax transcript, a SOAP note, and a plain SMS chat log: 73 pass, for forty-eight cents across all 100 calls. Every diagnosis code — exact SNOMED codes, pulled out of wildly different shapes — came back right. The failures clustered in three places: HL7 names coming back untouched, a few mis-translated gender codes, and a handful of hallucinated symptoms on records that should have reported none.
The fix was not more code — squishling has none to fix. It was the purpose: spelling out, in plain language, what a hand-written parser would have known by construction. HL7 writes names LAST^FIRST, often in all caps. A diagnosis is not a symptom — never infer one from a diagnosis name. An unrecognized gender code, including HL7's bare O/U/A/N letters, maps to "unknown," never a guess. Same class, same schema, same no-call-method trick — only the purpose changed, and the pass rate jumped to 95 of 100, for 51 cents. A prompt is code too, and it benefits from the same iteration a case statement would.
What if we ask twice?
During prep for this talk I filed an issue for a harness squishling didn't have yet — sample two different models and have a third judge disagreements — and squishling's maintainer shipped it the same day. Wiring it up on the pinned commit, though, surfaced an honest gap: the harness samples the escalation's first step twice, concurrently, rather than sampling two different steps. So a second model declared in the escalation sits unused — both samples come from the same model regardless.
Run it anyway: 95 of 100, the exact same five failures as a single attempt, at double the cost — $1.02 — and zero judge calls across all 100 fixtures. At a low temperature, that one model doesn't just usually agree with itself; it agreed on every fixture, including the five it got wrong, every single time. These are systematic misreadings, not sampling noise — a harness built to catch disagreement never gets a chance to fire unless the samples actually come from different models.
The elastic slider, and the router
A quick analysis of the data shows 63% of requests fit the standard canonical FHIR shape. That's the elasticity, drawn as a slider between fully flexible (the model handles everything, at a higher cost per request) and fully efficient (a hardened parser, cost near zero) — and the position on that slider is a decision you keep revisiting per integration, not a bet you place once at design time.
So: code the standard shapes, squish the rest. One branch routes the 63% that already match canonical FHIR straight to a fast, free, hand-written parser; the other 37% fall through to squishling. Same 100 fixtures, same 95 pass rate, 12 cents — a 76% discount over squishing everything, with the same five honest misses, because the pinned harness still samples one model twice. The payoff: integrations work on day one, cost per request stays low, and total LLM cost lands well below what the equivalent maintenance would have cost — without a rewrite, a re-architecture, or a bet placed up front.
It runs backward too. If you're an enterprise drowning under a thousand legacy parsers, find the ones sitting at the tail of your usage curve, squish them instead, and delete the code.
It was never just about health data
"Yeah, cool, but we don't need to parse health data." Fair — but do you
integrate with services? Every vendor integration has this exact same
shape. When a vendor webhook payload breaks your hand-written parser, rescue
the parse error and squish! it — same trick as the FHIR router,
just triggered by an exception instead of a format check, straight out of
squishling's own README.
"We don't integrate with services or APIs either." Then are you hiring? And
more seriously: do you ever have production errors? Because the same trick
catches those too — even a plain bug in your own code, a dig
call written for one shape that blows up on another, can rescue into a
squish! and come back with the right answer pulled straight out
of the stack trace. Any rescued exception, including ones from your own code,
can be the trigger.
What's next for squishling — and the ask
The roadmap is short: Bring Your Own Harness, turning what was a same-day PR for me into a documented plugin point for everyone else; framework-specific defaults for Rails, Sinatra, and friends instead of every app hand-rolling the same configure block; support for non-RubyLLM clients, since today's routing and harness logic is built directly on RubyLLM; and output-format annotations, so a schema can carry units and examples instead of leaving it all to prose in the purpose. The last item is only half a joke — this entire demo leaned on me personally generating mascot art on request, and that does not scale past one deck.
If any of that sounds useful: squish the star button on github.com/Coolhand-Labs/squishling, share it, and leave an issue. I'd genuinely like to know where this pattern is breaking for you — that's the actual reason I'm up here.
Related reading: Beginners Write Software with AI. Pros Use It to Make Software Elastic. in The Everything Engineer.