The Library Is Free. The Risk Isn't.
How I now choose open-source dependencies for anything we plan to run for years — license, ownership, and the cost of leaving, before features.
For a long time, my process for picking an open-source library looked like most developers’ process. Does it solve the problem? Is it popular? Is the documentation decent? Does it have recent commits? If the answer to all four was yes, we used it.
That process works fine for a weekend project. For software a business will run for ten years, I now think it is missing the most important questions. The library is free to download. It is not free to depend on.
This post is about how my thinking changed, and the checklist I use now — most recently when we designed a developer CLI for MSG91.
The questions I used to skip
When you add a dependency to a long-lived product, you are not just importing code. You are entering a relationship with whoever controls that code, under whatever terms they choose, for as long as you use it. Three things about that relationship matter more than features:
- What does the license allow — today and tomorrow?
- Who actually owns the project, and what are their incentives?
- How expensive would it be to leave?
None of these show up in a GitHub star count.
License is a business term, not a legal footnote
Most developers know the broad categories: permissive licenses like MIT and Apache 2.0, copyleft licenses like GPL and AGPL, and the newer “source-available” licenses that look open but restrict commercial use.
What took me longer to appreciate is that a license can change. Over the last several years, a number of well-known infrastructure projects moved from permissive licenses to more restrictive ones once their parent companies needed to protect a business. Users who had built on them suddenly had to choose between accepting new terms, staying on an old version forever, or migrating to a fork.
The code you already downloaded keeps its old license. But your future — security fixes, new features, compatibility with the rest of the ecosystem — follows the new one.
So the question is not only “is this license acceptable?” It is “how likely is it that this license changes, and what would I do if it did?”
Some rules of thumb I use:
- MIT and Apache 2.0 are the default comfort zone for anything we embed in a product or ship to customers. Apache 2.0 has the added benefit of an explicit patent grant.
- AGPL in a network service needs a deliberate decision, not an accident. It may be perfectly fine for an internal tool and a serious problem inside a product.
- “Open core” projects deserve extra attention. If the free version exists mainly to sell the paid version, ask which features you will need in two years and which side of the line they live on.
- Read the license of the model, not just the code. In AI work especially, the weights often carry different terms from the inference code. When we evaluated self-hosted speech models for voice features, Apache 2.0 weights were a hard requirement, and that ruled out several models that looked great on benchmarks.
Who owns it matters as much as what it says
Two projects can carry the same license and represent very different risks.
A project governed by a broad community or a neutral foundation, with many maintainers from different companies, is hard to take in a direction its users do not want. A project owned by a single company is only as stable as that company’s strategy. If the project stops serving the owner’s business, it may be deprioritised, relicensed, or quietly abandoned — and none of that requires anyone to act in bad faith.
This was the deciding factor when we chose a framework for the MSG91 CLI. There were two serious options in the Node.js world. One was a larger, more feature-complete framework owned by a big enterprise software company. The other was smaller, MIT-licensed and community-maintained. On features alone, the bigger framework was arguably better.
We picked the community-owned one anyway. The features we gave up, we could write ourselves. The risk we avoided — our developer tooling depending on another company’s roadmap — we could not fix later.
The real cost is the exit cost
The most useful question I have learned to ask is: “If we had to remove this dependency in a year, how painful would it be?”
If the answer is “a day of work,” you can be relaxed about almost everything else. If the answer is “we would have to rewrite the product,” you should be very strict.
Exit cost is something you can design. A few patterns I now push for:
Own the thin layer that matters. For the CLI, instead of adopting a heavy plugin system from a framework, we chose to write a small plugin dispatcher ourselves — a few hundred lines we fully own and understand. The framework handles argument parsing, which is easy to replace. The architecture — how product teams plug in — belongs to us.
Put a seam between your code and the dependency. Wrap third-party clients behind your own interface. If every part of your code calls the vendor SDK directly, every part of your code has to change when you leave. If they call your wrapper, only the wrapper does.
Make your most important asset vendor-neutral. For the CLI, we realised the most valuable thing was not any library at all — it was a clean, canonical OpenAPI specification of our APIs. A good spec can generate documentation, SDKs, tests and CLI commands, with any tooling you like. Investing there first means every tooling decision after it becomes cheaper to reverse.
Prefer standards over products. Open formats, open protocols, open specs. When the dependency is a standard, there is usually more than one implementation, and switching is a migration rather than a rewrite.
Self-hosting is the same question at a bigger scale
The same thinking applies to infrastructure. Managed services are wonderful until the bill, the terms, or the product changes. I am not against managed services — we use plenty of them — but for things at the core of the product, I prefer to run them on infrastructure we control, or at least make sure we could.
That is not ideology. It is the same exit-cost question. If a managed service is easy to leave, use it freely. If it would trap you, either self-host, or design the seam so you are not trapped.
My checklist now
When a team proposes a new dependency for something long-lived, these are the questions I ask:
- What is the license of the code — and of any models, data or assets it ships with?
- Has this project, or projects from the same owner, ever changed license?
- Is it owned by one company or governed by a community or foundation?
- How many active maintainers are there, and who employs them?
- If we had to remove it in a year, what would that cost?
- Can we put our own interface in front of it?
- Is there a standard underneath it that we can depend on instead?
It takes ten minutes. It has changed several decisions.
The trade-off, stated honestly
This approach is not free. Sometimes you pick the less polished library. Sometimes you write a few hundred lines that a framework would have given you. Sometimes you run infrastructure that a managed service would have handled.
I think that trade is worth it for anything you plan to live with for years. Features are visible on day one. Dependency risk shows up in year three, usually at the worst possible time. The best moment to deal with it is before you write the first import.