Module 6: Moats And Defensibility
The engineer's role in building moats
Description
The six previous lessons treated an advantage's type as a given fact: someone tells you whether sellerNetwork has sides: 2, whether a switching cost reaches dataAndWorkflow, whether some data has feedbackLoop: true. This lesson asks the question the rest of the module took for granted: who decides those values? The answer, almost always, is an engineer — not the strategy team, not marketing. Whether feedbackLoop is true or false depends on whether someone built the pipeline that connects purchase data back to the recommendation engine. Whether a switching cost reaches dataAndWorkflow or stays at habit depends on how deep the integration is that the product team decided to build. This lesson makes that link explicit, with the module's most direct example: the same initiative, built with one architecture decision or another, crosses the moat threshold or doesn't.
Connection to the module. Lesson 6 closed the type-by-type tour by asking what curatedDiscoveryAlgorithm —or any feature— would need to stop being copyable in a weekend. This lesson answers that question with a complete case: the same recommendations initiative, built two ways, with two completely different moatScore results. It's the last type-focused lesson before lesson 8's project, which applies the whole model to Mercado's complete inventory.
An everyday analogy: the same blueprint, two different foundations
Imagine two builders separately given the same job: a house with an extra room on the roof, for the future. The first builds the complete house, adds the room on top, and leaves it there — fulfilling the job exactly as asked, and the house looks identical to the blueprint. The second, before raising the first wall, checks the foundation: if someone someday wants to add a full second floor above that room, would the current foundation support it? They decide to reinforce it from the start, with beams deeper than the blueprint called for — invisible work nobody notices looking at the finished house, because from the outside the two houses are indistinguishable.
Five years later, both owners decide to build the second floor. The first discovers the foundation needs to be demolished and rebuilt from scratch — a project almost as big as building the whole house again. The second simply builds on top: the foundation was already ready for that, from day one. Nobody who visited either finished house, in year one, could have guessed which one had the reinforced foundation — the difference wasn't in what was visible, it was in an engineering decision made before anyone could appreciate it. That's exactly what separates a feature from a moat: not what the user sees on launch day, but the architecture left underneath, ready or not to support something bigger.
Worked example: the same initiative, two architecture decisions
Mercado needs a recommendations engine — showing each buyer relevant products on the home page. Two teams, in two parallel scenarios, get exactly the same assignment. The first, recommendationsEngineStatic, solves it with fixed business rules: best-selling products in the category, featured deals, with no individual buyer data involved — it works well, looks professional, and a competitor with a decent team could replicate the same logic in about four weeks. The second, recommendationsEngineDataDriven, solves the same visible problem —recommendations on the home page— but builds, from the start, the pipeline that connects every purchase to the ranking of future recommendations: every time someone buys something, the system learns and adjusts what it shows similar buyers. From the outside, on launch day, both screens can look almost identical. Underneath, one is a feature; the other is, from the first commit, a data moat.
Alongside this pair, we add a second example of the same idea applied to a switching cost: sellerToolsShallowApi, a shallow integration that only lets sellers check their inventory in read-only mode, and sellerToolsDeepWorkflow, the same tool built deep enough for the seller to manage their entire operation there —the example already worked through in lesson 4. The decision of how deep to build that integration is, again, an architecture decision, not a business strategy one.
// Pedagogical model: scores the DURABILITY (0-10) of a competitive advantage.
// Reused unchanged from lesson 2 -- this lesson shows that 'type' and
// its parameters (feedbackLoop, depth) are architecture decisions, not
// fixed properties of a product initiative.
function moatScore(advantage) {
const { name, type } = advantage;
let durability;
let rationale;
switch (type) {
case 'networkEffect': {
const { sides, localDecay } = advantage;
durability = sides >= 2 ? 8 : 5;
if (localDecay) durability -= 3;
rationale = `network effect ${sides}-sided${localDecay ? ', with local decay' : ', no decay'}`;
break;
}
case 'switchingCost': {
const { depth } = advantage;
const depthScore = { contractual: 3, habit: 5, dataAndWorkflow: 8 };
durability = depthScore[depth] ?? 3;
rationale = `switching cost of depth '${depth}'`;
break;
}
case 'scaleEconomies': {
const { fixedCostShare } = advantage;
durability = Math.round(fixedCostShare * 10);
rationale = `economies of scale with ${Math.round(fixedCostShare * 100)}% fixed cost`;
break;
}
case 'dataMoat': {
const { feedbackLoop, uniqueToUs } = advantage;
durability = feedbackLoop ? 7 : 2;
if (feedbackLoop && uniqueToUs) durability += 2;
rationale = feedbackLoop
? `the data feeds a loop that improves the product${uniqueToUs ? ' and is exclusive' : ''}`
: 'the data accumulates but doesn\'t feed back into the product';
break;
}
case 'brand': {
const { pricingPower } = advantage;
durability = pricingPower ? 6 : 2;
rationale = pricingPower
? 'the brand changes purchase behavior (tolerates price or friction)'
: 'the brand is recognized but doesn\'t change purchase behavior';
break;
}
case 'feature': {
const { timeToCopyWeekends } = advantage;
durability = Math.max(0, Math.min(3, timeToCopyWeekends));
rationale = `feature copyable in ~${timeToCopyWeekends} weekend(s)`;
break;
}
default: {
durability = 0;
rationale = 'unknown advantage type';
}
}
durability = Math.max(0, Math.min(10, durability));
const verdict = durability >= 7 ? 'moat' : durability >= 4 ? 'weak-moat' : 'not-a-moat';
return { name, type, durability, verdict, rationale };
}
const candidates = [
{ name: 'recommendationsEngineStatic', type: 'feature', timeToCopyWeekends: 4 },
{ name: 'recommendationsEngineDataDriven', type: 'dataMoat', feedbackLoop: true, uniqueToUs: true },
{ name: 'sellerToolsShallowApi', type: 'switchingCost', depth: 'habit' },
{ name: 'sellerToolsDeepWorkflow', type: 'switchingCost', depth: 'dataAndWorkflow' },
];
console.log('=== The same initiative, two architecture decisions, two different moatScores ===\n');
console.table(candidates.map(moatScore));
What to expect. Running the file with Node produces exactly this output:
=== The same initiative, two architecture decisions, two different moatScores ===
┌─────────┬───────────────────────────────────┬─────────────────┬────────────┬──────────────┬───────────────────────────────────────────────────────────────────────┐
│ (index) │ name │ type │ durability │ verdict │ rationale │
├─────────┼───────────────────────────────────┼─────────────────┼────────────┼──────────────┼───────────────────────────────────────────────────────────────────────┤
│ 0 │ 'recommendationsEngineStatic' │ 'feature' │ 3 │ 'not-a-moat' │ 'feature copyable in ~4 weekend(s)' │
│ 1 │ 'recommendationsEngineDataDriven' │ 'dataMoat' │ 9 │ 'moat' │ 'the data feeds a loop that improves the product and is exclusive' │
│ 2 │ 'sellerToolsShallowApi' │ 'switchingCost' │ 5 │ 'weak-moat' │ "switching cost of depth 'habit'" │
│ 3 │ 'sellerToolsDeepWorkflow' │ 'switchingCost' │ 8 │ 'moat' │ "switching cost of depth 'dataAndWorkflow'" │
└─────────┴───────────────────────────────────┴─────────────────┴────────────┴──────────────┴───────────────────────────────────────────────────────────────────────┘
Row 0 and row 1 solve the same business problem —recommendations on the home page, the same pixel on the same screen— and get durability: 3 and durability: 9 respectively. Nobody on the strategy team asked the engineers to "build a data moat" or "build a copyable feature" — that distinction never appeared in any product document. It was decided, silently, by whoever wrote the pipeline: one connected the purchase event back to the ranking, the other didn't. Row 2 and row 3 tell the same story with switchingCost: the difference between durability: 5 and durability: 8 isn't a pricing strategy or a seller negotiation decision — it's how much integration depth the engineering team decided to build into the dashboard.
Deep dive: the moat gets decided in the pull request, not the strategy meeting
This is, perhaps, the most important point for you as an engineer in the entire module: the five moat types moatScore recognizes are almost never the result of an explicit strategy decision — they're the accumulated result of hundreds of architecture decisions, made one by one, sprint by sprint, almost always without anyone in the room using the word "moat." The recommendationsEngineDataDriven team probably didn't sit down to decide "let's build a data moat" — it decided, for good engineering reasons (better conversion, better user experience), to connect purchase data to the ranking. That decision, made for product reasons, had as a side effect —maybe not entirely intentional— crossing the moat threshold. The recommendationsEngineStatic team made, with the same good faith, a different decision: ship fast, with simple rules, without yet building the data pipeline. Neither decision was "wrong" at the time — but one built a reinforced foundation, and the other didn't.
This gives an engineer a concrete responsibility the rest of the module, focused on strategy vocabulary, doesn't always make explicit: when you design the architecture for a new feature, the questions "does this connect to a real data loop, or is it a fixed rule?", "is this integration read-only, or does it touch the user's whole operational workflow?", "does this network effect break at every new border, or does it compound?" aren't business strategy questions someone else is going to ask you — they're technical design questions you decide, often without anyone else in the organization realizing you're deciding them. Lesson 5 mentioned integration depth as a switching-cost lever; this lesson names it explicitly for what it is: an architecture decision with strategic consequences, made by an engineer.
It's also worth naming the limit of this responsibility, so as not to overstate it: an engineer decides an advantage's architecture —whether it connects to a real mechanism or not—, but doesn't decide, alone, whether that advantage should be built in the first place. That's module 7's question (strategicFilter): even architecture that produces a perfect data moat is a bad engineering bet if the whole initiative doesn't serve Mercado's strategy. The engineer's role is to maximize the durability of what's worth building — not to build every possible moat, without a filter.
Common mistakes
Treating architecture as an implementation detail with no strategic consequence. What happens: during a feature's technical design, the engineering team picks the simplest, fastest-to-ship solution —fixed rules instead of a data pipeline, a read-only API instead of a deep integration— without anyone in the conversation connecting that choice to the resulting durability, because "that's a business thing, not ours." Why it happens: engineering culture tends to separate "how we build it" from "why it matters for the business," when in the dataMoat and switchingCost types that separation doesn't exist — architecture is the strategy. How to spot it: if nobody asked, during a technical design review, "does this decision raise or lower the durability of what we're building?", the connection wasn't made. How to fix it: add that question explicitly to the design review of any initiative presented as differentiation or competitive advantage — not after building it, before.
Assuming the fast version can be "deepened later" at no cost. What happens: the team ships recommendationsEngineStatic with the stated intention of "connecting it to real data next quarter," and that quarter never comes, because there's always something more urgent on the roadmap — the "temporary" version becomes permanent. Why it happens: the fast version already works, is already in production, no longer generates complaints — and without a moatScore signal showing the gap between durability: 3 and durability: 9, there's no visible urgency to prioritize deepening it. How to spot it: check whether any feature shipped as "version 1, we'll deepen it later" has gone more than two quarters without the data-loop version. How to fix it: treat the durability gap between the fast version and the deep version as technical debt with a number attached —not a vague intention—, and prioritize it with the same discipline as any other technical debt that compounds over time.
Building the deepest possible integration into everything, with no filter. What happens: motivated by this very lesson, a team decides everything should have the deepest possible integration —every new feature must connect to a data loop, every tool must be dataAndWorkflow— without first asking whether that initiative even belongs to Mercado's strategy. Why it happens: once it's understood that architecture decides durability, it's tempting to maximize it everywhere, forgetting that deepening an integration also has real engineering cost, and not every initiative deserves that investment. How to spot it: if the team is spending weeks deepening the switchingCost of a feature that, to begin with, doesn't serve Mercado's vision or differentiation, the effort is misdirected. How to fix it: first apply the filter of which initiatives serve the strategy (module 7) and, only on those, invest in maximizing their architectural durability — deepening the wrong moat is no better than having no moat at all.
Exercises
Exercise 1 — Find the architecture decision. A colleague shows you two possible implementations of a seller review system: (a) reviews are shown on the seller's page, sorted by date; (b) reviews feed a trust score that automatically adjusts the order of search results. Which architecture decision produces higher durability, and which moatScore type would each correspond to?
See solution
Option (a) is essentially a presentation feature —showing data sorted by date— with no loop feeding back into the product; it would correspond to type: 'feature', with a durability that never exceeds 3 no matter how much design effort went into the screen. Option (b) connects the reviews to an active product decision —search ranking— so that every new review improves the relevance of future results; it would correspond to type: 'dataMoat' with feedbackLoop: true, with a durability of at least 7. The difference between the two isn't how good the reviews look — it's whether option (b) was, in fact, the architecture decision someone made when designing the system.
Exercise 2 — Predict before running it. Without running code, imagine the sellerToolsShallowApi team decides to add, on top of read-only inventory lookup, the ability for sellers to update their inventory and receive orders directly from the Mercado dashboard — that is, its depth changes from 'habit' to 'dataAndWorkflow'. Predict the new durability and verdict, and verify by running moatScore on the updated object.
See solution
With depthScore = { contractual: 3, habit: 5, dataAndWorkflow: 8 }, changing depth from 'habit' to 'dataAndWorkflow' moves durability from 5 to 8, and the verdict goes from 'weak-moat' to 'moat'. The point of the exercise: the only difference between the two results is a decision about how deep to build the integration — no change in the market, in price, or in Mercado's strategy had to happen for this advantage to cross the threshold.
Exercise 3 — Design the version with a moat. Mercado wants to launch a "products you might like" email notification system. Describe, in two or three sentences, how you'd build the version that produces the highest possible durability per this lesson's types —what data you'd connect, and which product decision it would feed back into—, and compare it to a fast version a rushed team would likely build.
See solution
There's no single correct answer. A fast version, of type 'feature', would send a weekly email with the best-selling products from categories the user has ever visited — a fixed rule, easy to copy in a few weekends. A version with a moat would connect every open, click, or purchase that results from those emails back into the model that decides which products to include next week —which type of message drove the most conversion for similar users—, closing a real learning loop: type: 'dataMoat', feedbackLoop: true. The difference in engineering effort between the two isn't huge —the email looks practically the same to the user in both cases—, but the difference in durability is the same one that separated recommendationsEngineStatic from recommendationsEngineDataDriven in the worked example: from 3 to 9.
Summary and next step
In this lesson you closed out the module's mechanics tour with the piece that makes the rest of it meaningful for you as an engineer: the five moat types aren't a strategy vocabulary handed down from above — they're the result of architecture decisions made in technical design, sprint by sprint, often without anyone using the word "moat" in the conversation. You saw, with the same business problem solved two ways, that the difference between durability: 3 and durability: 9 can fit inside a single data-pipeline decision, and that the same logic applies to how deep a switching-cost integration goes.
Before moving on you should be able to: explain, with your own example, how an architecture decision, not a business decision, determined the type or parameters of a real advantage you know, and distinguish the engineer's role (maximizing the durability of what gets built) from strategy's role (deciding what's worth building in the first place).
Lesson 8, the project that closes the module, runs moatScore on Mercado's complete inventory of candidate advantages —from the five real types to the control group— to separate, with the complete model, which are real moats and which are, still, door decoration.
Resources
- Ben Thompson, "Aggregation Theory" (Stratechery, 2015) — stratechery.com/2015/aggregation-theory. The framework explaining how product and architecture decisions —not just commercial negotiation— determined which internet companies ended up controlling the relationship with the end user. In English.
- Ben Thompson, "The Moat Map" (Stratechery, 2018) — stratechery.com/2018/the-moat-map. The same article cited in the module's introduction: the distinction between internalized and externalized network effects is, at bottom, a platform architecture decision, not just a business positioning one. In English.