Why Sigma has no dependencies.

On owning your interface instead of inheriting someone else’s tree.

The go.mod for Sigma has no require block. There is a module path, there is a Go version, and that is the entire file. Nothing else, because there is nothing else to require. I want to talk about why that is deliberate, why it took a few genuinely bad experiences to get there, and why I think the calculation behind it has quietly flipped over the last couple of years.

Most things that call themselves AI libraries are a thin layer of convenience wrapped around one provider’s SDK, and that SDK drags in a transitive tree of its own. You import the friendly client, and you silently adopt everything underneath it: the JSON helper somebody liked in 2022, the retry package with the clever generics, the telemetry library you did not ask for and cannot easily remove. None of that is your code, none of it is on your release schedule, and all of it becomes your problem the moment something in the chain goes stale or sprouts a CVE.

I want to be specific about where this conviction comes from, because it is not theoretical. Some of the worst Go libraries I have had the misfortune of importing came from AI vendors, and the worst by a comfortable margin was Anthropic’s. The API on the surface was fine. The rot was underneath: dated dependencies that never got rectified the entire time I relied on the thing. You inherit the whole tree, including the parts the vendor has plainly stopped caring about, and short of leaving there is nothing you can do about it.

The part that still amuses me is the timing. This was the same Anthropic loudly announcing that Mythos Preview could autonomously hunt down zero-day vulnerabilities in everyone else’s software, briefing governments about flaws that had been sitting in critical systems for decades, generally appointing itself the last word on cybersecurity. Meanwhile the dependency tree under their own Go SDK was quietly going off and nobody had got round to it. There is a lesson about supply-chain hygiene in there that you might expect a security-first vendor to internalise before lecturing the rest of us. So I left. Sigma has no dependency tree to neglect, which is the entire point.

Sigma is standard library only. net/http makes the calls, encoding/json does the marshalling, context handles cancellation, and that is more or less the full cast list. Not “a handful of carefully chosen dependencies”, not “lightweight”, none at all. The audit surface is the code I wrote plus the Go standard library, and the standard library is maintained by people whose entire job is not breaking it. The test suite is deterministic and touches no network, because the test provider scripts its responses in memory. Build it today, build it again in a year, and you get the same artefact, because there is no upstream left to drift underneath you.

The dependency story is really a symptom of the thing I actually care about, which is neutrality. The interface in Sigma is mine. A model and a provider get registered against a registry, the client looks them up, and the provider is simply the part that knows how to turn my request into that vendor’s wire format and stream the events back as an assistant message. The provider is an implementation detail sitting behind my abstraction, rather than my abstraction being a polite veneer over somebody’s SDK. Swapping one provider’s API for a custom OpenAI-compatible endpoint, or pointing at a different provider entirely, is a registration change, not a rewrite.

That matters more than it sounds, because in the work I actually get paid for, the choice of provider is frequently not mine to make. When data has to stay in a particular jurisdiction, the provider you are allowed to use is decided by where the bytes are permitted to live, and that decision arrives from outside, from a regulator or a customer’s procurement team, with no interest whatsoever in what you imported eighteen months ago. I have spent real time establishing that running a particular frontier model under Australian data-residency rules meant one specific path and no other. The constraint is not a preference you can argue with, it is a wall. And there is no worse time to learn how deep the SDK is wired into your codebase than the day someone tells you the data is never leaving the region.

I should be honest about what this costs, because a post that only lists the upsides is marketing. You write the wire format yourself. When a provider changes their streaming protocol, that is your afternoon, not theirs. You forgo the ergonomic sugar the vendor ships, the helper that does the obvious thing in one line. You own the retries, the typed errors, the cancellation, the diagnostic redaction, all the unglamorous plumbing an SDK would have handed you for free. For plenty of projects that is a bad trade, and you should just import the SDK and get on with your life. I am not pretending otherwise.

What changed is the half-life. A few years ago you picked a provider and mostly stayed put, so coupling yourself to their SDK was a one-time cost amortised over years. Now the frontier moves every few weeks, the leading model carries a different name every quarter, and regulation keeps rearranging which providers you are even allowed to call. In that world the SDK is the fastest-rotting part of the stack, and bolting yourself to one is a liability dressed up as convenience. Owning a thin, neutral, dependency-free interface is no longer purism, it is the pragmatic position. And the pragmatism is not a bet on which vendor comes out on top, because the entire point is that I do not have to place one. A neutral interface means I support all of them, every provider and every model behind a single consistent surface. That is what gives our customers a real choice of model rather than whichever one we happened to wire in, and it is what turns a data-sovereignty or compliance requirement from a migration into an input the system already knows how to take. The bytes that may only live in one region, the customer who has already settled on a provider they trust: a neutral interface treats both as ordinary configuration rather than a crisis.

A module path and a Go version. I have come to regard it as a feature.

Sigma is MIT-licensed and lives at github.com/wintermi/sigma.

Originally published on linkedin.com.