Evals & Production Reliability
How you prove an AI workflow actually works
Why evals are the differentiator
After this you can explain why “how do you know it works?” is the central FDE question and the one most candidates fumble.
Every FDE loop bends toward one question: how do you know it works? For most roles that's a follow-up. For this one it's the whole interview wearing a different hat.
A Forward Deployed Engineer at Cursor ships a code-modifying agent into a customer's repo and then has to convince a skeptical senior engineer to trust it on their production code. That engineer has seen confident demos collapse the moment they touch a real codebase. The only thing that moves them is a number they can check.
Hand-waving here is the most common way strong candidates lose offers. They build something that works in the demo, present it well and then go quiet when asked to prove the success rate. The interviewer doesn't hear modesty. They hear a person who can't tell a working system from a lucky one.
Evals are what turn a flaky demo into a system a senior engineer will run unattended. Without them you have an anecdote. With them you have a contract: this workflow does X correctly on Y% of these tasks and we'll know within minutes if that drops.
Evals make “done” definablethe operational version of a success metric
An FDE who can't define success criteria can't define done. "The migration script works" is not a finish line. "The script converts 412 of our 430 components, the other 18 are flagged for manual review and zero introduce a type error" is a finish line a customer can sign off on.
“It looks right when I run it.”
Quality is a vibe that drifts silently.
Every regression is discovered by the customer.
You can't say when you're finished.
“93% on the golden set, gated at 90.”
A regression trips a gate before rollout.
You discover failures, not the customer.
Done is a measured threshold.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
The right column is what a senior customer engineer will run unattended.
Cursor deploys an AI coding agent, so eval rigor isn't a hygiene topic bolted onto the role. It's the literal deliverable. When the product you ship is a thing that edits code, "how do you measure that it edited the code correctly" is the job description compressed into one sentence.
Volunteer the metric before they ask. After you describe any workflow, say what you'd measure and what threshold would block a rollout. It flips you from someone being tested on rigor to someone who brought it. Across the technical screen, the system-design round and the paid onsite, this single habit reads as senior.
Don't claim a number you can't reconstruct. If you say “95% pass rate,” be ready for “on how many tasks, graded how and what were the failures?” A vague high number is worse than an honest “I measured 40 tasks, 37 passed, here are the 3 that didn't and why.”
“I don't ship a workflow I can't measure. Before I build, I write down the success metric and a small eval set that operationalizes it. That's what lets me tell you, with a number, whether this is ready for your team or not.”
Takeaway. “How do you know it works?” is the FDE filter. Lead with the metric and the threshold that would block a rollout - never ship a workflow you can't measure.
Self-check
QIn a Cursor FDE interview, why is hand-waving on “how do you know it works?” a uniquely costly mistake?
Designing an eval set for code-modifying AI
After this you can build a golden eval set for a code workflow, with graders and a pass/fail gate.
An eval set is a list of tasks where you already know the right answer, plus a grader that can decide automatically whether the agent got there. For code, you're lucky: the compiler and the test suite are graders that don't get tired.
Start by collecting golden tasks - concrete inputs with a known-good outcome. For a refactor workflow a golden task might be a real file and the assertion "after the edit this still compiles, the existing tests pass and the public behavior is unchanged." You don't need a thousand of these. You need fifteen that cover the cases you actually care about.
- 1Pull real examples. Mine the customer's repo and git history for representative tasks, not toy snippets. Adversarial cases live in their codebase already.
- 2Pin the known-good outcome. Write down what “correct” means per task: compiles, tests pass, type-checks, behavior preserved, no new lint errors.
- 3Pick a grader per task. Choose the cheapest grader that actually catches the failure you care about.
- 4Set a gate. Decide the pass rate below which a rollout is blocked and stick to it under deadline pressure.
- 5Run it on every change. If it's slow or expensive, you'll stop running it and an eval you don't run is a comment.
Choosing graders - and knowing how each one liesevery grader has a failure mode
- Grader
- Exact match
- Good for
- Deterministic output, format checks, small string transforms
- How it fails you
- Brittle - a valid but differently-formatted answer fails; useless for open-ended edits
- Grader
- Test-pass
- Good for
- Behavior preservation on refactors and migrations
- How it fails you
- Only as good as coverage; green tests on untested code prove little
- Grader
- Type-check / compile
- Good for
- Catching structurally broken edits cheaply and fast
- How it fails you
- Compiles ≠ correct; logic bugs sail straight through
- Grader
- LLM-as-judge
- Good for
- Subjective quality: is this explanation clear, is this the idiomatic fix
- How it fails you
- Non-deterministic, can be gamed, drifts with the judge model; needs its own calibration
| Grader | Good for | How it fails you |
|---|---|---|
| Exact match | Deterministic output, format checks, small string transforms | Brittle - a valid but differently-formatted answer fails; useless for open-ended edits |
| Test-pass | Behavior preservation on refactors and migrations | Only as good as coverage; green tests on untested code prove little |
| Type-check / compile | Catching structurally broken edits cheaply and fast | Compiles ≠ correct; logic bugs sail straight through |
| LLM-as-judge | Subjective quality: is this explanation clear, is this the idiomatic fix | Non-deterministic, can be gamed, drifts with the judge model; needs its own calibration |
Layer graders: cheap deterministic checks first, an LLM judge only for what they can't see.
Run compile, then type-check, then tests, then an LLM judge for the rest. The deterministic layers are fast, free and trustworthy, so they filter most failures before you spend a model call. Reserve the judge for taste questions a unit test can't express and calibrate it against a handful of human-labeled examples first.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
Cheap, deterministic checks first; the model call last; the gate decides rollout.
golden = {
"id": "refactor-auth-001",
"input_file": "src/auth/session.py",
"instruction": "Replace the deprecated session API with the v2 client.",
"expect": {"compiles": True, "tests_pass": True, "behavior": "preserved"},
}
def grade(task, agent_output):
if not type_checks(agent_output): # cheap, deterministic
return Fail("type_error")
if not run_tests(task.tests): # behavior preservation
return Fail("test_regression")
verdict = llm_judge(task, agent_output) # only for taste
return verdict # Pass / Fail + reasonCover the cases that bite, not just the happy path
A demo passes on the file you picked because it looks clean. Production fails on the file with a circular import, the generated code, the 2,000-line module nobody wants to touch. Seed your eval set with the ugly ones on purpose.
- Adversarial inputs: malformed files, partial edits, ambiguous instructions, files that shouldn't be touched at all.
- Edge cases from the real repo: generated code, vendored deps, the one file with no test coverage.
- Negative tests: the agent should sometimes refuse or escalate and the eval should reward that.
An eval set that only contains tasks the agent already passes is theater. It produces a comforting 100% and predicts nothing. Deliberately include tasks you expect to fail today - the gap is your roadmap and watching it close is the proof of progress.
What this looks like inside Cursora named benchmark and a DIY review grader
This isn’t abstract. Cursor built its own benchmark, CursorBenchCursor's internal benchmark that scores models on both task performance and token efficiency, not accuracy alone., that scores models on performance and token efficiency together - reflecting the shift toward price/efficiency, not raw capability alone. On it, GPT 5.4 has the best performance while sitting mid-range on token spend; lower-effort GPT 5.3 Codex variants use far fewer tokens at slightly lower performance. The headline efficiency metric is tokens per $100 spent. That dual-axis is the same instinct you want in your own eval set: don’t just ask did it pass, ask what it cost to pass.
You can codify the LLM-judge layer as a Cursor skill: “You are an expert code reviewer. Audit this diff for X, Y, Z.” - a mini BugBot you own.
Skill creation asks where to store it (project vs user), how to resolve conflicts, and when it auto-runs: after any feature work touching components, only when explicitly asked, or whenever tests / adjacent source files change. Wiring it to auto-run on commit turns your grader into a gate that fires on every change instead of one you have to remember to run.
Takeaway. Golden tasks pin “correct,” layered graders (compile → type-check → tests → LLM judge) decide it cheaply and a pass/fail gate blocks rollouts when quality regresses.
Self-check
QWhy layer a compile check and a test-pass check before an LLM-as-judge grader, instead of just using the judge?
Tracing, metrics and observability
After this you can instrument a model-backed workflow so you can debug it in production.
You can't debug what you can't see. The first thing to build after the workflow works once is the trace that records what it actually did.
A trace captures one run end to end: the inputs and context that went in, the model's output, the tool calls it made, plus tokens, latency and cost. When a customer says "it did something weird on this PR," the trace is the difference between reproducing it in two minutes and guessing for an afternoon.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
Each layer rests on the one below - if the trace isn’t captured, nothing above it exists.
What to capture on every model call
- Inputs + context
- The exact prompt, retrieved files and rules - the agent's full view of the world
- Output
- Raw model response before any post-processing
- Tool calls
- Which tools fired, with what arguments and what they returned
- Tokens
- Prompt and completion counts, the input to cost and the signal for context bloat
- Latency
- Wall-clock per call and per task, broken out by step
- Cost
- Dollars per task, derived from tokens and model
- Outcome
- The grader verdict for this run, linked back to the trace
If a field isn't captured at runtime, it doesn't exist when you need to debug.
The metrics that actually tell you somethingaggregate the traces
- Metric
- Success rate
- What it answers
- Is the workflow still doing its job?
- When it should alarm
- Drops below the gate threshold over a rolling window
- Metric
- p50 / p95 latency
- What it answers
- Is it fast enough to use, including the slow tail?
- When it should alarm
- p95 crosses the point where users abandon it
- Metric
- Cost per task
- What it answers
- Is the unit economics still sane?
- When it should alarm
- Creeps up - usually context bloat or extra retries
- Metric
- Error / retry rate
- What it answers
- Is the plumbing healthy underneath the model?
- When it should alarm
- Spikes from timeouts, rate limits or tool failures
| Metric | What it answers | When it should alarm |
|---|---|---|
| Success rate | Is the workflow still doing its job? | Drops below the gate threshold over a rolling window |
| p50 / p95 latency | Is it fast enough to use, including the slow tail? | p95 crosses the point where users abandon it |
| Cost per task | Is the unit economics still sane? | Creeps up - usually context bloat or extra retries |
| Error / retry rate | Is the plumbing healthy underneath the model? | Spikes from timeouts, rate limits or tool failures |
Track p95, not just the average. The average hides the runs that make people give up.
The scary failure isn't a crash, it's a slow slide. A provider ships a new model snapshot, your context grows as the repo grows and success rate erodes a point a week. Nothing errors. The way you catch it is by running the eval set on a schedule, not just at build time and alerting when the rolling success rate or cost per task moves off its baseline.
Surface failures to a human who can act
A metric that drops is a notification. A useful notification carries the trace link, the failing input, the grader's reason and a guess at the category. The on-call engineer should be able to click once and see exactly what the agent saw.
- Link every alert to the specific trace and the prompt/context that produced it, so root cause is one click away.
- Tag failures by category at capture time (bad context, wrong tool, timeout) so patterns aggregate instead of scattering.
- Keep enough context to reproduce without the customer's help - assume they won't or can't re-run it for you.
In the system-design round, when asked to design an AI workflow, draw the observability layer unprompted. Say: every call emits a trace, traces roll up into success rate / p95 / cost-per-task and the eval set runs on a schedule against the same traces to catch drift. That one paragraph signals you've operated these systems, not just built them.
Takeaway. Trace every call (inputs, output, tools, tokens, latency, cost), roll traces into success rate / p95 / cost-per-task and run evals on a schedule to catch silent drift - with every alert linked back to the trace that caused it.
Self-check
QWhy is tracking p95 latency more important than tracking average latency for a model-backed workflow?
Rollouts, guardrails and incident response
After this you can ship and operate an AI workflow safely, assuming something will eventually go wrong.
You're putting an agent that edits code in front of a team that didn't write it. Roll it out the way you'd roll out a risky migration: quietly first, then a little, then everyone, with a metric gating each step and a way to stop.
Progressive rolloutearn trust one gate at a time
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
A metric gates each step - every expansion is a decision backed by numbers, not a date.
- 1Shadow mode. The agent runs and produces output, but nothing it does reaches production - you compare its proposals against what humans actually did. Pure signal, zero risk.
- 2Small cohort. Turn it on for a couple of willing engineers or a low-stakes repo. Watch success rate, cost and their reactions.
- 3Wider rollout. Expand only when the cohort's metrics hold above the gate. Each expansion is a decision backed by numbers, not a calendar date.
- 4Full deployment. Default-on for the team, with the eval suite running on a schedule and alerts wired up.
Shadow mode is the most impactful step and the one people skip. Running the agent on real inputs while discarding its output lets you measure real-world success rate before a single user is exposed. For a code-modifying agent, it's how you find out the demo number was optimistic without anyone paying for it.
Guardrails - assume the model will sometimes be wrong
Reject outputs that don't compile, type-check or match the expected shape before they ever land.
Constrain which files, paths and actions the agent may touch. No edits to migrations or secrets.
High-risk actions stop for review. The agent proposes, a senior engineer approves, until trust is earned.
Guardrails aren't a lack of confidence in the model. They're the reason a customer lets the model near anything that matters.
The strongest guardrail isn’t a prompt asking the model to behave - it’s real code. Cursor hooks run shell scripts at ~a dozen lifecycle trigger points: prompt submit, agent reads a file, agent writes a file, MCPModel Context Protocol. A standard that lets an AI agent pull in context from outside the repo, like Jira tickets or internal docs. tool call, terminal tool call, file deletion, before MCP access and the stop hook when a run finishes.
Canonical use: scan every prompt for PIIPersonally Identifiable Information. Data that can identify a person (names, emails, SSNs); regulated and sensitive. / API keys / secrets and, if found, halt it before it reaches the model and return a custom error. A cloud agent can’t do anything that violates the hook. Build once, share across the team and agent types - hooks work in cloud agents too. (A before-prompt hook can also log which rules are actually in context, which is how you audit rule compliance.)
Rollback and kill switches
- A kill switch that disables the workflow instantly, owned by someone who can pull it at 2am without a deploy.
- A rollback path for whatever the agent changed - easy when edits go through PRs, which is one more reason to route them that way.
- A documented “what we do when it misbehaves” that the customer's team has actually read, not just received.
Incident response for AIa bad output is an incident
- 1Triage. Is this one bad PR or is the success rate falling for everyone? Check the dashboard before touching anything.
- 2Mitigate. Pull the kill switch or narrow the guardrails to stop the bleeding. Mitigation first, root cause second.
- 3Diagnose. Open the trace, find the variable that changed - model snapshot, context, a prompt edit, an upstream timeout.
- 4Fix the root cause. Change the prompt, the context, the guardrail or the model, whichever the trace points at.
- 5Add an eval. Encode the failing case as a golden task so the gate would have caught it. The incident is only closed when it can't silently recur.
The deliverable of an incident isn't the hotfix, it's the new eval. If the same failure can happen again without tripping a gate, you mitigated a symptom and learned nothing durable. “We add a regression eval for every incident” is a sentence that makes senior engineers exhale.
Incident response, the other direction: agents as respondersCursor’s own dogfood story
Cursor’s automations feature was born from exactly this problem - it started internally so engineers wouldn’t wake at 3am to comb logs. On a PagerDuty trigger, a cloud agent uses the Datadog MCPModel Context Protocol. A standard that lets an AI agent pull in context from outside the repo, like Jira tickets or internal docs. to fetch logs, investigate, find root cause and - if severity is low - implement a fix. The on-call engineer opens their laptop to a root cause and a PR ready to merge, saving roughly 30 minutes of response time, which matters when an outage can cost millions per minute.
Automations never merge directly - they always produce a human-reviewed PR, and the person clicking merge is the accountable one.
The one genuinely new attack vector is who can trigger them: an automation anyone in a Slack channel can fire is a path to your code - don’t wire people who shouldn’t touch the repo to a code-touching automation.
Scope tools to least privilege (e.g. a read-only SQL/Databricks connection so the agent can fetch data but never drop a prod table) and pair them with pre-action hooks. Note: cloud agents respect account privacy mode but not legacy privacy mode, since the VM must store code for the run.
Don't promise zero failures - it's both false and a bad sign to a sharp interviewer. Promise that failures are bounded, caught fast and turned into evals. Truth-seeking means naming the risk and showing the machinery that contains it.
Takeaway. Roll out shadow → cohort → full with metrics gating each step, guard with validation / allow-lists / human-in-the-loop, keep a kill switch and close every incident by adding the eval that would have caught it.
Self-check
QAfter mitigating a bad-output incident, what single step actually closes the loop and why?
Debugging real-world model failures
After this you can diagnose why a model or agent failed in production and tell it as an interview story.
When an agent does something wrong, the amateur reaction is to rewrite the prompt and hope. The professional reaction is to find out which of five things actually broke.
A failure taxonomyname the category before you reach for a fix
- Category
- Bad context
- What it looks like
- Right model, wrong information - it edited the wrong file or missed a dependency
- Where the fix usually lives
- Retrieval / context construction
- Category
- Bad prompt
- What it looks like
- Instructions were ambiguous or under-specified for this case
- Where the fix usually lives
- Prompt wording, examples, output schema
- Category
- Wrong tool
- What it looks like
- It called the wrong tool or used the right one incorrectly
- Where the fix usually lives
- Tool definitions, descriptions, guardrails
- Category
- Model limitation
- What it looks like
- The task is genuinely beyond this model's capability
- Where the fix usually lives
- Stronger model, task decomposition, human-in-the-loop
- Category
- Infra / timeout
- What it looks like
- Rate limit, timeout, truncated output, a flaky upstream
- Where the fix usually lives
- Retries, backpressure, batching, plumbing
| Category | What it looks like | Where the fix usually lives |
|---|---|---|
| Bad context | Right model, wrong information - it edited the wrong file or missed a dependency | Retrieval / context construction |
| Bad prompt | Instructions were ambiguous or under-specified for this case | Prompt wording, examples, output schema |
| Wrong tool | It called the wrong tool or used the right one incorrectly | Tool definitions, descriptions, guardrails |
| Model limitation | The task is genuinely beyond this model's capability | Stronger model, task decomposition, human-in-the-loop |
| Infra / timeout | Rate limit, timeout, truncated output, a flaky upstream | Retries, backpressure, batching, plumbing |
Most “the model is dumb” complaints turn out to be bad context, not model limitation.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
Ranked by how often each category is the real culprit - check the top one first.
The debugging method
- 1Reproduce. Pin the exact input from the trace. A bug you can't reproduce, you can't fix or verify.
- 2Inspect the trace. Read what the agent actually saw - the context, the tool calls, the raw output - instead of guessing.
- 3Isolate the variable. Change one thing at a time. Swap the model, then the context, then the prompt and watch which move fixes it.
- 4Fix the root cause. Apply the change in the layer the taxonomy points at, not the first layer that's convenient.
- 5Add an eval. Turn the reproduction into a golden task so the fix is permanent and measured.
Before you spend a day on a fix, ask whether it's one bad roll or a real pattern. Re-run the same input several times: if it fails intermittently, you have non-determinism to bound with validation and retries. If it fails every time, you have a systemic bug to fix at the root. Treating a flake like a regression wastes a day; treating a regression like a flake ships a broken workflow.
Which knob to turnprompt vs. context vs. model vs. guardrails
- Change the prompt
- when the instruction was ambiguous or the output format was wrong
- Change the context
- when the model had the wrong or missing information - usually the real culprit
- Change the model
- when the task genuinely exceeds capability, after ruling out context and prompt
- Change the guardrails
- when the output was unsafe or out of bounds and needs hard limits, not better odds
Reach for context and prompt first; they're cheaper and fix more failures than a model swap.
Tell it as a storythe interview payoff
The behavioral and onsite rounds will ask for a model failure you debugged. The structure that lands is the same every time and it's the structure of the work itself.
“The migration agent kept dropping a decorator on about 8% of files. I pulled a failing case from the trace and saw the decorator lived in an import the agent never retrieved - bad context, not a model limitation. I fixed the retrieval to include the import graph, re-ran the eval set and the failure rate went from 8% to zero. Then I added those files as golden tasks so it can't come back.”
End every failure story on the metric that proved the fix. “It feels better now” is what loses the room; “8% to zero on the eval set and a regression test so it stays there” is what wins it. The metric is the whole point - it's the same rigor the role is screening for, told in past tense.
Don't tell a “we” story here. The behavioral round screens for extreme ownership, so use “I diagnosed, I fixed, I proved it.” Credit the team elsewhere, but own the failure and the fix in this answer.
Takeaway. Categorize the failure (bad context / prompt / tool / model / infra), reproduce → inspect the trace → isolate one variable → fix the root → add an eval and tell it with “I” and the metric that proved the fix.
Self-check
QHow do you tell a one-off flake apart from a systemic regression and why does it matter?