The morning the marketing site moved to a new host, it was built and cut over the same day, with the owner's go-ahead, and then checked against a counted list: 29 addresses, same pages, same titles, same analytics tag, every old redirect still a redirect, the 404 page still a 404. The first build from the new setup deployed in about a minute. That is what a forward deployed engagement looks like on a good day, and the reason it was a good day is everything that had been written down in the months before it.
Most explanations of forward deployed engineering are frameworks: five phases with week numbers attached, written by companies that sell staffing or software. This page is the other thing. It is the operating model as it actually ran inside a founder-led company for eight months, taken from the notes, handoff documents and review records kept at the time, with the numbers left in. Nothing here is a template. It is what happened, arranged in the order it happens.
The short answer
A forward deployed engagement runs on five habits: the owner's decisions are written down before any code; something usable ships through a real channel every week; the quality bar lives in the compiler, not in a document; every external dependency has a stand-in so credentials never block; and the engagement ends with a handover a stranger could execute. Everything else is detail, and the detail is below.
Before day one: the decisions that are not mine
The first artifact of the engagement was not a repository. It was a table with two columns, human and engineer, that said who owned what: the human writes the specs, reviews the issues and decides the order; the engineer does everything between "this is ready" and "this is on the development branch". One line under it said the spec is the product decision and the human owns it entirely.
Beside that table sat the owner's rules, and they carried into every later session. No live change to the domain, the zone or the running site without his say-so. Never probe a hostname before creating it, a mistake the project had already paid for once. Keep the analytics and search tooling exactly as they are. Plain English in anything he reads. Later, when the go-to-market work started, a second list appeared with a heading that did the work of a contract: founder decisions, do not relitigate. Nine lines. Which data source is in and which is out. No phone calls to customers, ever. Which segment first. Which platforms get budget and which are an explicit extra.
The repository's own instruction file fenced scope in the same spirit: build exactly what is asked, nothing more; no "while we're here" additions, no speculative error handling for scenarios that do not exist yet, nothing outside the acceptance criteria. On another product the equivalent file was headed "locked decisions, do not deviate", and each decision carried the date it was confirmed.
Engineers read these lists as constraints. They are the opposite. Every line is a decision I never have to make, defend or revisit, and a boundary the owner never has to police. On my own projects I rebuild freely; one of them went from version one to version two with 238 commits discarded after six days. Inside someone else's business you do not get to do that, and the list is what makes the speed safe.
Week one: prove the machine on something small
The first thing that shipped was a login page. Not because it mattered, but because it was the smallest thing that could go through the whole build pipeline end to end: a spec written from a static design, three issues in a fixed shape (the backend handler with mock data, the screen, the wiring between them), a plan, a build, a review, a merge. The repository's issue history still shows it: issues one to three are the login page, four is its database tables, and five to seven are the same three issues again, this time labeled merged. The machine was proven on a screen nobody would ever look twice at, so that when the video pipeline went through it a week later, the pipeline was the only new thing.
Week one also settles how the customer reviews. Here it was one gesture: label an issue "ready", from a browser or a phone. That label was the priority control. The owner decided what got built and in what order without ever opening an editor, and the engineer never had to guess what came next.
Every week: something usable, through a real channel
The rule that governed the cadence was written into the architecture document of another engagement in one sentence, and it applied everywhere I have worked since: every week ends with something usable via at least one real channel. Engine progress nobody can touch is theater.
"Usable" was defined in advance so it could not drift. On that engagement the channels were listed by week: engine core in weeks one and two, the web chat in weeks two and three as the first fully usable channel, the messaging adapter in weeks four and five, and the next platform explicitly held back until the earlier one had produced retention evidence. On the go-to-market work it was a fixed count from a fixed date: two videos a week from week three, twelve by day sixty, each title chosen in advance with its search volume beside it, so no week's output was negotiated inside the week.
What happens in a week where nothing ships? The records answer this with measurement dates rather than sanctions. The first read of the real conversion rate was set for week four, on the first 200 registrations. A separate engagement wrote a kill gate at weeks five to eight with numeric criteria, and a week-twelve decision named for what it was: double down, change the wedge, or stop honestly. A silent week is not a failure to be hidden. It is explained in plain English, with what blocked it and who unblocks it.
The engagement in three lanes. The owner's lane is short on purpose: decide, label, review, do the named steps. The red box is where the weeks go.
Speed with safety: the bar lives in the compiler
The engagement produced roughly 2,900 commits across 111 issues without a single pull request, and it did not ship code that panics. Those two facts are the same fact. The quality bar was not a document anyone had to remember; it was a list of things the compiler refuses. The lint configuration opens with its own philosophy: if it can panic or hide bugs, deny it. Then it does: unwrapping a value that might not exist, denied; a placeholder that says "to do", denied; an unreachable branch, denied; a wildcard that swallows an error, denied. The front end mirrors it: no debug prints, no non-null assertions, no untyped values, dead code is a build failure. A comment that says "fix later" cannot be committed. There is nowhere for a shortcut to hide.
Around that bar ran a loop, and much of the building was done by coding agents inside it. One task per iteration, with a fresh context each time so nothing accumulates. Search the codebase before writing, because the thing usually exists. Implement fully, no placeholders. Run every check; if any fails, fix and rerun all of them from the start. Commit with the issue and task number in the message, and then verify the commit exists and the working tree is clean, because uncommitted code is lost code. Mark the task done. Exit. The loop restarts with a clean slate. The twelve rules that govern it fit on one screen, and the last of them is the one that makes the rest survivable: git is the safety net, every task is a commit, every issue is a branch, full reversibility.
Review was three reviewers with narrow remits, run in parallel, told to find and not to fix: one for error handling and process management, one for performance, one for the interface. Their contract with the machine was binary, written after it was once broken: either add the findings as tasks or write the file that says the review passed; do neither and the script will merge the bugs you just found. It happened before. An independent audit ran after, constrained so it could not invent work: every finding must cite the rule or spec line it violates, and a good audit has five to fifteen findings; twenty is noise.
Here is what that looks like on one feature, the deferred render, from the six rounds of review notes kept in the repository. Round one reviewed five commits and found fourteen items, the worst of them a publish that would silently ship a master video with an old caption size baked in. Round two re-reviewed round one's fixes and found four new problems the fixes had introduced, including one where I had told the agent something true about database cost and wrong about the user's experience, and the note says so: my mistake, let me correct it now. Round three found three deploy blockers, two of them the kind where a user publishes without their latest edit and never knows. Round four was two characters of SQL, an equals sign that had to become "in", because my own description of the race in round three had been wrong. Round five found a logo that had an enum value but no code path to draw it, discovered by walking the product rather than reading it. Round six closed the three items that had been consciously deferred. Every round closed with its cost: about two hours, forty minutes, thirty, five, twenty, forty-five. About five hours across six rounds, for a feature estimated at ten to fourteen days.
Every round also declared itself final. "All known races are guarded." "Production-ready, for real this time." "Truly final. No more rounds. Ship it." Then, in round six, in capitals, "truly done". I leave that sequence in because it is the most honest record I have of how review actually goes, and because a customer who has been told "done" four times deserves to know the fifth was checked by a different method.
Seams and credentials: never wait on a key
The slowest thing in any engagement is not the code. It is the customer's accounts: the payment processor, the video host, the email provider, the DNS zone, the API key that has to be issued by someone who is on holiday. So the system is built so that none of them can block it.
Every external dependency sits behind a seam with two implementations: the real one, and a deterministic stand-in that records what it would have done. Which one runs is decided at construction by whether the secret is present; where two secrets are needed, both are required. On one product that covered video, email, webhooks, custom domains and billing, and the result was that the whole thing ran and tested with no cloud account and no external keys at all: 127 unit tests and 179 endpoint tests, the endpoint tests in a real worker runtime against a migrated test database. When the keys arrived, one environment variable turned each real thing on and nothing else changed. On the next product the rule was tightened further: in a shipped runtime the stand-in is unreachable, and a missing key produces an honest "not configured" rather than a fabricated result. The README says it in four words: no fabricated ad account or metrics.
The same discipline governs how secrets move. A public site never holds a key; its contact form posts to the app, which does. Keys are minted by a local script that prints them once and stores only the hash. A secret is never pasted into a chat, and one that was gets re-issued as a named step in the handover. Where my own token was deliberately under-scoped, the note says who must act instead: the DNS records get added by the owner, because the engineer's token is read-only on the zone. That is not a limitation. It is the owner's rule from day one, kept.
Other people's rules: the last mile
The constraints that cost the most time were not technical and were not ours. They belonged to platforms, and the only way to learn them was to try to publish. The product had to post to five of them, and each had a ceiling you discover by hitting it. One refuses any link that is signed or expiring, which meant the entire media store had to serve plain public addresses or nothing would post; the playbook calls it the silent killer. One caps unverified accounts at fifteen minutes of video, so a full-length master fails without saying why. One drops reach by roughly 40 to 50 percent when the post contains a link, so links go in the first comment. One takes no video at all, one image per post, and archives posts after about a week. One turned out not to have the requirement we had assumed at all; the verification pass corrected our own note.
Every ceiling went into a playbook with a mark against it: works as is, needs a change, cannot do. The conclusion at the bottom is the forward deployed one: most of the "needs a change" items were the same three jobs, done once and reused. A transform layer that asks "as is, transform, or skip" before scheduling; a gate at connection time that checks the account type before anything is queued; and one public-address guarantee that satisfied two platforms at once.
Infrastructure has rules too, and they are written in the learnings files so they are learned once. Uploads over 100 megabytes were dying at the reverse proxy with a timeout, so uploads went straight to storage on signed addresses and never touched the server. The host restarts containers without warning, which corrupted the database until writes were made fully synchronous; a startup sweep now resets any job the restart stranded. A preview environment once inherited the production routes and took the live traffic, so the preview config now overrides them with an empty list. A health probe that polled the GPU kept it warm around the clock at about a thousand dollars a month; it was replaced with an on-demand check.
Measure before deciding
The build notes carry a number beside almost every decision, because a decision without one is an opinion. A representative set from one engagement:
| What | Before | After | What changed |
|---|---|---|---|
| Render of an eleven-chunk video | 14 min | about 2 min | chunks encoded at the same time instead of one after another |
| Merge step | 90 s | 38 s | one encoder option the merge never needed, removed |
| Output size from a 439 MB source | 1.87 GB | 247 MB | a quality setting backed off to one nobody could tell apart |
| Timed graphic overlay, per 90 s chunk | 0.08x, over 10 min | 3.4x, 26.5 s | timing moved from the filter to the input, so the graphic is decoded only while it is on screen |
| Preview of a brand profile | 4 to 5 min | 15 to 90 s | rendered in the browser instead of on a server; zero server cost |
| Transcription of a fifteen-minute video | 117 s on a GPU | 2.4 s | moved to a hosted speech model; the GPU path retired |
| Configuration surface | 50+ fields | 6 | 841 lines deleted across two commits; the model decides the rest |
| Idle GPU cost | about $1,000 a month | 0 | polling health check replaced with an on-demand one |
One full pipeline run was scored the same way: 365 timeline entries assembled, 41 footage placements from a library of 148, of which 22 were right, five acceptable, seven the wrong location and three too long. Nobody publishes a scorecard like that about their own AI. It is the only way I know to make the next run better.
The handover is the deliverable
An engagement that ends with a running system and no handover has not ended. It has stopped being visible. The ten handoff documents from this engagement, across four codebases, all follow one shape, and the shape is a contract.
- From, to, date, subject. Every one opens by saying whose problem it is and whose it is not: "nothing in this folder is application code", "no changes on your side", "not urgent". And who to come back to with questions.
- What shipped, with the commits. Each change named against the commit that made it, and the verification beside it: build green, lints clean, test suite at its baseline, no new failures.
- What was deliberately not built, and why. A section whose title is the point. The event webhook was not built because the receiving route did not exist yet; the note says do not set the address, and holds the secret ready for when it does.
- What was ruled out, with reasons, so the next engineer does not re-litigate it. Adaptive streaming for eight-second clips: overkill. Removing the master render: too many consumers. Feature flags: out of scope, ship the clean cutover.
- The activation order, with the actor on every line. You hand the owner the DNS records; the owner adds them; you verify the domain; the owner sets two secrets and redeploys; the owner triggers one real event and you confirm it landed. Step six, the one nobody volunteers: re-issue the key, because the current one was pasted into a chat.
- Acceptance the other side can check. Every address in the sitemap answers with the same page; every old redirect still redirects; a test object appears within a minute and disappears on delete; performance 90 or above on mobile; no change to any analytics identifier.
- Open questions for the owner, asked rather than decided. Keep the two old posts or retire them: he said either is fine, so it is recorded as either. And a bridge so nothing breaks in between: for a week the app wrote its pages to both the old folder and the new one, and the mirror was removed the day the other side confirmed.
Then the exit. The last document is a copy list, six files, take these and leave the rest, followed by "what is next, in order", where every step the engineer cannot take names the person who can: the founder connects the search property, the founder creates the token, the founder approves the weekly schedule before it spends anything. The customer's own team can add a second brand to the shipped system without engineering; the note shows how. And the documentation keeps itself current after I leave, because an agent with a named remit regenerates it when the structure changes and is forbidden from creating new documents.
The same model with a team
On a retail platform I led for nineteen months, a live merchandising operation with purchase orders, cost of goods, stock transfers between stores and integrations into the point of sale, the accounting system and the shipping carriers, the model was the same and the mechanics were different. Branches carried the release version rather than an issue number. Features merged into the version branch many times over a month before the version merged to main, and the release boundary was where the pull requests lived. Reverts were part of the day, not an incident: a change merged at 8:06, reverted at 8:09, fixed, re-landed at 8:21 and released at 8:24. What I owned was the architecture, the split between the transactional store and the analytics store, the AI layer over the sales and stock data, and every seam into the systems the business already ran. A team of seven built on that. The commit log of a real operation is mostly reconciliation of other people's data, and that is the work.
When this model does not fit
I sell this way of working, so this section matters more than the others.
- The owner cannot decide weekly. The whole model runs on one gesture a week from the person who owns the decisions. If that person is unavailable for a month, the engineer will either stall or start deciding for them, and both are worse than not starting.
- Nobody inside will own the system afterwards. The handover has to land on someone. If it cannot, the system dies the month the engineer leaves, however good the document.
- The data cannot be read this week. Not "we have it" but "an engineer can read it now, with permission". If the answer is a procurement process, run the procurement first.
- What is wanted is a strategy document. A different job. Pretending an engineer is the cheap way to get one wastes both.
- A product already does it. If something on the market does the job with the integrations you need, buy it. The forward deployed case is the problem between products, inside your own process.
Common questions
What is the forward deployed engineering model?
An operating model in which an engineer builds inside the customer's business: with the customer's data, inside its systems, under its written rules, shipping something usable every week, and ending with a handover the customer's own team can execute. It differs from consulting in that the artifact is a running system, and from a product team in that the work is shaped by one company rather than many.
What happens in the first week?
Decisions, not code. The owner's decisions and operating rules go into a file the engineer treats as fixed. Then one small feature is taken through the whole build machine end to end, so the pipeline is proven before anything large goes through it.
What ships each week?
Something the customer can use through a real channel. Progress nobody can touch does not count. A week that produces nothing usable is explained in plain language, with what blocked it and who unblocks it.
How does one engineer keep the pace of a team without breaking things?
The quality bar lives in the compiler and the linters, so code that can panic, swallow an error or leave a placeholder cannot be committed. One task at a time, a commit after each, automated reviewers with narrow remits, and a learnings file so a mistake is made once. Roughly 2,900 commits across 111 issues without a pull request, on one engagement.
How are credentials handled so they do not block?
Every external dependency has a real implementation and a deterministic stand-in, chosen by whether the secret is present. The whole system runs and tests before any customer account exists. When the keys arrive, one variable turns the real thing on.
What does the handover contain?
From, to and date; what shipped with the commits; what was deliberately not built and why; what was ruled out; a numbered activation order naming who does each step; open questions asked rather than decided; and any security action, such as re-issuing an exposed key.
When does the model not fit?
When the owner cannot decide weekly, when nobody inside will own the system, when the data cannot be read this week, when a strategy document is what is wanted, or when a product already does the job.
Start here
What is a forward deployed engineer? The job, from inside itThe role itself: where the term came from, the mistake a local reviewer catches that no engineer would, and when hiring one is the wrong call.
Measured, not asserted
Running a 26B model at 124 tokens/sec on a CPU, no GPUThe same habit applied to inference: the byte budget, the dead ends, and the numbers on real hardware.