Engineering
How to Evaluate an Open-Source Library Before You Commit — A Framework for Teams of 1–10
Choosing the wrong dependency costs weeks of rewrites. This framework — community pulse, maintenance signals, API stability, and licensing — is how we decide what enters our codebase.
Adding a dependency is easy. npm install takes 12 seconds. Removing it six months later, after it's wired into 40 files with undocumented side effects, takes two weeks. Small teams feel this more acutely — there's no dedicated architecture review board. One engineer's Friday-afternoon decision becomes the team's Q2 liability.
We evaluate roughly 8-10 libraries a month at Krypton Forge. Here's the framework we've settled on. It's opinionated because the alternative — reading GitHub stars and hoping — doesn't work.
The Three-Question Gate
Before any deep evaluation, three yes/no questions:
Does this library solve a problem we actually have? Not "might have in six months." Not "sounds useful." Right now, today, is there a concrete pain point this resolves?
Could we build the subset we need in under two days? If yes, write it. Owned code has zero maintenance risk, zero breaking-change surprise, and zero license ambiguity. The bar for "build vs. buy" at small-team scale is lower than most engineers think.
Is the library's scope bounded or sprawling? A library that does one thing is maintainable. A library that started as a form validator and now includes a router, a state manager, and an animation framework is a migration waiting to happen.
Signal 1: Maintenance Pulse
We check five data points in GitHub, in this order:
| Signal | Healthy | Warning | Red Flag |
|---|---|---|---|
| Last commit | < 3 months | 3–6 months | > 1 year |
| Issue response | Most issues answered within a week | Answers sporadically | Issue list is a graveyard |
| PR merge rate | External PRs merged regularly | Only maintainer PRs | Open PRs older than 6 months |
| Release cadence | Regular (weekly to monthly) | Erratic bursts | No releases in > 6 months |
| Bus factor | 3+ active contributors | 2 contributors | Solo maintainer with no succession |
We learned the bus-factor lesson the hard way. A form library we depended on for Paraslace had a solo maintainer who stopped responding in Q3 2025. The library wasn't broken — but one security advisory and no one to patch it. We migrated off it over three weekends. Now, solo-maintainer libraries get extra scrutiny: is the maintainer employed by a company that funds the work? Is there a clear fork path?
Signal 2: API Stability
Look at the changelog for the last 12 months. Count the breaking changes. A library that ships breaking changes quarterly is a library whose authors don't consider downstream stability their problem.
Good signs:
- Semantic versioning that actually means something (unlike half the npm ecosystem)
- Deprecation warnings before removal (ideally one major version of notice)
- Migration guides for major versions
Bad signs:
- "Refactored internal architecture" in a minor version bump
- Breaking changes listed under "Bug fixes"
- Changelogs that are just commit messages with emojis
We also check how the library handles its own dependencies. If it pins exact versions of transitive deps, expect dependency hell when you integrate it. If it declares broad ranges and tests against multiple versions, it's maintained by someone who understands the ecosystem.
Signal 3: The Escape Hatch
Every dependency needs an exit plan. Before committing, answer: if we had to rip this out in Q4, what would the migration look like?
Libraries with clean interfaces make migration feasible. Libraries that require you to wrap every component in their Provider, use their custom routing, and adopt their CSS-in-JS solution make migration a rewrite.
The escape hatch test: can you replace the library with a different implementation behind the same interface? If the answer is no, the library isn't a dependency — it's a framework, and you should evaluate it as such.
Good escape hatches:
date-fns— swap forluxonordayjs, same function signaturespg(node-postgres) — any Postgres client has similar interfaceszod— validation libraries share API patterns
Bad escape hatches:
- Next.js — replacing it means rewriting routing, SSR, and build pipeline
- Prisma — custom schema DSL, generated client; migration means rewriting queries
- Tailwind — utility classes embedded everywhere; extraction is labor
Bad escape hatches aren't disqualifying. They just require different evaluation criteria. A framework decision is an architectural decision, not a dependency decision.
Signal 4: License and Governance
MIT/Apache 2.0: green. GPL: depends on your distribution model (SaaS is fine; on-prem distribution gets complicated). AGPL: read carefully — it applies to network use. BUSL/SSPL: understand the restrictions. Anything custom: have a lawyer read it.
Also check: does the project have a CLA (Contributor License Agreement)? Who holds the copyright? Has the project changed licenses mid-stream? A project that relicensed from MIT to BUSL once can do it again.
The Decision Matrix
After evaluation, every library falls into one of four buckets:
- Adopt. Healthy maintenance, stable API, acceptable escape hatch. Use it.
- Adopt with wrapper. Use it but behind a thin abstraction layer you own. Adds ~30 lines of code. Saves weeks later.
- Vendor. Copy the subset you need. Accept the maintenance burden in exchange for zero external risk. Works for utilities under 500 lines.
- Skip. Build it, find an alternative, or live without it.
What We Actually Use (May 2026)
Our stack across Krypton Forge projects, with the framework applied:
- Next.js 16 + React 19 — Framework tier. High escape-hatch cost, but the ecosystem maturity justifies it for SSR-heavy apps.
- Tailwind v4 — Design-system tier. Bad escape hatch, good productivity. We accept the lock-in because the CSS output is portable.
- Prisma ORM — Data tier. High migration cost. We use it because the type-safety is worth it for multi-tenant schemas.
- date-fns — Utility tier. Good escape hatch. Swappable.
- pino — Logging tier. Clean interface. We wrap it in a thin logger module anyway.
We skip: ORMs with weaker TypeScript support, CSS-in-JS libraries with runtime overhead, and state management libraries when React Context + useReducer is sufficient.
The framework took us four years of mistakes to develop. The short version: evaluate maintenance before features, plan your escape before you install, and build more than you think you should.
Tags
- open-source
- dependencies
- due-diligence
- architecture
- decision-making
More on engineering
- SQLite Durable Workflows — What the HN Crowd Got Right, and What They SkippedA 473-point Hacker News post argues SQLite is all you need for durable workflows. We break down the architecture, where it actually works, and where it crumbles for Indian SMB workloads that need shared state.
- Docker, PM2, nginx — the small-team production stack that worksNot every team needs Kubernetes. For 1-5 person teams shipping to a VPS, the Docker + PM2 + nginx stack is boring, reliable, and cheap. Here's the exact config we run across twelve deployments, including the footguns and the fixes.
- TypeScript strict mode in production — why it's not optionalTeams that skip strict mode rationalise it as pragmatism. It's not. It's deferred debugging. Here's what strict mode catches before production, what it costs to adopt mid-project, and why we enable it on day one of every codebase.