Data, SQL & Measurement
Modeling the funnel and proving program impact
SQL for the funnel
After this you can answer GTM questions with SQL under interview conditions without producing inflated or double-counted numbers.
The data round at Cursor is where your systems claims get audited. You built an enrichment-and-routing pipeline in the practical round; now you have to prove it worked with a query you write live, while someone watches your reasoning.
Treat the SQL screen as the same job in a different costume. A GTM Engineer who can architect lead routing but can't count routed leads correctly hasn't closed the loop. The interviewer cares less about exotic syntax and more about whether your numbers survive a sanity check.
The grain trapthe single most common wrong answer
Most botched funnel queries come from one mistake: joining tables at different grains and then counting rows as if they were unique entities. A leads row is one person. An opportunities row is one deal. A usage_events row is one product action. Join them naively and a single lead with forty sessions becomes forty leads.
- Table
- leads
- Grain (one row =)
- one captured person
- Join key
- lead_id
- Count it with
- count(distinct lead_id)
- Table
- accounts
- Grain (one row =)
- one company
- Join key
- account_id
- Count it with
- count(distinct account_id)
- Table
- opportunities
- Grain (one row =)
- one deal
- Join key
- opp_id, fk account_id
- Count it with
- count(distinct opp_id)
- Table
- usage_events
- Grain (one row =)
- one product action
- Join key
- fk lead_id, ts
- Count it with
- count(*) or count(distinct lead_id)
| Table | Grain (one row =) | Join key | Count it with |
|---|---|---|---|
| leads | one captured person | lead_id | count(distinct lead_id) |
| accounts | one company | account_id | count(distinct account_id) |
| opportunities | one deal | opp_id, fk account_id | count(distinct opp_id) |
| usage_events | one product action | fk lead_id, ts | count(*) or count(distinct lead_id) |
Before you write a join, say the grain of each table out loud.
The fix is rarely cleverness. Aggregate the high-cardinality table to the grain you want before you join or wrap your count in distinct on the right key. When usage muddies a per-lead count, collapse usage_events to one row per lead in a CTE first, then join.
with first_qualified as (
select lead_id, min(qualified_at) as qualified_at
from lead_stage_history
where stage = 'qualified'
group by lead_id
)
select
l.segment,
count(distinct l.lead_id) as captured,
count(distinct fq.lead_id) as qualified,
round(
count(distinct fq.lead_id)::numeric
/ nullif(count(distinct l.lead_id), 0), 3) as qual_rate
from leads l
left join first_qualified fq using (lead_id)
where l.captured_at >= date '2026-01-01'
group by l.segment
order by captured desc;Window functions earn their keep on touch attribution and cohortsfirst-touch, last-touch, sequencing
First-touch and last-touch live in row_number() over a partition. Cohorts live in date_trunc plus a window count. Sequencing (did enrichment fire before routing?) lives in lag() and lead(). These four patterns answer most of what the funnel round will throw at you.
with touches as (
select
lead_id, channel, occurred_at,
row_number() over (
partition by lead_id order by occurred_at asc
) as touch_seq
from marketing_touches
)
select
date_trunc('month', l.captured_at) as cohort_month,
t.channel as first_touch_channel,
count(distinct l.lead_id) as leads
from leads l
join touches t on t.lead_id = l.lead_id and t.touch_seq = 1
group by 1, 2
order by 1, leads desc;Dirty data is the systems problem wearing a SQL hat
Dedup, nulls and inconsistent enums show up in queries the same way they show up in the pipeline you built. An interviewer who drops a duplicate lead_id or a null segment into the dataset is checking whether you notice before you report. Handle it explicitly rather than letting the database silently decide.
- Dedup at the entity grain with
row_number()keyed on the latestupdated_at, not a blinddistinctthat hides which row won. - Coalesce nulls into an explicit bucket (
coalesce(segment, 'unknown')) so they appear in the report instead of vanishing from agroup by. - Normalize enums (
lower(trim(stage))) when the source mixesQualified,qualifiedandQUALIFIED. - Guard every division with
nullif(denominator, 0)so an empty segment returns null, not a query error.
Run a row-count and a distinct-key count on every joined CTE. If they diverge when you expect them equal, you have a fan-out. Cross-check one segment total against a known figure the interviewer mentioned earlier. State the time window and the filter you applied as part of the answer, not as an afterthought.
Narrate the grain before you write the join: "leads is one row per person, usage is one row per event, so I'll collapse usage to per-lead in a CTE first." That sentence alone signals more seniority than a syntactically perfect query written silently.
Don't over-claim what a number means. "3,200 qualified leads" is a count; "3,200 qualified leads, defined as first-time hitting score 75, in Q1, deduped on lead_id" is an answer. The qualifier is where truth-seeking shows up under pressure.
Takeaway. Name the grain of every table before you join, count with distinct on the right key and state the window and filters as part of the number - not after it.
Self-check
QYou join leads to usage_events and report 9,400 "active leads," but the raw lead table only has 3,100 rows. What almost certainly happened and how do you fix it?
Defining program metrics
After this you can choose metrics that genuinely evaluate a growth program and define them tightly enough that they can't be gamed.
A growth program without a scoreboard is a hobby. Your job is to pick the two or three numbers that tell the team whether a startup program or partnership motion is working, then defend why those and not the flattering ones.
Cursor runs a developer-led motion that crosses self-serve into enterprise. The metric set has to respect that the same lead can convert through product usage, a partnership or a startup-program credit. Start by separating the one number you steer toward from the numbers that keep you honest.
- Metric role
- North-star
- What it answers
- Is the program creating durable value?
- Startup-program example
- Activated teams from the program still active at day 30
- Metric role
- Guardrail
- What it answers
- What must not break while we push the north-star?
- Startup-program example
- Credit cost per activated team; rep response SLA
- Metric role
- Leading
- What it answers
- What moves now, before the outcome lands?
- Startup-program example
- Enriched-and-qualified rate within 24h of capture
- Metric role
- Lagging
- What it answers
- What confirms real impact weeks later?
- Startup-program example
- Paid conversion and seat expansion at day 90
| Metric role | What it answers | Startup-program example |
|---|---|---|
| North-star | Is the program creating durable value? | Activated teams from the program still active at day 30 |
| Guardrail | What must not break while we push the north-star? | Credit cost per activated team; rep response SLA |
| Leading | What moves now, before the outcome lands? | Enriched-and-qualified rate within 24h of capture |
| Lagging | What confirms real impact weeks later? | Paid conversion and seat expansion at day 90 |
Commit to one north-star; surround it with the guardrails it would otherwise sacrifice.
The program funnel, end to endcapture to expansion
Every growth program shares a backbone. Instrument each transition so you can see where leads stall, then attach a rate to every arrow rather than reporting raw counts that hide the drop-offs.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
Attach a rate to every arrow; the gates are where a lead can be rejected, not just advanced.
- 1Capture. Lead enters from a form, partner feed or product signup. Count: new leads by source.
- 2Enriched. Waterfall enrichment attaches firmographic and tech-stack data. Rate: enrichment match rate.
- 3Qualified. Scoring crosses the ICP threshold. Rate: enriched → qualified.
- 4Routed. Lead lands with the right owner inside the SLA. Rate: routed within SLA.
- 5Meeting / activated. First real engagement or product activation. Rate: routed → activated.
- 6Expansion. Paid conversion or seat growth. Rate: activated → expanded.
Efficiency metrics prove the engineering, not just the funnelthe part marketing ops forgets
Funnel rates show the program works. Efficiency metrics show you built infrastructure instead of doing the work by hand. For a GTM Engineer seat, these are often the metrics that separate you from a campaign operator.
- Enrichment match rate
- share of leads that got usable data from the waterfall
- Cost per enriched lead
- spend across providers divided by matched leads
- Routing SLA hit rate
- share of leads routed to an owner inside the target window
- Manual hours removed
- human time the automation replaced per week
- Pipeline error rate
- share of runs that failed, retried or fell to a fallback
These say "I built durable plumbing," which is what the seat is.
Define "qualified" so the metric can't be gamedwhere upstream logic leaks into the scoreboard
If "qualified" means "score above the threshold," then anyone who loosens the scoring model can manufacture qualified leads without improving the business. A precise definition pins the metric to an outcome, not to a knob someone upstream controls.
A defensible definition reads like a query: first time a lead crosses ICP score 75, with a verified work email and a matched account, excluding existing customers. Each clause closes a gaming path. The verified-email clause stops junk captures from counting; the existing-customer exclusion stops the program from claiming credit for accounts it didn't create.
A number with no baseline and no target is trivia. Before launch, record the current state ("enrichment match rate is 58% on inbound") and set a target ("75% by end of quarter"). Iteration only has meaning against a line you drew first.
Resist the metric bouquet. Ten dashboards with thirty metrics is a way to avoid committing to what the program is for. Name one north-star, two guardrails and the leading indicator you'll watch weekly; everything else is diagnostic, not the scoreboard.
Takeaway. Pick one north-star plus its guardrails, attach a rate to every funnel transition and define "qualified" tightly enough that no upstream knob can inflate it.
Self-check
Feedback loops
After this you can design measurement that feeds back into scoring and routing so the program improves itself, not just reports on itself.
Reporting tells you what happened. A feedback loop changes what happens next. The seat's impact comes from building the second kind, where won/lost outcomes quietly retune the system that produced them.
A scoring model is a hypothesis: "these traits predict a good lead." Without a loop, that hypothesis ossifies. Sales closes deals the model rated low and ghosts leads it rated high and nobody tells the model. Closing the loop means the outcome flows back to the input that made the prediction.
The closed loop, made concreteoutcome feeds the scorer
- 1Emit. Every routing decision logs the lead, its score, the owner and a timestamp.
- 2Observe. The CRM marks the deal won, lost or stalled, with a reason code.
- 3Join. A scheduled job stitches outcome back to the original score and segment.
- 4Learn. Recompute which traits actually predicted wins; flag features that no longer earn their weight.
- 5Adjust. Update the scoring weights or routing thresholds, with the change logged and dated.
- 6Re-measure. Watch the next cohort's conversion to confirm the change helped, not just moved the number.
Detecting drift before it costs a quarterthe silent failure mode
Pipelines rot quietly. A data provider changes its schema and match rate slips from 75% to 60% over three weeks; nobody notices until pipeline dries up. Drift detection means the metric watches itself.
- Drift signal
- Match rate drops week over week
- Likely cause
- Provider schema or coverage change
- What the loop should do
- Alert; fall back to next provider in the waterfall
- Drift signal
- Qualified rate climbs but conversion falls
- Likely cause
- Scoring inflated; threshold too loose
- What the loop should do
- Re-fit weights against recent outcomes
- Drift signal
- Routing SLA hit rate decays
- Likely cause
- Volume spike or owner capacity change
- What the loop should do
- Re-balance round-robin; alert on backlog
- Drift signal
- Enrichment cost per lead rises
- Likely cause
- Cheap provider missing more; falling to pricey one
- What the loop should do
- Re-order the waterfall by cost-adjusted hit rate
| Drift signal | Likely cause | What the loop should do |
|---|---|---|
| Match rate drops week over week | Provider schema or coverage change | Alert; fall back to next provider in the waterfall |
| Qualified rate climbs but conversion falls | Scoring inflated; threshold too loose | Re-fit weights against recent outcomes |
| Routing SLA hit rate decays | Volume spike or owner capacity change | Re-balance round-robin; alert on backlog |
| Enrichment cost per lead rises | Cheap provider missing more; falling to pricey one | Re-order the waterfall by cost-adjusted hit rate |
Each signal maps to an automated response, not a quarterly review.
Experiments with honest readoutsA/B on routing and messaging
When you A/B a routing rule or an outreach variant, the discipline is the same as any experiment. Randomize at the lead or account level, pre-register the metric and resist peeking until you have the sample to call it. A directional read stated as a directional read beats a false certainty.
- Randomize at the right unit (account, not lead, when reps work whole accounts) to avoid contamination.
- Pre-commit to the primary metric and the minimum sample before you launch, so you can't retrofit a winner.
- Report the effect with its uncertainty and call out when the sample is too small to conclude anything.
Instrument at the sourcethe workflow emits the events
You can't measure what the workflow doesn't emit. Build the event emission into the automation itself, so every enrichment, score and route writes a structured record at the moment it happens. Bolting analytics on afterward gives you gaps exactly where the system failed.
{
"event": "lead.routed",
"lead_id": "ld_8f21",
"account_id": "ac_03b9",
"score": 82,
"owner": "rep_14",
"rule": "segment_round_robin_v3",
"sla_target_min": 15,
"routed_at": "2026-06-15T17:04:22Z",
"idempotency_key": "ld_8f21:routed:v3"
}A program that re-scores itself from real outcomes and re-orders its enrichment waterfall by cost-adjusted hit rate is a self-improving primitive, not a one-off campaign. That's the difference between "I ran a program" and "I built infrastructure the next ten programs reuse."
"I instrument every workflow step to emit a structured event, join outcomes back to the original score weekly and let the model re-fit against what actually converted. The same emission feeds drift alerts, so a slipping match rate pages me before it dries up pipeline."
Takeaway. Make every workflow step emit a structured event, join outcomes back to the score weekly and wire drift signals to automated responses so the program tunes itself.
Self-check
Attribution honesty
After this you can reason about attribution models and their biases and communicate what a program actually caused without overclaiming.
Attribution is where GTM data is most tempting to lie. Every model is a simplification with a built-in bias and a developer-led motion makes the lie easier because product usage muddies every marketing claim.
Truth-seeking, the value screened throughout this loop, shows up most sharply here. The honest answer to "did the startup program drive that revenue?" is usually "partly and here's how confident I am," not a clean number that makes a slide look good.
- Model
- First-touch
- How it assigns credit
- All credit to the first interaction
- Built-in bias
- Over-credits awareness; ignores what closed
- Model
- Last-touch
- How it assigns credit
- All credit to the final interaction
- Built-in bias
- Over-credits closing; ignores what created demand
- Model
- Linear multi-touch
- How it assigns credit
- Equal credit across all touches
- Built-in bias
- Flatters minor touches; assumes every touch mattered equally
- Model
- Time-decay
- How it assigns credit
- More credit to touches near the close
- Built-in bias
- Defensible but still a guess at causality
| Model | How it assigns credit | Built-in bias |
|---|---|---|
| First-touch | All credit to the first interaction | Over-credits awareness; ignores what closed |
| Last-touch | All credit to the final interaction | Over-credits closing; ignores what created demand |
| Linear multi-touch | Equal credit across all touches | Flatters minor touches; assumes every touch mattered equally |
| Time-decay | More credit to touches near the close | Defensible but still a guess at causality |
No model measures cause; each one redistributes correlation under a different assumption.
PLG breaks the marketing-sourced storyproduct usage muddies the claim
In a self-serve motion, a developer can discover Cursor, use it solo for two months, then show up in a partnership-sourced deal that takes the credit. The touch that mattered most never appeared in the marketing log. Any attribution that ignores product usage will systematically over-credit the last human touch.
- Product usage often precedes the first recorded marketing touch, so "marketing-sourced" undercounts product-led demand.
- Bottom-up adoption means the buyer and the user differ; the user's journey is invisible to last-touch.
- A single account can have dozens of self-serve users before one becomes the enterprise champion.
Incrementality is the only honest questionwhat the program actually caused
Correlation asks "did leads who touched the program convert more?" Incrementality asks "would they have converted anyway?" The second is harder and far more valuable. The cleanest evidence is a holdout: a comparable group that didn't get the program, measured against the group that did.
- Weakest
- Program-touched leads convert higher (pure correlation)
- Better
- Matched cohorts: similar leads with vs. without the program
- Strong
- Geo or segment holdout where the program was withheld
- Strongest
- Randomized holdout at the lead or account level
State which rung your claim stands on; don't borrow the credibility of a higher one.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
Each rung rests on the one below; name the rung your number actually stands on.
When to invest vs. accept a directional readthe pragmatic call
Better attribution costs engineering time and sometimes deliberately withheld pipeline. Spend it where the decision is expensive and reversible-only-at-cost. For a small experiment, a directional read is enough; for a multi-quarter budget allocation, a holdout earns its cost.
When asked "how much revenue did the program drive?", don't pick a model and quote a number. Say which model you'd use, name its bias out loud, then propose the cheapest holdout that would turn correlation into a causal read. That sequence is exactly the truth-seeking the founder round screens for.
"Last-touch says the program sourced $1.2M, but it can't see the product usage that preceded most of those deals, so treat that as an upper bound. A geo holdout next quarter would tell us the incremental number. Until then I'd report it as directional, not sourced."
The pressure in the room is to give a confident single number. Giving one you can't defend is the fastest way to fail a truth-seeking screen. Quantify the uncertainty instead; "directionally positive, here's the read I trust" beats a precise figure built on a model you'd disown under questioning.
Takeaway. Name the attribution model's bias before you quote its number, treat marketing-sourced as an upper bound in a PLG motion and reach for a holdout when the decision is expensive enough to justify it.
Self-check
QIn Cursor's developer-led motion, why does a last-touch model tend to over-credit a partnership or sales touch and what's the most honest way to correct for it?
Dashboards that drive action
After this you can present data so non-technical stakeholders can act and document definitions so the team stays self-sufficient.
A dashboard nobody acts on is a screensaver. The seat's documentation responsibility lands here: dashboards and metric definitions are how a flat team stays self-sufficient without routing every question through you.
The JD names Looker, Tableau, Power BI and Omni. The interviewer doesn't care which logo you pick; they care that you can justify it and that you design for a decision rather than a data dump.
Pick a BI tool and own the reasoningthe choice matters less than the why
LookML centralizes metric definitions in version-controlled code.
Strong when you want one source of truth and self-serve exploration.
Spreadsheet-speed exploration with a governed semantic model underneath.
Fits a small team that wants fast iteration without losing definitions.
Best-in-class visual exploration and ad-hoc analysis.
Governance of definitions lives outside the tool, so you must add discipline.
Cost-effective inside a Microsoft stack; DAX is powerful but a learning curve.
Strong when the org already lives in Office and Azure.
For this seat the honest pick is a tool with a governed semantic layer, because the whole job is preventing the "whose number is right?" fight. Looker or Omni let the definition of qualified live in one place that every chart inherits.
One screen, one questiondesign for a decision
Every dashboard should answer a single question a specific person has to act on. Before adding a chart, ask whose decision it changes. If the answer is "it's interesting," it belongs in an exploration view, not the action dashboard.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
The difference is whether the screen changes a decision or just displays numbers.
- Headline
- The one metric vs. its target, with the trend
- Decision
- What the viewer should do if it's red vs. green
- Breakdown
- The single cut that localizes the problem (by segment or source)
- Freshness
- When the data last updated, stated on the screen
If a panel doesn't change a decision, it's clutter.
Alert on the metric, don't just chart itthe part people skip
A chart waits for someone to look. An alert reaches out when the number crosses a line. The match-rate drift from the feedback-loop section should page you, not sit on a tab nobody opened for two weeks.
- Set thresholds tied to the guardrails, not arbitrary round numbers ("match rate below 65% for two days").
- Route the alert to the owner who can act, with the breakdown attached so they don't have to go hunting.
- Tune for signal: an alert that fires daily gets muted and a muted alert is worse than none.
Cursor's own field-engineering team ships this. They built an internal usage calculator in Cursor: feed it an account name or a direct team ID (e.g. straight from Stripe) and it returns a granular breakdown of how that account uses Cursor by token and by model, so GTM can forecast usage scope and trends. It started as an Airtable no-code prototype and graduated to real code as it earned its keep, hosted on Vercel and pulling live data through webhooks and a proxy. The win isn't the chart, it's the placement. Rather than make a rep open another tool, they embedded it as a tab inside Salesforce, prefiltered to the account or opportunity in view, and still take feature requests to make it more ergonomic.
"in Salesforce we have actually a tab that pulls up this usage calculator that's prefiltered to that account or that opportunity."
Document definitions to end the "whose number" fightself-sufficiency by design
The fastest way to lose trust in data is two dashboards showing different "qualified lead" counts. A short, versioned metric dictionary, one definition per metric with the exact logic and owner, prevents it. This is the lightweight documentation the role explicitly asks you to maintain.
- Metric
- Qualified lead
- Definition (exact logic)
- First crossing ICP score 75, verified email, matched account, non-customer
- Owner
- GTM Eng
- Source of truth
- lead_stage_history
- Metric
- Match rate
- Definition (exact logic)
- Leads with usable enrichment / total enriched, rolling 7d
- Owner
- GTM Eng
- Source of truth
- enrichment_runs
- Metric
- Routed within SLA
- Definition (exact logic)
- Routed ≤ 15 min of qualify / total routed
- Owner
- GTM Eng
- Source of truth
- routing_events
| Metric | Definition (exact logic) | Owner | Source of truth |
|---|---|---|---|
| Qualified lead | First crossing ICP score 75, verified email, matched account, non-customer | GTM Eng | lead_stage_history |
| Match rate | Leads with usable enrichment / total enriched, rolling 7d | GTM Eng | enrichment_runs |
| Routed within SLA | Routed ≤ 15 min of qualify / total routed | GTM Eng | routing_events |
One row per metric; the dashboard chart links to its row.
When every chart links to its definition and ships with an alert and a clear "so what," the team stops asking you what a number means and starts acting on it. That's the self-sufficiency the seat is measured on and it's why dashboard design is engineering, not decoration.
Asked to design a dashboard, start with the question and the person: "This is for the program lead to decide weekly whether to reallocate enrichment spend." Then build backward to the three panels that answer it. Designing from the decision, not the data, reads as senior.
Takeaway. Design every dashboard around one decision and one owner, alert on the guardrails instead of just charting them and keep a versioned metric dictionary so nobody argues about whose number is right.
Self-check
QTwo teams pull "qualified leads" from different dashboards and get different numbers, eroding trust in the data. As the GTM Engineer, what's the durable fix?