- Authors

- Name
- Youngju Kim
- @fjvbn20031
- Introduction — From One Customer to All Customers
- The FDE ↔ Product Feedback Loop
- Spotting the Pattern Across N Customers
- Extracting a Config and an Abstraction
- The Rule of Three: When NOT to Generalize
- The Tension: Shipping Fast vs Building for All
- When NOT to Generalize
- Great Products Are Born From Forward-Deployed Work
- Wrapping Up
- References
Introduction — From One Customer to All Customers
In the previous two posts we covered what a forward deployed engineer (FDE) is, and how a throwaway prototype gets one customer to the aha moment. This post tackles the next question: how do you grow bespoke work built for one customer into a platform for all of them?
This is the reason an FDE organization exists, and its hardest challenge. Done wrong, the company becomes a consultancy hauling around as many distinct blobs of code as it has customers. Done right, the insight drawn from the field becomes the company's core product. In fact, many great B2B products were born on exactly this path.
The FDE ↔ Product Feedback Loop
An FDE's real value isn't the code — it's the information. The FDE is the person in the company who knows the customer's reality most deeply. Which problems recur, which data actually exists, what delights the customer and what frustrates them — all of it accumulates in the FDE's head and fingertips.
In a healthy organization, this information flows to the product team.
- FDE → product: "Five customers all asked us to hand-build this feature. It should be a product."
- Product → FDE: "We built a generalized version of that feature. For the next customer, try this instead of a bespoke build."
When this loop turns, the FDE has to build from scratch less often, and product plans its roadmap on field evidence rather than imagination. When the loop breaks, the FDE forever rebuilds the same thing, and product builds features detached from real demand.
Spotting the Pattern Across N Customers
Productization starts with pattern recognition. One customer's request is just a request; a request that recurs across several customers is a signal.
An FDE's sense for patterns comes from questions like these.
- Am I copy-pasting the same code to different customers?
- Am I rebuilding what is essentially the same flow for each customer, changing only the names?
- Are several customers hitting the same problem while describing it differently?
The third one matters most. Customer A wants "contract risk review" and customer B wants "vendor document check," but underneath, both may be the same skeleton: "find and flag specific conditions in a long document." The eye that sees the structure below rather than the vocabulary on the surface — that's the first step of productization.
Extracting a Config and an Abstraction
Once you spot the pattern, the next move is to split it into what varies and what doesn't. What doesn't vary becomes the engine (the product); what varies falls out into config.
Take contract clause extraction. What differs per customer is roughly "which clauses to find," "which categories to classify into," and "what format to export." The act of extracting itself — the skeleton of calling the model and parsing the result — is all the same.
So you transform the bespoke code like this, pushing the per-customer differences out of code and into declarative config.
# customers/acme.yaml — per-customer config (the part that varies)
customer: acme-corp
clause_types:
- indemnification
- termination
- liability
output_format: csv
risk_threshold: 0.7
notify_channel: "acme-legal-slack"
# customers/globex.yaml — different customer, same engine
customer: globex-inc
clause_types:
- data_privacy
- auto_renewal
output_format: json
risk_threshold: 0.5
notify_channel: "globex-procurement-email"
The engine is a single, customer-agnostic piece of code that reads this config and runs.
# engine.py — the engine shared by every customer (the part that doesn't vary)
def run_extraction(config: dict, documents: list[str]) -> list[dict]:
results = []
for doc in documents:
clauses = extract_clauses(doc, config["clause_types"])
flagged = [c for c in clauses if c["risk"] >= config["risk_threshold"]]
results.extend(flagged)
export(results, fmt=config["output_format"])
notify(config["notify_channel"], summary=summarize(results))
return results
Now when a new customer arrives, you don't write new code — you add one YAML file. The bespoke work has been promoted into the product's configuration. That is what "from bespoke to platform" concretely looks like.
The Rule of Three: When NOT to Generalize
The most common mistake here is generalizing too early. The moment you see the first customer's request, you start building a grand abstraction, sure that "everyone will want this." And usually that abstraction is wrong. The second customer wants something subtly different from the first, and the hastily built abstraction becomes a shackle.
Hence an old piece of wisdom: the rule of three.
- First: Just build it. For this one customer, bespoke.
- Second: Build something similar again. But don't generalize yet. Instead, observe the commonalities and differences between the two cases.
- Third: When the same pattern shows up a third time, now abstract. With three real cases in hand, you know with evidence what is genuinely common and what is a per-customer variation.
The heart of the rule of three is that you build the abstraction grounded in data. An abstraction built by imagining from one case is a gamble; one built by observing three cases is a design. The little duplication of copy-pasting twice is far cheaper than the large debt of a wrong abstraction.
"Duplication is far cheaper than the wrong abstraction." — this adage rings especially heavy in an FDE's productization calls.
The Tension: Shipping Fast vs Building for All
At the heart of an FDE organization sits a fundamental tension.
- On one side is the customer in front of you. To close the deal this quarter, you have to solve this customer's problem right now. Speed is everything.
- On the other side is every future customer. If you build bespoke every time, the company doesn't scale. You have to build a reusable product.
These two collide often, because the fastest path for the current customer is hardcoding, while the best path for everyone is generalizing. Mature FDEs and organizations don't try to eliminate this tension — they consciously manage it.
- Win the customer first, but leave a trail: Solve it bespoke and fast this time, but leave a signal for product like "this is the third similar case."
- Hand generalization to product: If the FDE tries to also generalize in the field every time, they'll do neither well. The FDE spots the pattern and hands it off; product turns it into a product.
- Distinguish one-offs from repeats: Some customer needs really are unique to that customer. Those are right to leave bespoke, not generalized. The urge to productize everything is exactly what wrecks a product.
When NOT to Generalize
Separate from the rule of three, there are cases where generalization is not the answer from the start.
- When it's genuinely that customer's alone: A need bound to a specific regulation, a specific legacy system, or a specific org structure has no room to be reused by others. Forcing it into the product only makes the product complex.
- When you haven't seen the pattern yet: With only one case, you can't tell whether it's a pattern or a coincidence. Wait.
- When it isn't core value: You don't need to generalize every minor peripheral convenience. Concentrate resources on the product's central value.
- When the cost of generalizing exceeds the benefit: A flexible abstraction isn't free. You buy maintenance burden, complexity, and the risk of getting it wrong. For two or three customers, leaving it be may be better.
Great Products Are Born From Forward-Deployed Work
All of this converges on one conclusion: many great B2B products were born not from imagining at a desk, but from repetition in the customer's field.
The pattern goes like this. An FDE hand-solves the same problem across several customers, and in that repetition discovers the real demand. The discovery becomes a feature through config and abstraction, features stack into a product, the product becomes self-service, and finally customers can use it themselves without an FDE. Then the FDE moves on to the next unexplored problem.
This virtuous cycle is the ultimate purpose of the FDE model. FDEs work in the direction of making themselves unnecessary. What is done by hand today becomes tomorrow's product feature, and because that feature exists, the next customer no longer needs hands at all. Saving one customer grows into a product for thousands.
Wrapping Up
The road from bespoke to platform does not open automatically. It takes a living feedback loop between FDE and product, an eye that reads patterns across customers, a design sense that separates what varies from what doesn't, a rule of three that blocks premature generalization, and the maturity to consciously manage the tension between the current customer and future ones.
If there's a single message running through all three of these posts, it's this: the FDE is the bridge that solves real problems on the customer's front line and feeds that experience back as a product for everyone. Winning one customer with a throwaway prototype, then growing what you learned from that win into a platform — that is how forward-deployed engineering builds software.
References
- Sandi Metz, "The Wrong Abstraction": https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction
- Rule of Three (refactoring): https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)
- Martin Fowler, "Refactoring": https://martinfowler.com/books/refactoring.html
- Palantir, "A Day in the Life of a Forward Deployed Software Engineer": https://blog.palantir.com/a-day-in-the-life-of-a-palantir-forward-deployed-software-engineer-45ef2de257b1
- Paul Graham, "Do Things That Don't Scale": https://paulgraham.com/ds.html