← back to all posts

Dependencies are the new attack surface

28.05.2026 supply-chain agentic-coding tca pi-agent

For about fifteen years the dependency calculus was simple: someone else has solved the problem, their version is probably better than yours, and pulling it in costs you a line in a manifest file. That trade has quietly inverted. The cost of a bespoke library has fallen off a cliff thanks to agentic coding, and the cost of someone else's code — both the parts you read and the parts you don't — has been getting louder every quarter. I just made my own lighter, composable architecture library public on Codeberg: qija-tca. This post is partly about why.

// the tanstack incident

A few weeks ago OpenAI published their response to the TanStack npm supply chain attack. Short version: an attacker pushed a compromised version of a widely used package, and a meaningful percentage of the JS ecosystem — including a lot of AI-adjacent tooling — pulled it in transitively before anyone noticed. OpenAI's writeup is measured and well worth reading, but the headline is the bit that should make every engineering lead nervous: their build systems were exposed not because they took a dependency on TanStack, but because something they took a dependency on did.

That's the part you don't see when you run npm install or add a Swift Package. Your manifest file shows you a handful of names. The lockfile shows you hundreds, sometimes thousands. And every one of those is a maintainer account, an npm token, an exposed CI runner, a maintainer's laptop, a forgotten 2FA recovery code. Each transitive dependency is a separate trust relationship you didn't sign up for. The TanStack incident isn't an outlier — it's the same pattern as event-stream, ua-parser-js, node-ipc, the xz utils backdoor. The frequency is going up, not down.

// the economics flipped

The argument against rolling your own has always been time. Why build a state management library when Redux exists? Why build a composable architecture when TCA exists? Why build a date parser, a router, a form library, when twenty thousand engineers have already solved each of those for you? Time, expertise, edge cases, maintenance burden — the deck was stacked in favour of pulling in the dependency.

Agentic coding has rewritten that deck. The thing I would not have built in a week of solo evenings two years ago I can now sketch, implement, test, and harden in an afternoon with a structured pipeline. Not because the AI is doing the thinking for me — it isn't — but because the cost of going from a clear design to working, tested code has dropped by something like an order of magnitude. The design work is still mine. The taste is still mine. The decisions about what to include and what to leave out are still mine. What's gone is the rote keystroke cost that used to make "just use the library" the rational answer.

And on the other side of the ledger, what you keep is enormous. You keep the surface area you wrote. You keep the features you actually use, and none of the ones you don't. You keep control over the API, the naming, the error model. You keep the right to delete things. Most importantly: you keep your supply chain to one entry — you.

// qija-tca

qija-tca is the composable architecture I'm using inside Qija. It's not a fork of Point-Free's TCA — it's a lighter, narrower take on the same ideas, with the parts I actually use and none of the parts I don't. Reducers, stores, effects, a testing harness. It's small enough that I can hold the whole thing in my head, audit it in an afternoon, and modify it without having to read someone else's release notes. It builds in seconds and the test suite runs in milliseconds.

Two years ago I would not have written it. The cost-benefit didn't make sense. Today, it took less time to build and harden than I would have spent waiting for a single major-version migration of someone else's library. And every feature I add to it is one I needed; every line of code in it is one I read.

// the pi agent process

The reason this is actually viable — and not just a romantic justification for not-invented-here syndrome — is the process I use to build it. Inside Project Fiets, the other product I have in motion, there's a directory called .pi that holds a small set of scripts and prompts for what I call the pi agent process. The idea is simple: a single command spawns a work task, automatically branches off main, and walks the change through five stages.

  1. Plan — restate the task, identify the touched files, sketch the design.
  2. Review — sanity-check the plan against the codebase before writing a line of code.
  3. Implement — write the change, in the style of the codebase, with tests.
  4. Verify — run the tests, run the linter, run the build. No exceptions.
  5. Summarise — produce a tight PR description with the rationale and the diff highlights.

The pipeline is opinionated and the contract between stages is strict. Each stage produces a structured artefact the next stage consumes. The branch isolation means a failed run never touches main. The testing step is non-negotiable — if the tests don't pass, the run fails and nothing gets merged. That single rule is what makes the whole thing trustworthy at speed. Without it, you're moving fast and breaking things. With it, you're moving fast and the things you break are caught by the gate before they touch anything you care about.

The other thing the structure unlocks: you can use lighter, cheaper models. A vague prompt to a frontier model produces a vague answer. A tight, structured brief — with a plan, the relevant files, a clear acceptance test — produces a precise answer from a much smaller model. I've been running a lot of the implement stage on Deepseek v4 Flash, which is lightning fast and absurdly cheap. The frontier models still get to do the planning and the reviewing. The grunt work goes to the model that can do it in a second for a fraction of a cent. The pipeline is what makes that split possible.

// the bigger point

The combination of these two things — a process that makes bespoke code cheap and trustworthy, and a supply chain landscape that is getting more hostile by the quarter — is what convinces me that the default answer is shifting. Not for everything. You should still take a dependency on the things that are genuinely hard, genuinely stable, and genuinely audited. Use Foundation. Use SwiftUI. Use the platform.

But the layer above the platform — the architectural glue, the framework you build your app on top of — that's the layer where the calculus has actually flipped. A bespoke composable architecture is now cheaper to own than a third-party one is to depend on, once you count the audits you should have done and the migrations you'll have to do. You get fewer features and less risk. You get full control and no surprise breaking changes. You get a single supply chain entry: yourself.

For a product you intend to ship and maintain, that trade is no longer close.

— AM, Amsterdam, May 2026