ConceptIntermediateQuality, Cost & Token Economics / Eval design for product teams / #13

How often should evals run in a CI pipeline versus on demand?

Run the cheap slice on every pull request and the expensive suite on its own clock, because the moment the expensive one becomes the gate, people stop waiting for it.

The direct answer
Split eval cadence into two speeds, never one. A small, fast slice of the eval suite runs in CI on every pull request and blocks the merge if it fails. The full, expensive suite runs on a fixed nightly schedule against the main branch, and again on demand, in full, before any release cut or any change to the underlying model or the prompt template. Never let the expensive suite double as the CI gate itself: once it is slow enough that people start merging around it, you have lost the whole gate, not just some coverage.
Do this, in order
  1. Split eval cadence into two speeds, never one.Why: a single slow gate does not stay a gate. People learn to route around it, and then nothing checks anything.
  2. Put a small, fast, diff-weighted slice in CI, blocking every merge.Why: it has to finish fast enough that nobody is ever tempted to skip it.
  3. Run the full, expensive suite on a fixed nightly schedule, and recut the score by category, not blended.Why: a blended pass rate can barely move while one category quietly falls apart underneath it.
  4. Require the full suite on demand before any model swap, prompt rewrite, or release cut.Why: those changes touch everything at once, which is exactly what a slice scoped to one small diff is built to miss.
  5. Set a real number that would flip the pick, and check it monthly.Why: without a stated cutoff, fast and cheap quietly becomes the whole policy by default, everywhere, forever.
  6. Leave the full, blocking check in place wherever a miss cannot wait even a day.Why: the split only pays off where the cheap side of the trade is genuinely cheap. Some checks never get that cheap side.

How to answer this, stage by stage

Nobody is grading whether you know the word "nightly." They are grading whether you can say, out loud, which mistake you would let sit for a day and which one you never would.

1
Scope it to one real product before answering in general terms
Say it like this
"Let me ground this in one tool. Whittle is an in-editor code completion plugin built by Quillhouse. It sits inside the IDE and finishes a line, or a whole function, as a developer types. Senna Vysotsky runs quality on it."
Why this works
A general "how often should evals run" answer turns into a policy memo fast. One real product forces an actual number, not a vibe.
2
Say your structure out loud before diving in
Say it like this
"I'm going to split this into two speeds, and then tell you the one rule for when the slow speed has to run before merge instead of after it. That second part is the part most teams get wrong."
Why this works
Signals you have a shape in mind, not just an opinion you're building live.
3
Reframe the question before answering it
Say it like this
"This isn't really a calendar question, how often, every commit or once a week. It's asking which kind of wrong answer I'm willing to let sit for a day, and which kind I'm not willing to let sit at all."
Why this works
Stops you from answering with a schedule and nothing underneath it.
4
Give the one decision, plainly
Say it like this
"A small, fast slice runs in CI, blocks every merge, and finishes in under two minutes. The full suite runs every night on its own, recut by language, not blended. And the full suite also has to run on demand, in full, any time the underlying model changes, because that's the one kind of change the fast slice is built to miss."
Why this works
This is the actual answer, said in one breath, with the exception that matters folded in.
5
Prove it with the failure, cut to four sentences
Say it like this
"Here's what happens without the split. Whittle used to run the full suite on every single pull request. Once the team was shipping thirty plus PRs a day, that queue backed up for hours, so engineers started merging on an old, stale green check instead of waiting. A model upgrade quietly broke how Whittle finished Rust code, and it sat live for four days before a new hire asked why the eval suite hadn't caught it."
Why this works
Shows the real cost of one slow gate, not just the mechanism of a fast one.
6
Say what you would measure going forward
Say it like this
"I'd track the nightly pass rate by language, not company wide, and I'd set a real cutoff: if more than one real regression in ten is still slipping past the fast slice over a month, the slice needs to grow, or the on-demand rule needs to cover more kinds of change."
Why this works
Shows you're thinking past this one incident, into the thing that catches the next one.
7
Say what you'd leave alone
Say it like this
"I wouldn't split the secret-scan check, the one that looks for hardcoded API keys in a suggested completion. That check is already small and fast on its own, and a miss there can't wait a day. The split is a fix for checks that are big and slow, not a policy I'd apply everywhere by default."
Why this works
Shows judgment instead of one rule stamped over every check in the pipeline.
8
Close on the decision, not the story
Say it like this
"So: a fast slice gates every merge, the full suite watches the main branch every night, and any change to the model itself always earns the full suite before it merges, no exceptions."
Why this works
Ending on the rule, not the anecdote, is what makes it sound like a method you'd reuse on your very next PR.

Let's learn

Every pull request to Whittle used to sit in a queue for most of an afternoon before it was even allowed to merge. That was the whole gate: one eval suite, six thousand held-out coding tasks across twelve languages, each one actually run against real unit tests, forty two minutes, about thirty five dollars in compute, every single time.

When Quillhouse shipped a handful of pull requests a week, forty two minutes was nothing, closer to a coffee break than a cost. As Whittle grew past forty thousand developers using it inside their IDE every day, the team shipping it grew too, and pull requests climbed to more than thirty a day. The queue for that one gate stretched past three hours on a busy afternoon.

Knowledge spark: what's a held-out task? A coding problem the model never saw while it was being trained or tuned. It's kept aside, just for grading. That's what makes passing it mean something. Test it on something it already memorized and the score tells you nothing real.

Nobody decided to stop trusting the gate. It happened in three small steps. First, engineers started batching several small changes into one bigger pull request, to make the wait worth it. Then, on a genuinely small change, someone would merge against an old passing check from earlier that day instead of waiting on a fresh one, "it's basically the same diff." Then that became the normal move for anything that looked minor, and nobody was really watching which pull requests had a fresh check behind them anymore.

Rust task pass rate vs company wide pass rate, the four days nobody noticed
100% 40% model swap ships new hire asks Day 0 Day 2 Day 4
Company wide pass rate, blendedRust task pass rate
The blended line moves from about seventy one percent to sixty nine percent, barely a wobble. The Rust line falls from seventy four percent to forty one percent the day the model swap ships, and stays there for four days.

A new model version shipped, meant to make completions faster across the board. Company wide, the blended pass rate barely moved, seventy one percent down to sixty nine. But the model had picked up a bad habit in Rust: it started suggesting .unwrap() inside functions that were supposed to hand an error back to the caller, code that compiles fine and then panics the first time something actually goes wrong. Rust's own held-out pass rate fell from seventy four percent to forty one percent, and the blended number never showed it, because Rust was a small slice of everything Whittle suggested that week.

The gate did not fail to run. It ran on a version of the code from before the change.

Senna Vysotsky found this out because a new hire asked her a plain question: didn't the eval suite catch this? The honest answer was that the pull request carrying the model swap had queued for over three hours, and the engineer shipping it had re-merged against an earlier green check rather than wait for a fresh run against the new model. The gate existed. It had simply stopped being the thing that actually ran before code shipped.

The decision that mattered Whittle used one eval suite for two different jobs: deciding whether a single pull request was safe to merge, and deciding whether the whole product was ready to ship to every user. Those aren't the same question, and the second one is much slower to answer honestly than the first one needs to be. Collapsing them into one gate made sense when PRs were rare. It stopped making sense once they weren't.

By the time the new hire's question surfaced it, roughly eighteen hundred real Rust completions had already been accepted by working developers with the panic-prone pattern inside them. At its worst, an autocomplete tool that quietly hands out code that crashes in production is worse than no autocomplete at all, because a developer trusts a suggestion more than a blank line.

What I'd leave alone: the check that scans every suggested completion for a hardcoded secret, an API key or a password pasted into code. That one stays full and blocking on every single pull request, because it's already small and fast on its own, checking a pattern in text costs nothing like running six thousand real coding tasks. The split is a fix for a check that is both big and slow. Not every check is.

The lesson: a fast, cheap eval slice is not a smaller version of safety, it's a different tool for a different job. The mistake wasn't building a fast slice. It was letting the one slow, thorough suite quietly become the only thing standing between a bad model swap and forty thousand developers, on a clock too slow for anyone to actually wait for.

Now here is the same thing as a story

Read the long version below when you want to feel why a three hour queue, not a bad model, was the actual failure. The short version is above.

Senna Vysotsky joined Quillhouse when Whittle shipped one pull request every couple of days, most of them from a team of six. She built the eval suite herself: six thousand tasks, real code, run against real tests, and she was proud that nothing merged without it. Forty two minutes felt like nothing next to the confidence it bought.

For most of a year, that held up fine. The team grew, Whittle got picked up by bigger companies, and pull requests climbed from a handful a week to more than thirty a day. The eval suite didn't get any faster. It just got queued behind more and more requests to run it.

Nobody announced a change in how people merged code. It crept in over a few sprints. An engineer with three small fixes started bundling them into one pull request instead of three, so the wait only happened once. Someone waiting on a queued run for a genuinely tiny typo fix noticed an earlier run from that morning had passed on nearly the same diff, and merged against that instead of waiting again. It felt harmless. It happened again the next week, on something slightly less tiny.

The model swap that caused the actual damage looked, on paper, like a routine upgrade. It shipped on a Wednesday. The pull request carrying it queued for three hours and twenty minutes. The engineer shipping it, watching the clock, merged against a check that had passed against the previous day's code, reasoning the model change itself was "just a config flip," and moved on to the next thing on their plate.

Nobody skipped the gate on purpose. They'd simply stopped believing the gate would ever run before they needed to move on.

Four days passed. A new hire, three weeks into the job, was reviewing Whittle's Rust suggestions for a training doc and noticed something odd: a suggested completion inside a function that clearly returned a Result was ending in .unwrap(), code that would panic instead of handing the error back up. She asked Senna, plainly, whether the eval suite had caught this. It was a fair question. Senna didn't have a fast answer, and that bothered her more than the bug did.

She pulled the merge history for the model-swap pull request and found the stale check. Then she pulled Whittle's own weekly numbers and split them by language instead of trusting the blended line. Rust's held-out pass rate had fallen from seventy four percent to forty one percent, the day the model shipped, and stayed there. The blended number, sitting at sixty nine, had never once dipped below seventy on the dashboard anyone actually looked at.

The decision Senna would take back went back to the day she built the suite. She'd made one eval suite answer two different questions: is this pull request safe to merge, and is Whittle, as a whole, ready to ship to everyone using it. Those questions don't need the same amount of certainty on the same clock. Bundling them made total sense when a PR came along every couple of days. It quietly became the whole problem once PRs came every twenty minutes.

Run the same Wednesday again with the new design. A small, three-hundred-task slice, weighted toward Rust since that's what the model-swap diff touched, runs in under two minutes and blocks the merge either way. But this pull request carries a model version change, so policy requires the full six-thousand-task suite, in full, before it's allowed to merge at all, no stale-check shortcut available. The full run finishes in forty two minutes, flags Rust's pass rate falling to forty one percent, and the merge is blocked before a single developer's IDE ever offers that panic-prone suggestion.

One design trusted a single, slow gate to still mean something even after the team outgrew the wait it demanded. The other design keeps the fast promise fast, and reserves the slow, thorough one for exactly the changes that need it.

What I'd tell myself, back on the day I built that first suite: the moment a check gets slow enough that people start finding a way around it, they will find one, quietly, without ever deciding to cut a corner on purpose. That's not a discipline problem. That's a design problem, and it was mine.

PICK, said out loud before the reasons

Four letters, not five. PICK commits first, then shows you exactly why one kind of miss is cheap and the other one is not.

PPosition. Your pick, before any reasoning.
A fast slice gates every pull request. The full suite runs nightly and, separately, on demand before any release or model swap. Never the full suite as the everyday gate.
Say the pick first. "It depends" is not an answer an interviewer can push on.
IImpact. Who feels each kind of error, and in what units.
A miss in the fast slice is felt by one developer, for at most a day, until the nightly run catches it, no user-facing incident, nothing shipped to a customer. A gate too slow to trust is felt by the whole engineering team, every day, in hours lost to a queue, and then in a habit that doesn't reverse on its own once it forms.
Naming both sides in real units is what separates a tradeoff from a hunch.
Hand sketched comparison panel titled which miss costs more. Left panel, a gauge icon labeled fast slice misses one, caption nightly run catches it in fourteen hours, nobody outside the team ever sees it. Right panel, a scale icon labeled full suite gates every PR, caption the wait breaks trust, engineers start merging around the check for good.
One miss is small and gets caught by morning. The other one is quiet, and it never gets caught, because the team stops trusting the thing meant to catch it.
CCost asymmetry. Which error is cheap, and which one changes behaviour.
A missed regression in the fast slice is cheap: visible within a day, absorbed by the nightly run, nobody outside the team ever sees it. A slow, blocking gate is expensive and hidden: it doesn't fail loudly, it just teaches engineers to route around it, and once that habit forms it doesn't reset itself even after the gate gets fast again. Optimise against the second one.
This is the actual judgment call. Everything else in the answer is just naming its edges clearly.
KKill criteria. What evidence would flip the pick.
If more than one real regression in ten, confirmed by the nightly run over a rolling month, turns out to have already been live for more than a day before anyone caught it, the fast slice is too thin and needs to grow. And any change to the model itself always requires the full suite before merge, never just the fast slice, because a new model touches every language at once, not just the files in one diff.
A pick with no way to flip it isn't a decision, it's a habit wearing a decision's clothes.

Three things worth stating plainly, since this is where the real judgment sits. The rejected alternative was throwing more compute at the single full-suite gate instead of splitting it, parallelising the six thousand tasks across dozens of runners to shrink forty two minutes down. It lost for two reasons: several of those tasks spin up a real language runtime and a real test framework per task, so the wall clock has a floor near fifteen to eighteen minutes even maxed out, still too slow against thirty plus pull requests a day, and running that much compute on every single pull request, forever, costs roughly forty times what running it once a night does. The AI specific failure worth naming by name is a silent, per-category model regression: a base model swap that looks fine on a blended score while one language quietly falls apart underneath it. The guardrail is recutting the nightly pass rate by language, never trusting the blended number alone, plus the on-demand rule that any model swap earns the full suite before merge, not after. And the trade being accepted on purpose, out loud: the fast slice will occasionally let a real regression through for up to a day, in exchange for a gate fast enough that nobody is ever tempted to route around it. That's a latency and completeness trade, taken deliberately, not a hope that both could be free.

And if you want to be sure it really works, try it somewhere else

Same four letters, an insurance claims tool this time, and the pick flips: here the full suite is the everyday gate, on purpose.

Backstitch is a tool adjusters use at Bellcastle Insurance to draft a first-pass summary of a claim, coverage, damages, and any exclusions that might apply, before a human adjuster signs off. Adalet Doyen runs quality on it.

Backstitch's own eval suite holds two thousand held-out claim scenarios, checked against real determinations a senior adjuster already made. Adalet's team ships maybe three or four pull requests a week, not thirty a day. So the full suite, all two thousand scenarios, runs in full, blocking, on every single pull request, twenty five minutes, every time. A fast, small smoke check exists too, but it's only for engineers iterating on a branch before they've even opened a pull request, to get quick feedback without waiting on the real gate.

The decision Adalet would not make the same way Adalet considered mirroring Whittle's split exactly, on the reasoning that a fast CI gate is simply the better practice. She rejected it. A missed regression here isn't a bug that sits quietly until a nightly run catches it. It can attach to a real payout decision the same day a claim is filed, and there's no cheap, absorbed version of that mistake to weigh against the wait.

Same method, opposite result: at Bellcastle, the cost asymmetry runs the other way. A slow gate barely costs anything, since pull requests are rare and nobody's ever stuck in a real queue behind one. A missed regression costs a lot, and it can't wait a day. So the full, expensive suite stays the everyday blocking check, and the fast check is only for private iteration, never the thing that actually gates a merge.

Swap the trigger and it still runs.
Speed: an interviewer caps you at ninety seconds. Say the split straight: "fast slice gates the merge, full suite watches the main branch overnight, and any model change always earns the full suite before it merges." That line alone answers the question.
Cost: there's no budget this quarter for a nightly server just to run the full suite on a schedule. At minimum, keep the full suite required on demand for the two riskiest moments, a model swap and a release cut, since those are exactly what a diff-scoped slice is built to miss.
The model got better, for real: say Whittle's completion model gets upgraded to a stronger version. A higher blended pass rate isn't proof every language improved together. Rerun the full suite, recut by language, before trusting the new blended number at all.

Where people run it wrong.
They treat "run evals in CI" as meaning the full, expensive suite has to clear on every commit, and either it becomes too slow to be a real gate, or the team quietly stops enforcing it and nobody notices for months.
They let a nightly cadence stand in for every kind of change, including the rare, high-risk ones, a model swap, a prompt rewrite, that genuinely need the full check before merge, not the next morning.
They watch only the blended pass rate, and never recut it by whatever a real regression is most likely hiding behind, language, claim type, region.

How to use it live. Say the split out loud before you give a number: "before I answer with a cadence, let me ask what a missed regression actually costs here, in hours, because that decides whether the slow check runs before the merge or after it." That buys a beat to think, and it shows the interviewer you're pricing the mistake, not reciting a schedule.

Flashcards (tap any card to flip it)

1 · THE FRAMEWORK
What framework is this, and what's its one job?
Tap to flip
ANSWER
PICK: commit to a position first, then show the asymmetry between the two kinds of mistake. Built for tradeoff questions, A or B, where "it depends" fails the question.
2 · THE PERSON
Who is this answer about?
Tap to flip
ANSWER
Senna Vysotsky, who runs quality on Whittle, an in-editor AI code completion plugin built by Quillhouse. She built the eval suite herself.
3 · THE HABIT
What did engineers start doing once the one eval gate got too slow?
Tap to flip
ANSWER
They started merging against an older, stale passing check instead of waiting hours for a fresh run, first on tiny changes, then on bigger ones.
4 · THE ASYMMETRY
What's the cost asymmetry PICK turns on here?
Tap to flip
ANSWER
A miss in the fast slice is cheap: caught within a day by the nightly run. A gate too slow to trust is expensive and hidden: it teaches the whole team to route around it, and that habit does not reverse on its own.
5 · THE OLD DECISION
What decision would Senna take back?
Tap to flip
ANSWER
Making one eval suite answer two different questions at once, safe to merge and ready to ship to everyone, on the same slow clock.
6 · THE NUMBER
Fill in the blank: the old CI gate ran all ___ tasks in ___ minutes. The new CI gate runs ___ tasks in about ___ seconds.
Tap to flip
ANSWER
6,000 tasks in 42 minutes, then 300 tasks in about 90 seconds. The full suite still runs, just on its own clock instead of blocking every merge.
7 · THE REPLAY
Same bad model swap, new design, what changes?
Tap to flip
ANSWER
A model swap always requires the full suite before merge. Rust's pass rate falling to 41 percent gets caught before merge, not four days later by a new hire's question.
8 · CROSS PRODUCT TRANSFER
Section 4 runs PICK again on a different product. Which product, and how does the pick flip?
Tap to flip
ANSWER
Backstitch, a claims-summary tool at Bellcastle Insurance. There, the full suite becomes the everyday CI gate itself, because a missed regression can't wait a day and pull requests are rare enough to afford the wait.

Check yourself Score: 0 / 0

Fill in the blank
1. The Rust completion regression sat live in production for ___ days before anyone noticed. Under the new design, the same kind of regression would get caught by the nightly run within about ___ hours.
Show hint
Look at the timeline on the chart in Section 1, from the model swap to the new hire's question.
Show answer
4 days, then about 14 hours. The nightly run is what closes that gap, once a model swap can no longer merge against a stale check.
True or false
2. True or false: because the CI smoke slice only checks 300 of the 6,000 held-out tasks, every pull request is now less safe than it was before the split.
  • True
  • False
Show hint
Think about what the old, single full-suite gate was actually catching once engineers started merging around it.
Show answer
False. The full 6,000-task suite still runs, every night and on demand for risky changes. The old single gate looked more thorough on paper, but a slow gate that people route around catches less in practice than a fast one everyone actually waits for.
Multiple choice
3. What is the real cost of making the full, expensive eval suite the CI merge gate on every single pull request?
  • A. It uses too much electricity to be worth running at all.
  • B. It's the correct choice, since more coverage on every merge is always safer.
  • C. It's slow enough that engineers start routing around it, so the team loses the gate entirely, not just some coverage.
  • D. It makes the underlying model itself less accurate over time.
Show hint
Think about what actually happened to the model-swap pull request while it queued for three hours.
Show answer
C. More coverage sounds safer, but only if people actually wait for it. Once the wait gets long enough, people stop waiting, and the "more thorough" gate ends up catching less than a fast one everyone respects.
Short answer, name the reversal
4. What old decision would Senna take back, and why did it make sense back when Whittle first shipped?
Show hint
Look at the key point box in Section 1 titled "The decision that mattered."
Show answer
Model answer: Making the same six-thousand-task, release-grade suite double as the CI merge gate for every pull request. It made sense early on, when Quillhouse shipped a handful of PRs a week and forty two minutes cost almost nothing. It stopped making sense once daily PR volume grew and that same forty two minutes turned into an all-day queue people quietly learned to work around.
Short answer, apply it yourself
5. Pick an AI feature you use yourself that ships behind some kind of automated check. Name one kind of change to it, a new model version, a prompt rewrite, a new data source, that you think should always require a full check before it ships, no matter how fast the everyday check is. Why that one?
Show hint
Think of a change that touches how the feature behaves everywhere at once, not just in the one place a normal update would touch.
Show answer
Model answer: A phone's photo-editing "remove object" AI tool. If the underlying model gets swapped, that should always get a full check across many photo types, faces, text, patterned backgrounds, before it ships, because a new model changes how it handles every kind of photo at once, not just whatever narrow case the day's update was meant to fix.
Multiple choice
6. At Bellcastle Insurance, Backstitch's full 2,000-scenario suite runs on every single pull request, blocking, even though it takes 25 minutes. Why does the same PICK method land on a different answer there than it did for Whittle?
  • A. Because Bellcastle has more engineers, so the wait matters less to any one person.
  • B. Because a missed regression there can attach to a real payout decision the same day, and pull requests are rare enough that a 25-minute wait never builds a real queue.
  • C. Because insurance companies are required by law to run slower software checks.
  • D. Because claims data can't be tested with an automated eval suite at all.
Show hint
Reread the C step, cost asymmetry, and ask which side is cheap at Bellcastle versus which side is cheap at Quillhouse.
Show answer
B. The asymmetry flips because the two real numbers flip: pull request volume is low enough that the wait costs almost nothing, and the cost of a miss is high enough, and immediate enough, that there's no cheap, absorbed version of that mistake to trade against it.
Before you close the answer
Why this works
Tests whether you'll actually price both kinds of mistake before setting a schedule, instead of reciting "run it in CI and also nightly" with nothing underneath it. Most candidates never say which changes are allowed to wait a day and which ones aren't.
Follow-up traps
"Isn't a ninety second slice basically for show, since it only checks 300 of 6,000 tasks?" Response: it was never meant to catch everything alone. It's meant to catch enough, fast enough, that people actually wait for it. The nightly run and the on-demand full run are what cover the rest, within a bounded number of hours, not never.

"What if the nightly run itself eventually gets too slow to trust, the same way the old gate did?" Response: same fix, one level up. That's exactly what the kill criteria is for, if a real regression is proven to sit past a day too often, tighten the cadence or widen what triggers an on-demand run, rather than let a slow safety net become a norm nobody rechecks either.
If pressed
The fast slice isn't a random sample of the 6,000 tasks. It's weighted toward whatever languages and files the actual pull request touches, so a diff that only changes Rust-related code runs a slice biased toward Rust tasks, not an even spread across all twelve languages the diff never came near.
From U2xAI Academy

From answering questions to owning outcomes.

A live workshop where you ship a working AI agent, defend a launch decision, and walk away with a portfolio recruiters can't wave off, not just more questions to study.

  • A live AI agent you actually shipped
  • A launch decision you can defend under pressure
  • An interview-ready portfolio, not more flashcards
Know more