The bill that quietly ends a project rarely arrives in year one. It arrives in year three, when a dependency the client did not know they had renews, changes terms, or disappears. The build rule below is how we keep that bill from being the client's problem.
What a dark dependency is
A dark dependency is anything the deliverable needs to keep working that the client cannot see, name, and replace. Three tests, all three required. If they can see it in a diagram but cannot name the contract, it is dark. If they can name it but cannot replace it without rebuilding the whole thing, it is dark. If they can replace it only by signing a new contract with the same vendor under a different name, it is dark.
We treat this as a build-time rule. By the time a dependency goes dark in production, it is too late to make it visible cheaply (Class C — observable in the engagement's dependency list and runbook).
Patterns we have refused on real engagements
These are not hypotheticals. Each pattern below is one we have walked into on intake calls and declined to ship.
The bundled orchestration layer. A platform offers a friendly UI for chaining models and tools. The chains live in their database. Export produces a JSON the platform itself can read and nothing else can. We refused this on a content-ops engagement and shipped plain files in the client's own repository, with a runbook for executing them outside any platform.
The vector database with no portability path. The hosted vector DB has a fine API and no documented way to dump embeddings, metadata, and index parameters in a form another store can ingest without re-embedding everything. Re-embedding is the trap — at scale it is the cost of the entire project. We ran a self-hosted store on the client's infrastructure, with an export procedure tested before sign-off.
The "free" identity provider with a contractual ceiling. Free up to N users, then tier jumps. The contract does not let the client export the user table with password hashes. Leaving means every user has to reset their password. The cost of leaving is not money — it is a one-time hit to the client's relationship with their users that no money refunds.
The model behind a name, not a version. A vendor exposes "their best model" via an API. The model behind the name changes silently. The deliverable drifts and nobody can tell whether it is the prompt, the data, or the model that moved. We pin model versions in the contract and write the falsifier into the runbook: if the pinned version is deprecated and no equivalent is available, the engagement re-opens at our cost.
The hosting account with the deployment scripts inside. The Terraform, the Helm charts, the CI workflows all live inside the vendor's account, exportable as screenshots. We refused this two months ago. The replacement: every deployment artifact lives in a repository the client owns, with a runbook that walks a fresh engineer from clone to production in under an hour. We test that walkthrough with someone who was not on the build.
The rule, written down
For every engagement, before any code ships, the dependency list is fully populated. Three columns, no exceptions:
- What it is. The exact service, library, or contract — with version numbers, edition tiers, and the line of code or config that consumes it.
- What it costs. Monthly estimate at current usage, the tier-change threshold above current usage, and the line item the client will see on the invoice.
- How to replace it. Named substitute, expected cost delta, a one-paragraph migration note, and the falsifier that says the substitute will not work for this case (so the client knows ahead of time which dependencies are genuinely load-bearing).
The list is in the ledger. The ledger is the client's. We do not ship deliverables without it.
Why year three is the right horizon
Themesis has a useful piece on the longer horizon — the gap between the systems we have now and the genuinely autonomous ones we may eventually have, and what to prepare for in the meantime: How to Prepare for the AGI World: Tools, Pseudo-Persons, and Slaves (Class E). Our one-line frame, in our voice: the period between now and whatever comes next is exactly when extractive contracts get signed at scale, because everyone is moving fast and nobody is reading the dependency list. The piece is worth your time before your next vendor call.
The point of the no-dark-dependencies rule is not paranoia about the future. It is that the contracts being signed in 2026 will outlive the platforms they were signed on top of, and the client who owns their dependency list will outlive the contract.
What to ask your current vendor
If you have a project running today, three questions are sufficient to find the dark dependencies:
- Show me the dependency list. Three columns: what, what it costs, how to replace it.
- Pick one dependency. Walk me through replacing it without you in the room.
- Which dependency, if it disappeared tomorrow, would end this project?
A vendor who cannot answer the third question on the spot is not necessarily extractive — but they have a dark dependency they have not surfaced, and you are the one carrying the risk.
