AI EngineeringZero to ProductionHome·About·Contact
Part V · Chapter 7

The Forward Deployed Engineer Method

Chapters 1–6 taught you to build LLM systems. This one teaches you to deploy them the way a Forward Deployed Engineer does: embed with a real user, find the problem worth solving, ship a thin slice, and expand only as you earn trust. It's the difference between a demo and a system someone depends on.

⏱️ ~1.5 hours🧪 1 labs🎯 Beginner→Tech-lead

Learning objectives

  • Explain the Forward Deployed Engineer (FDE) method.
  • Work backward from a customer outcome, not a feature list.
  • Ship a thin end-to-end slice fast, then iterate with the user.
  • Scale the FDE approach across a team.
▶ Runnable companionEvery code block in this lesson is also a standalone file under code/ch07-fde/ in the course, with a README on how to run it. Read here, run there — no need to copy-paste.

1 · What an FDE does essential

Chapters 1–6 taught you to build; the Forward Deployed Engineer method is about building the right thing with the customer in the room. An FDE embeds with users, learns the real problem, and ships working software against it — closing the gap between what's technically possible and what actually helps.

2 · Work backward from the outcome essential

Don't start from "what can the LLM do?" Start from the customer's outcome — the decision they need to make, the hours they want back — and work backward to the smallest system that delivers it. Features are a means; the outcome is the point.

Customer outcome hours saved Smallest slice end-to-end Ship + watch in production Iterate with user tight loop
🗺️ How to read this diagram

This is the whole FDE method in one picture — a loop, read left to right, that you keep running with a real customer instead of guessing behind a spec. Each box feeds the next.

  • Customer outcome (hours saved) — start here, not with a feature list. Name the result the customer actually wants (a decision made, hours given back). Everything else serves this.
  • Smallest slice (end-to-end) — the arrow means "work backward to": pick the tiniest thing that delivers that outcome, but works the whole way through, from input to result.
  • Ship + watch (in production) — you put that slice in front of the real user and observe how they actually use it, where it breaks, what they ask for next.
  • Iterate with user (tight loop) — you turn what you watched into the next slice. The shorter this loop, the faster you learn — that closeness to the user is the FDE's real edge.

In short: It's a cycle, not a straight line: outcome → smallest slice → ship & watch → iterate, then back to a sharper slice. The point is learning fast, not shipping big.

3 · Intermediate — the thin end-to-end slice intermediate

Ship a thin vertical slice that works end-to-end for one real case — not a polished module in isolation. A rough pipeline the user can actually run beats a perfect component they can't, because it produces the feedback that tells you what to build next.

Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
Python · score whether a plan is a thin slice or a big-bang build (runs)
slice.pydef slice_score(end_to_end, weeks_to_first_use, num_features):
    risk = 0
    risk += 0 if end_to_end else 3          # not end-to-end = flying blind
    risk += 0 if weeks_to_first_use <= 2 else 2
    risk += 0 if num_features <= 2 else 2    # scope creep
    if risk == 0: return "thin slice — ship it, then learn"
    if risk <= 3: return "trim scope — get to real use faster"
    return "big-bang risk — you'll build the wrong thing"

print(slice_score(end_to_end=True,  weeks_to_first_use=1, num_features=1))
print(slice_score(end_to_end=False, weeks_to_first_use=8, num_features=6))
thin slice — ship it, then learn
big-bang risk — you'll build the wrong thing
▶ How this works

This tiny program is a gut-check scorer: describe a plan and it tells you whether it's a safe "thin slice" or a risky "big-bang build". It turns the chapter's advice into a rule you can run. The idea: add up risk points for the three things that most often sink a project, then read the total.

  1. def slice_score(end_to_end, weeks_to_first_use, num_features): takes three facts about your plan: does it work all the way through, how many weeks until a real person uses it, and how many features you're building first.
  2. risk = 0 starts a running score, then each line adds points for a warning sign. risk += 0 if end_to_end else 3 reads as: "add 0 if it's end-to-end, otherwise add 3." Not being end-to-end is the biggest penalty — the comment calls it flying blind.
  3. The next two lines add 2 points each if it takes more than 2 weeks to first use, or if you're packing in more than 2 features (that's scope creep). Small and soon scores low; big and slow scores high.
  4. The if ladder reads the total: 0 means a clean thin slice, <= 3 means trim scope and get to real use faster, and anything higher is flagged as big-bang risk — you'll likely build the wrong thing.
  5. The two print(...) lines run the scorer on a good plan and a bad one so you can see both verdicts.

What the output means: Two lines print. The first plan (end-to-end, 1 week, 1 feature) scores 0 → "thin slice — ship it, then learn". The second (not end-to-end, 8 weeks, 6 features) scores the maximum → "big-bang risk — you'll build the wrong thing".

Try this: Change the second call to weeks_to_first_use=2, num_features=2 but keep end_to_end=False. It still adds 3 for not being end-to-end, so it lands on "trim scope" — proof that shipping something that works end-to-end matters most.

4 · Advanced — iterate in the feedback loop advanced

Once the slice is in real use, the FDE lives in the feedback loop: watch how the user actually uses it, where it fails, what they ask for next — and turn that into the next slice. This is where evals (Ch 5) matter: you instrument the slice so "it's better now" is measured, not felt.

Proximity to the user is the advantageThe FDE edge isn't better code — it's a shorter loop between building and learning what to build. Being in the room (or the same Slack) when your software fails is worth more than a month of guessing behind a spec.

5 · Professional — managing the pull to over-build professional

The hard part is discipline: resisting the urge to build the general, elegant system before you've proven the specific, ugly one solves the problem. Ship narrow, earn the right to generalize with evidence of real use. Over-building is the most common FDE failure.

6 · Tech-lead — scaling the method tech-lead

A lead turns one FDE's instinct into a team practice: outcome-first framing in every project, thin-slice-then-iterate as the default, instrumentation baked in so learning is measured, and a path to productize what started as a bespoke deployment. The method scales when the loop (outcome → slice → measure → iterate) is the team's default, not one person's habit.

Bespoke first, product secondMany great products started as one FDE solving one customer's problem end-to-end, then generalizing what worked. A lead runs this deliberately: prove value bespoke, then invest in the product — not the reverse.

Exercise CH07.1 — Design a thin slice

Context: The whole method starts by working backward from a customer outcome, then cutting scope until the first slice is genuinely thin. Doing it once on a real problem is how the instinct sticks.

Your task: Take a real problem someone has: write the customer outcome in one sentence, design the thinnest end-to-end slice that delivers it, and score the plan — cutting scope until it reads as a thin slice.

Requirements:

  • State the customer outcome in a single sentence — not a feature list
  • Design a slice that is genuinely end-to-end (a real user can use it)
  • Score it with the slice-risk logic from the ladder
  • If it flags risk, trim scope and re-score until it reads "thin slice"
  • Keep first real use inside ~2 weeks and the slice to a couple of features

💡 Hint: If you can't name who uses the slice and what they get, the outcome isn't sharp enough yet — fix that before touching scope.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · Rebuild slice_score from the lessonBeginner

Context: The Forward Deployed Engineer's core instinct is shipping the thinnest end-to-end slice. The lesson turns that instinct into a number so a plan's risk is visible before anyone writes code.

Your task: Reimplement the lesson's slice_score and map the total risk to a verdict.

Requirements:

  • +3 risk if the slice is not end-to-end
  • +2 risk if first real use is more than 2 weeks out
  • +2 risk if it bundles more than 2 features
  • Map total to thin slice / trim scope / big-bang risk
  • Show a clean plan scoring 0 and a bloated plan scoring high

💡 Hint: Each risk factor is an independent additive penalty; sum them first, then classify the single total.

Show solution

The scorer encodes the FDE priorities: end-to-end matters most, then speed to real use, then scope.

def slice_score(end_to_end, weeks_to_first_use, num_features):
    risk = 0
    risk += 0 if end_to_end else 3
    risk += 0 if weeks_to_first_use <= 2 else 2
    risk += 0 if num_features <= 2 else 2
    if risk == 0:
        return "thin slice - ship it, then learn"
    if risk <= 3:
        return "trim scope - get to real use faster"
    return "big-bang risk - you'll build the wrong thing"

print(slice_score(True, 1, 1))   # thin slice - ship it, then learn
print(slice_score(False, 8, 6))  # big-bang risk - you'll build the wrong thing
Exercise 2 · Outcome-first framing + the slice check togetherIntermediate

Context: A plan that lists features but names no customer outcome is the chapter's classic anti-pattern. Validity requires both a stated outcome and a thin-slice score.

Your task: Write validate_plan that rejects a plan with no customer outcome, then falls through to the slice-scoring logic.

Requirements:

  • Read the plan's customer_outcome and reject if it is missing or blank
  • Only score the slice once the outcome gate passes
  • Reuse the same +3/+2/+2 risk factors from Exercise 1
  • Return a clear message: rejected, thin-slice OK, or trim-with-risk-N
  • Demonstrate both a rejected plan and an accepted thin slice

💡 Hint: Treat the outcome as a hard precondition — a short-circuit return before any scoring — not another additive penalty.

Show solution

Outcome-first is the gate before scope: a well-scoped slice aimed at no outcome is still the wrong thing built quickly.

def slice_score(end_to_end, weeks_to_first_use, num_features):
    risk = (0 if end_to_end else 3) + (0 if weeks_to_first_use <= 2 else 2) \
         + (0 if num_features <= 2 else 2)
    return risk

def validate_plan(plan):
    outcome = plan.get("customer_outcome", "").strip()
    if not outcome:
        return "REJECT - name the customer outcome first, not a feature list"
    risk = slice_score(plan["end_to_end"], plan["weeks_to_first_use"], plan["num_features"])
    if risk == 0:
        return f"OK - thin slice toward: {outcome}"
    return f"TRIM (risk {risk}) - slice toward: {outcome}"

print(validate_plan({"customer_outcome": "", "end_to_end": True,
                     "weeks_to_first_use": 1, "num_features": 1}))  # REJECT ...
print(validate_plan({"customer_outcome": "cut incident triage from 45min to 5min",
                     "end_to_end": True, "weeks_to_first_use": 1, "num_features": 1}))  # OK - thin slice ...
Exercise 3 · Rank candidate slices by value-per-riskAdvanced

Context: When several thin slices are viable, the FDE builds the one with the best return on risk — and knows that the safest slice is worthless if it delivers almost nothing.

Your task: Given candidate slices each with hours_saved_per_week and a slice risk, rank them and pick what to build first, discarding safe-but-pointless options.

Requirements:

  • Compute each slice's risk with the Exercise 1 scorer
  • Skip any candidate whose value falls below a minimum threshold
  • Rank by a value-per-risk ratio, highest first
  • Guard against divide-by-zero when risk is 0 (e.g. use risk + 1)
  • Show a high-risk slice and a too-cheap slice both dropped for the same reason: not worth building first

💡 Hint: A safe slice and a valuable slice can both be wrong first picks — the ratio is what reconciles them into one ordering.

Show solution

The FDE picks the thinnest slice that still moves the outcome — highest value per unit of risk, with a floor on value so 'safe but pointless' loses.

def slice_score(end_to_end, weeks, feats):
    return (0 if end_to_end else 3) + (0 if weeks <= 2 else 2) + (0 if feats <= 2 else 2)

def rank_slices(candidates, min_value=1.0):
    scored = []
    for c in candidates:
        risk = slice_score(c["end_to_end"], c["weeks"], c["feats"])
        if c["hours_saved_per_week"] < min_value:
            continue                     # too little value to bother, even at low risk
        value_per_risk = c["hours_saved_per_week"] / (risk + 1)  # +1 avoids div-by-zero
        scored.append((value_per_risk, c["name"], risk))
    scored.sort(reverse=True)
    return scored

cands = [
    {"name": "triage-diagnoser", "hours_saved_per_week": 8, "end_to_end": True, "weeks": 1, "feats": 1},
    {"name": "full-provisioner", "hours_saved_per_week": 20, "end_to_end": False, "weeks": 8, "feats": 6},
    {"name": "tidy-log-formatter", "hours_saved_per_week": 0.2, "end_to_end": True, "weeks": 1, "feats": 1},
]
for vpr, name, risk in rank_slices(cands):
    print(f"{name}: value/risk={vpr:.1f} (risk {risk})")
# triage-diagnoser wins; full-provisioner high value but high risk; formatter dropped (too little value)
Exercise 4 · Instrument the feedback loop so 'better' is measuredExpert

Context: The FDE instruments the slice so "it's better now" is a number, not a feeling — and small user samples make a lucky streak look like progress.

Your task: Given two rounds of pass/fail user sessions, decide whether the latest iteration is a real improvement or just within noise.

Requirements:

  • Compute a success rate from a list of session results
  • Require a minimum sample size before judging (small samples return "not enough sessions")
  • Require the delta to clear a minimum threshold before calling it real
  • Distinguish improvement, regression, and within-noise
  • Show that the same percentage jump is "real" on a large sample but "noise" on a tiny one

💡 Hint: Two gates protect you here: enough sessions to trust the rates at all, and a delta big enough to exceed the wobble of a small sample.

Show solution

With small samples, a raw rate jump can be noise; a simple absolute-margin rule tied to sample size keeps you honest about whether the loop actually improved things.

def success_rate(sessions):
    return sum(1 for s in sessions if s["success"]) / len(sessions)

def is_real_improvement(before, after, min_delta=0.10, min_n=10):
    if len(before) < min_n or len(after) < min_n:
        return False, "not enough sessions to conclude - keep watching"
    delta = success_rate(after) - success_rate(before)
    if delta >= min_delta:
        return True, f"real improvement: +{delta:.0%}"
    if delta <= -min_delta:
        return False, f"regressed: {delta:.0%}"
    return False, f"within noise ({delta:+.0%}) - not proven"

before = [{"success": i < 6} for i in range(12)]   # 6/12 = 50%
after  = [{"success": i < 9} for i in range(12)]   # 9/12 = 75%
print(is_real_improvement(before, after))  # (True, 'real improvement: +25%')
print(is_real_improvement(before[:4], after[:4]))  # (False, 'not enough sessions ...')
Exercise 5 · A review gate that resists over-buildingProfessional

Context: Over-building is the chapter's number-one failure. A team needs a lightweight gate so the general, abstracted version is only approved once there is evidence of real use.

Your task: Encode a may_generalize gate that approves building the general version only when usage evidence clears thresholds for distinct users, weeks in production, and real requests.

Requirements:

  • Check distinct users, weeks in production, and real request volume against minimums
  • Collect a reason for every criterion that fails
  • Return "stay bespoke" with the joined reasons, or approval when all pass
  • Demonstrate a block on thin evidence (1 user / 1 week / a few requests)
  • Demonstrate approval on strong evidence (several users / weeks / dozens of requests)

💡 Hint: Generalisation is earned by evidence, not taste — the gate exists precisely to overrule the engineer who feels ready to abstract.

Show solution

The discipline is procedural: you don't earn the right to generalize on taste, you earn it on evidence. This turns 'ship narrow' into a checkable rule.

def may_generalize(feature, min_users=3, min_weeks=2, min_requests=25):
    reasons = []
    if feature["distinct_users"] < min_users:
        reasons.append(f"only {feature['distinct_users']} users (need {min_users})")
    if feature["weeks_in_prod"] < min_weeks:
        reasons.append(f"only {feature['weeks_in_prod']}w in prod (need {min_weeks})")
    if feature["real_requests"] < min_requests:
        reasons.append(f"only {feature['real_requests']} real uses (need {min_requests})")
    if reasons:
        return False, "stay bespoke: " + "; ".join(reasons)
    return True, "evidence of real use - approved to generalize"

print(may_generalize({"distinct_users": 1, "weeks_in_prod": 1, "real_requests": 3}))
# (False, 'stay bespoke: only 1 users (need 3); only 1w in prod (need 2); only 3 real uses (need 25)')
print(may_generalize({"distinct_users": 5, "weeks_in_prod": 4, "real_requests": 60}))
# (True, 'evidence of real use - approved to generalize')
Exercise 6 · Your lead asks you to scale the FDE method across 4 teamsIndustry scenario

Context: Your tech lead wants the FDE loop to be every team's default, not one engineer's habit — and the quality of the practice must be visible across teams.

Your task: Design how you'd operationalise the FDE method across four teams, and write a scorecard that flags teams drifting back to big-bang builds. (Design + code.)

Requirements:

  • Require an outcome-first brief on every project
  • Default to thin slices (short time-to-first-use, few features in slice one)
  • Bake instrumentation/evals in from the start; productise only after bespoke is proven
  • Score each team on named-outcome, fast-first-use, thin-slice, has-evals, and bespoke-before-platform
  • Return a score, a grade (following vs drifting), and the list of failed checks
  • Show one team passing and one drifting on every check

💡 Hint: The scorecard is the same discipline turned outward: it makes "are we still doing FDE?" a per-team number instead of a vibe.

Show solution

Design (section 6, tech-lead rung). Make the loop the default and measure adherence:

  1. Outcome-first framing required in every project brief (a named customer outcome, not a feature list).
  2. Thin-slice-then-iterate as the default: first user-facing use within ~2 weeks, ≤2 features in slice #1.
  3. Instrumentation baked in so 'better' is measured every iteration (ties to Ch 5 evals).
  4. Path to productize only after bespoke value is proven (the generalization gate above).

A scorecard turns the practice into something a lead can see across teams:

def fde_scorecard(team):
    checks = {
        "outcome_first":     bool(team.get("named_outcome")),
        "fast_first_use":    team["weeks_to_first_use"] <= 2,
        "thin_slice":        team["features_in_slice1"] <= 2,
        "instrumented":      team["has_evals"],
        "bespoke_before_platform": not team["built_platform_first"],
    }
    score = sum(checks.values())
    drifting = [k for k, ok in checks.items() if not ok]
    grade = "following FDE" if score >= 4 else "drifting to big-bang"
    return score, grade, drifting

teams = {
    "payments": {"named_outcome": "cut refunds MTTR", "weeks_to_first_use": 2,
                 "features_in_slice1": 1, "has_evals": True, "built_platform_first": False},
    "platform": {"named_outcome": "", "weeks_to_first_use": 10,
                 "features_in_slice1": 7, "has_evals": False, "built_platform_first": True},
}
for name, t in teams.items():
    score, grade, drift = fde_scorecard(t)
    print(f"{name}: {score}/5 {grade} | drift={drift}")
# payments: 5/5 following FDE | drift=[]
# platform: 0/5 drifting to big-bang | drift=[all five]

✓ Checkpoint — you can move on when you can…

  • Explain the FDE method and outcome-first framing.
  • Design a thin end-to-end slice.
  • Iterate in a measured feedback loop.
  • Resist over-building; scale the method to a team.

Knowledge check check yourself

✓ Knowledge check

The FDE method says to ship the thinnest end-to-end slice rather than a polished component in isolation. Why does a rough pipeline the user can actually run beat a perfect module they can't?

Show answer
Because the whole point is learning fast, not shipping big. A slice that works end-to-end for one real case produces the feedback that tells you what to build next; a perfect isolated component produces none. In the slice_score model, not being end-to-end is the biggest risk penalty precisely because it leaves you flying blind.
✓ Knowledge check

What does the chapter identify as the most common FDE failure, and what discipline counters it?

Show answer
Over-building — reaching for the general, elegant system before proving the specific, ugly one solves the problem. The counter is to ship narrow and earn the right to generalize only with evidence of real use (bespoke first, product second).
© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in