First public release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
# BuildOrBuy
|
||||
|
||||
Compares building a capability against adopting one over a multi-year
|
||||
horizon, and checks the constraints that disqualify an option before it does
|
||||
any arithmetic.
|
||||
|
||||
## What it does and why
|
||||
|
||||
Build-or-buy decisions are usually made as build days against a licence fee.
|
||||
That framing hides the three things that most often decide the answer, so
|
||||
this library models each of them.
|
||||
|
||||
### A prohibited option is not an expensive one
|
||||
|
||||
Suppose a hosted vendor is cheaper, but its terms of service forbid the use
|
||||
you need. No cost comparison can give the right answer, because that option
|
||||
was never available. You record the constraint as a `blocker`. Blockers are
|
||||
checked before any cost is computed, and a blocked option is reported as
|
||||
`not-applicable`. Pricing it would imply it could win.
|
||||
|
||||
### Self-hosting a third-party tool is a build with a buy label
|
||||
|
||||
Running someone else's software yourself is not purchasing it. You take on
|
||||
backups, upgrades, capacity and on-call, and none of that shows up in a
|
||||
licence table. An `adopt-self-hosted` option that has not listed its
|
||||
`operationalSurface` is reported as not assessed and left out of the
|
||||
ranking.
|
||||
|
||||
### The operating cost dominates, and it is the number nobody fills in
|
||||
|
||||
Build cost is paid once and estimated with care. Operating cost is paid
|
||||
every year, for example the upkeep on an adopted tool or a usage bill that
|
||||
keeps running after the project that needed it has ended. Costs are totalled
|
||||
over `horizonYears`, and `operateDaysPerYear: null` makes an option
|
||||
uncostable instead of free. Treating that term as zero is how "adopt" wins
|
||||
on paper.
|
||||
|
||||
A recommendation made while any option is still unestimated is labelled
|
||||
provisional. When most of an option's cost is operating, the finding says
|
||||
so.
|
||||
|
||||
## A cost win is not the whole decision
|
||||
|
||||
The result is a cost comparison, not a verdict. Some reasons to build are
|
||||
not about cost: data custody, keeping a third party out of the critical
|
||||
path, a licence you cannot accept. If such a reason rules an option out,
|
||||
model it as a blocker so the option is removed. If it is a preference, weigh
|
||||
it in the decision made around this output.
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { compare, formatComparison, type Candidate } from '@extant2000/build-or-buy'
|
||||
|
||||
const candidates: Candidate[] = [
|
||||
{ name: 'in-house build', approach: 'build',
|
||||
costs: { buildDays: 20, operateDaysPerYear: 2 }, where: 'decision.md' },
|
||||
{ name: 'hosted service', approach: 'buy-hosted',
|
||||
costs: { buildDays: 1, operateDaysPerYear: 1, cashPerYear: 600 }, where: 'decision.md' },
|
||||
{ name: 'self-hosted tool', approach: 'adopt-self-hosted',
|
||||
costs: { buildDays: 3, operateDaysPerYear: 6 },
|
||||
operationalSurface: ['backups', 'upgrades', 'capacity', 'on-call'], where: 'decision.md' },
|
||||
{ name: 'vendor X', approach: 'buy-hosted',
|
||||
costs: { buildDays: 1, operateDaysPerYear: 0, cashPerYear: 300 },
|
||||
blockers: [{ reason: 'terms of service prohibit the intended use', where: 'vendor-terms.md' }],
|
||||
where: 'decision.md' },
|
||||
]
|
||||
|
||||
const cmp = { horizonYears: 3, dayRate: 800 }
|
||||
|
||||
const { winner, viable, disqualified, uncostable, reason } = compare(candidates, cmp)
|
||||
|
||||
// A report in the @extant2000/evidence-record format, rendered as text.
|
||||
console.log(formatComparison(candidates, cmp))
|
||||
```
|
||||
|
||||
`totalCost(candidate, cmp)` returns the horizon total, or `null` when an
|
||||
input is missing. `toReport()` returns the structured report that
|
||||
`formatComparison()` renders. Every finding cites the `where` of its
|
||||
candidate or blocker.
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
npm install @extant2000/build-or-buy
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT. See [LICENSE](LICENSE).
|
||||
Reference in New Issue
Block a user