Lesson 10 / 10
The Buy-or-Build Decision
Comparing a capability's two options by total cost of ownership: adding up the adaptation, integration edge, training, release upgrade, and exit line items separately, buying coming to 402 hours plus 114 a year against building's 606 hours plus 54 a year, the winner changing at a 3.40-year horizon, the breakeven horizon falling to 1.79 years once the cut risk of the eight points with an outside owner is added, and that same bound rising to 12.00 years as the product's built-in scope grows.
Contents
The previous lesson worked with what was already there: an unchangeable system stood, a layer was built around it, and its ownership was handed off step by step. That system had once either been bought or written in-house; every wrapper, coordination, and window cost paid today is a continuation of that old decision. This lesson tries to measure the decision itself.
In the regional library network’s fictional model, the management unit wants a reporting surface. There are two options: buying an outside product or writing it in-house. The decision goes unanswered if it is asked as “which is cheaper,” because cheapness is not defined without stating a time span.
Total Cost of Ownership Is Line by Line
A buy decision is often defended with a single number — the setup cost. But over a capability’s lifetime there are five separate line items it pays: two only once, two every year, and one paid on the way out.
IN20: the capability count, user count, and hour rates are fictional. IN21: exit cost is not measured in this lesson; it was measured in another lesson by scanning a source tree, and it enters here as a ready-made line item — 96 hours for the bought path, 24 for the built path. IN22: total cost of ownership is the sum of the once-paid line items plus the horizon times the yearly line items; exit is paid at the end of the horizon.
// decision.mjs — a capability's two options: buy or build. Every line item is computed from // model sizes; the exit item is taken as external input (IN21). const NEED = 18; // the report capability the management unit wants const USERS = 9; // people using the surface const SOURCE_EDGES = 5; // source systems to connect to // IN20: hour rates and sizes are fictional. const RATE = { setup: 40, adaptHours: 14, writeHours: 26, buyEdge: 22, buildEdge: 12, trainBuy: 4, trainBuild: 6, releaseCount: 2, releasePoint: 3, maintainCapability: 2.5, exitBuy: 96, exitBuild: 24 }; // One option's line items. "builtin": the number of capabilities the product already provides. function lineItems(builtin) { const adapted = NEED - builtin; const buyEdges = SOURCE_EDGES + 1; // the product wants its own copy of the data too const buy = { adapt: [RATE.setup + adapted * RATE.adaptHours, 0], "integration edge": [buyEdges * RATE.buyEdge, 0], train: [USERS * RATE.trainBuy, USERS * 2 * RATE.releaseCount], "release upgrade": [0, RATE.releaseCount * (adapted + buyEdges) * RATE.releasePoint], exit: [RATE.exitBuy, 0], }; const build = { adapt: [NEED * RATE.writeHours, 0], "integration edge": [SOURCE_EDGES * RATE.buildEdge, 0], train: [USERS * RATE.trainBuild, USERS], "release upgrade": [0, NEED * RATE.maintainCapability], exit: [RATE.exitBuild, 0], }; const sum = (o, i) => Object.values(o).reduce((t, v) => t + v[i], 0); return { buy, build, buyOnce: sum(buy, 0), buyYearly: sum(buy, 1), buildOnce: sum(build, 0), buildYearly: sum(build, 1), adapted, buyEdges }; } // IN22: total cost of ownership = paid once + horizon x yearly. const tco = (once, yearly, horizon) => once + horizon * yearly; // The horizon where two lines cross; negative means no crossing. const breakeven = (o1, y1, o2, y2) => (o1 - o2) / (y2 - y1); const m = lineItems(11); console.log(`need ${NEED} capabilities, the product provides ${NEED - m.adapted} of them built in, ` + `${m.adapted} will be adapted; edges: buy ${m.buyEdges}, build ${SOURCE_EDGES}\n`); console.log("item buy (once) buy (yearly) build (once) build (yearly)"); console.log("------------------ ------------ ------------ ------------------- ------------------"); for (const name of Object.keys(m.buy)) console.log(`${name.padEnd(18)} ${m.buy[name][0].toFixed(0).padStart(12)} ${m.buy[name][1].toFixed(0).padStart(12)} ` + `${m.build[name][0].toFixed(0).padStart(19)} ${m.build[name][1].toFixed(0).padStart(18)}`); console.log(`${"total".padEnd(18)} ${m.buyOnce.toFixed(0).padStart(12)} ${m.buyYearly.toFixed(0).padStart(12)} ` + `${m.buildOnce.toFixed(0).padStart(19)} ${m.buildYearly.toFixed(0).padStart(18)}`); // IN23: a point with an outside owner is cut with probability 0.12 a year, an inside owner with 0.20. // The outside path exists only in the "buy" option; the inside path (source systems) exists in both. const OUTSIDE = 0.12, INSIDE = 0.20; const outsideCost = RATE.exitBuy + (RATE.setup + m.adapted * RATE.adaptHours) + m.buyEdges * RATE.buyEdge; const insideCostBuy = SOURCE_EDGES * RATE.buyEdge, insideCostBuild = SOURCE_EDGES * RATE.buildEdge; const buyYearlyRisk = m.buyYearly + OUTSIDE * outsideCost + INSIDE * insideCostBuy; const buildYearlyRisk = m.buildYearly + INSIDE * insideCostBuild; console.log("\nhorizon (yrs) buy build winner diff buy (risky) build (risky) winner"); console.log("------------- ------- --------- -------- ------ ------------- -------------- --------"); for (const horizon of [1, 2, 3, 4, 5, 8]) { const buy = tco(m.buyOnce, m.buyYearly, horizon), build = tco(m.buildOnce, m.buildYearly, horizon); const buyRisk = tco(m.buyOnce, buyYearlyRisk, horizon), buildRisk = tco(m.buildOnce, buildYearlyRisk, horizon); console.log(`${String(horizon).padStart(13)} ${buy.toFixed(0).padStart(7)} ${build.toFixed(0).padStart(9)} ` + `${(buy < build ? "buy" : "build").padStart(8)} ${Math.abs(buy - build).toFixed(0).padStart(6)} ` + `${buyRisk.toFixed(0).padStart(13)} ${buildRisk.toFixed(0).padStart(14)} ${(buyRisk < buildRisk ? "buy" : "build").padStart(8)}`); } console.log(`\npoints with an outside owner: buy ${m.adapted + 1}, build 0; ` + `edges with an inside owner: ${SOURCE_EDGES} in both options`); console.log(`cut cost: outside ${outsideCost} hours (buy), inside ${insideCostBuy} / ${insideCostBuild} hours; ` + `yearly expected buy ${(OUTSIDE * outsideCost + INSIDE * insideCostBuy).toFixed(1)}, build ${(INSIDE * insideCostBuild).toFixed(1)} hours`); console.log(`breakeven horizon: risk-free ${breakeven(m.buyOnce, m.buyYearly, m.buildOnce, m.buildYearly).toFixed(2)} years, ` + `risky ${breakeven(m.buyOnce, buyYearlyRisk, m.buildOnce, buildYearlyRisk).toFixed(2)} years`); console.log("\nbuiltin adapted buy (once) buy (yearly) breakeven (yrs) winner at 5 yrs"); console.log("-------- --------- ------------ ------------ ------------------ -----------------"); for (const builtin of [6, 9, 11, 14, 17]) { const s = lineItems(builtin); const breakevenYears = breakeven(s.buyOnce, s.buyYearly, s.buildOnce, s.buildYearly); const winner = tco(s.buyOnce, s.buyYearly, 5) < tco(s.buildOnce, s.buildYearly, 5) ? "buy" : "build"; console.log(`${String(builtin).padStart(7)} ${String(s.adapted).padStart(9)} ` + `${s.buyOnce.toFixed(0).padStart(12)} ${s.buyYearly.toFixed(0).padStart(12)} ${breakevenYears.toFixed(2).padStart(18)} ` + `${winner.padStart(17)}`); }
need 18 capabilities, the product provides 11 of them built in, 7 will be adapted; edges: buy 6, build 5
item buy (once) buy (yearly) build (once) build (yearly)
------------------ ------------ ------------ ------------------- ------------------
adapt 138 0 468 0
integration edge 132 0 60 0
train 36 36 54 9
release upgrade 0 78 0 45
exit 96 0 24 0
total 402 114 606 54
horizon (yrs) buy build winner diff buy (risky) build (risky) winner
------------- ------- --------- -------- ------ ------------- -------------- --------
1 516 660 buy 144 582 672 buy
2 630 714 buy 84 762 738 build
3 744 768 buy 24 942 804 build
4 858 822 build 36 1122 870 build
5 972 876 build 96 1302 936 build
8 1314 1038 build 276 1841 1134 build
points with an outside owner: buy 8, build 0; edges with an inside owner: 5 in both options
cut cost: outside 366 hours (buy), inside 110 / 60 hours; yearly expected buy 65.9, build 12.0 hours
breakeven horizon: risk-free 3.40 years, risky 1.79 years
builtin adapted buy (once) buy (yearly) breakeven (yrs) winner at 5 yrs
-------- --------- ------------ ------------ ------------------ -----------------
6 12 472 144 1.49 build
9 9 430 126 2.44 build
11 7 402 114 3.40 build
14 4 360 96 5.86 buy
17 1 318 78 12.00 buy
The Winner Depends on the Time Horizon
The first table splits the two options into five line items and shows the items pointing in different directions. Buying is about a third cheaper on the once-paid side (402 against 606 hours) but more than twice as expensive on the yearly side (114 against 54 hours). Two items produce this difference. The release-upgrade item is 78 hours for buying, 45 for building; the number for buying does not depend on the product’s own release calendar but on how many points that calendar touches — seven adaptation points and six edges. If adaptation drops, upgrades get cheaper too; as adaptation grows, every release costs more.
The edge item carries the second difference. The bought path connects not to five sources but to six, because the product wants its own copy of the data; and since every edge needs its own adapter, the cost per edge is 22 hours against 12 hours for the in-house build. The same five sources come to 132 hours against 60 across the two options.
The second table follows these two lines across the horizon. At a one-year horizon, buying is ahead by 144 hours; at three years the difference falls to 24 hours; in the fourth year, building takes the lead. The crossing point is exactly 3.40 years. The 24-hour difference in the three-year row is small enough that choosing one of the rates slightly differently would change the winner; a document defending the decision has to write down not just a single winner’s name but the horizon and the difference too.
Dependency Risk Is a Line Item Too
The math so far assumes both paths run without a hitch. At enterprise scale, this assumption does not hold: if a path’s owner is someone else, that path can be cut, and the cost of the cut can be measured.
IN23: a path with an outside owner is cut with probability 0.12 a year, a path with an inside owner with 0.20. The bought path has eight points with an outside owner: seven adaptation points and the edge for the product’s own data copy. The cost of cutting these is exit plus re-adaptation plus edges, that is, 366 hours. The built path has no outside owner; in both options, the five source edges are owned by other teams, and their cut cost is 110 hours for buying, 60 for building.
The yearly expected cost comes to 65.9 hours for buying, 12.0 for building. When these two numbers are added on top of the yearly line item, the breakeven horizon falls from 3.40 years to 1.79 years: the option that looked like the winner for three years without accounting for risk starts losing from the second year once risk is included. Dependency risk is not a warning sentence, it is a yearly line item; until it is turned into a line item, it cannot enter the decision.
The last table shows what variable the decision actually depends on. When the capability the product provides built in rises from 6 to 17, the breakeven horizon stretches from 1.49 years to 12.00 years, and the winner at the five-year horizon changes. The difference between the two options is not in their names — it is in how much of the need the product already covers out of the box. As scope rises, the adaptation points fall; as adaptation falls, both the once-paid and the yearly release item drop; the same math, with the same rates, turns the other way.
Summary
- Total cost of ownership is five line items: buying 402 hours plus 114 a year, building 606 hours plus 54 a year; the option that is cheaper once-paid is more expensive yearly (IN20, IN22).
- The release-upgrade item depends not on the product’s calendar but on the number of adapted points: seven adaptation points and six edges come to 78 hours a year, while maintaining the in-house build costs 45.
- The bought path connects not to five sources but to six, and pays 22 hours per edge; the same sources cost 60 hours in the in-house build. A product’s own copy of the data is an edge line item.
- The winner depends on the horizon: breakeven at 3.40 years, and the difference at the three-year horizon is only 24 hours. A document defending the decision must write down the horizon and the difference along with the winner’s name.
- Dependency risk is a yearly line item. The eight points with an outside owner and their 366-hour cut cost come to 65.9 hours a year; once this item is added, the breakeven horizon falls to 1.79 years (IN21, IN23).
Course Wrap-Up
Over the course, the same fictional network was modeled ten times, and every lesson read a different quantity off the same network.
| Lesson | Modeled enterprise object | Measured quantity | Cost of the decision |
|---|---|---|---|
| The Enterprise Architecture Concept | 9 systems, 6 owners, 13 capabilities, 10 data entities | Alignment 11/13; 2 gaps, 6 overlaps, 1 orphan system; 20 of 23 directed edges cross an ownership boundary | 5 of the 6 overlaps spread across two separate owners and require 5 reconciliations |
| Frameworks | Layer, view, governance cycle, maturity level | The detailed form answers 10 of 12 questions with 106 items, the light form answers 5 with 22 items | 16.8 items per additional answer; maintenance per change is 18 items against 2 |
| Business Process Modeling | A 16-step process; actor, input set, output | 10 steps automated, 4 a human decision; 8 actors, 5 owners, the owner changes in 9 of 10 handoffs | Pushing the boundary gains 62 touches but produces 10 wrong decisions and 30 rollbacks; breakeven at 6.20 touches |
| Capability Mapping | Capability, the system serving it, the data written, the chain of reading systems | 11 of 13 capabilities are traceable; the short chain shows 1.55 systems, the full chain 4.73 | Coordination rounds rise from 5 to 29; 67 percent of the systems to touch do not show up in the first ring |
| Integration Patterns | A single edge, built with four different patterns | With remote calls, 100 source requests for a hundred queries, 0 for the others; freshness windows of 0 and 1 tick | When the source renames a single column, 3 of 5 edges silently return the wrong answer, and coordination is needed with 2 of 3 owners |
| The Enterprise Service Bus | Nine flows, in a pairwise and a centralized layout | 9 against 8 units written; the hub processes 24 messages for 6 events; with the hub down, 0 of 18 deliveries go through | The seventh system drops from 5 units and 4 owners to 2 units and 2 owners; in exchange, 4 rules with an outside owner pile up in the hub |
| Master Data Management | A member record held in three systems, and the golden record | 720 write events produce 131 conflicting (member, field) pairs in the distributed layout, 95 with the golden record | Remaining inconsistency is 48 in both layouts; write steps rise from 1.00 to 1.61, and with the source system down, accepted writes fall from 496/720 to 238/720 |
| The Data Warehouse and Transformation Pipelines | Five reports, four sources, an eight-step transformation pipeline | 163 queries and 5,354,308 records read per day; scanning across periods, operational load at 90.2, 27.2, and 79.2 percent | A single field change breaks one step but drops all five of the five reports; the report with a one-hour freshness requirement never makes it to the warehouse at any period |
| Working With Legacy Systems | A record system whose source code is unreachable, and its wrapper | 9 of 16 (caller, operation) pairs close, 71.8 percent of calls; 7 pairs and 16,927 calls stay outside | Coordination falls from 24 steps to 13, but the co-written window does not go below 30 days because of the slowest owner’s cycle |
| The Buy-or-Build Decision | A capability’s two options and five cost line items | Buying 402 hours plus 114 a year, building 606 plus 54 a year; breakeven at 3.40 years | The 8 points with an outside owner bring an expected cost of 65.9 hours a year, and the breakeven horizon falls to 1.79 years |
The ten lessons’ shared rule is a single sentence: at enterprise scale, the unit of measure is the edge. Inside a single system, the unit of measure was a call, a record, or a second. At enterprise scale none of these carries a decision by itself; in every table, what decided things was the number and direction of edges, which ownership boundary an edge crossed, and how many owners a change called into coordination. The wrapper’s scope, the choice of golden record, and the warehouse’s period were all bounded in the same place: at the owner its own decision could not reach.
The gap the course leaves is this. Edges were built and counted, ownership was separated, buying was told apart from building — but none of these decisions said which quality attribute they were protecting. What was a centralized layout chosen for: availability, changeability, consistency? And no mechanism was built to check whether decisions stayed on paper or kept holding as they were made. The next course, M20/K04 Quality Attributes and Governance, opens with these two questions.
To keep your progress and take notes, Log in
My notes
Log in to take notes.