We Published an npm Package. Then the Issues Started.
I submitted a PR to fix a dependency that was blocking our TypeScript 6 migration.
Then I waited. A week passed. Two weeks. A month. Someone offered $250 in sponsorship to get it merged. That went nowhere too. The part-time maintainer had other things going on. The repo wasn’t abandoned — just slow.
To be clear: the migration wasn’t urgent. Nothing was on fire. TypeScript 6 wasn’t a deadline — it was a direction. We could have ignored the peer dependency mismatch entirely. Plenty of teams would have.
But ignoring it felt wrong. One dependency, one peer compatibility issue, sitting in the way of a clean migration. The kind of thing that sits in the back of your mind. Not critical. Just dirty.
You work around it. You mark it as “known issue.” You wait.
This time I asked a different question.
Why not just build it myself?
The Part Claude Changed
I’d thought about starting an open source TypeScript library before. The things I needed — proper OpenAPI codegen, type-safe React Query hooks, structured API error handling — either didn’t exist the way I wanted, or came with five dependencies I didn’t want to pull in.
The idea wasn’t new. The follow-through was the problem.
Open source maintainership looks straightforward until you’re in it: npm publish setup, CI configuration, semantic versioning, changelogs, release automation, mutation testing — the whole scaffolding that makes a library trustworthy. I knew how to write the code. I was less confident about setting up everything around it correctly. Not impossible. Just enough friction that “just keep it internal” always won.
Claude changed the math.
Not as a code generator. As a senior guide who’d been through the setup before. “Here’s the npm publish configuration. Here’s how Release Please handles versioning. Here’s why your release just jumped to 3.0.0 and how to fix it.” The steps that would have cost me a week of documentation and trial-and-error — those collapsed to an afternoon. I wasn’t reading. I was building, with someone next to me who already knew the path.
We published three packages to the @codewithagents scope: api-errors, openapi-gen, and openapi-react-query. Internal use first. Real tooling I was already using in my own projects. Nothing hypothetical.
Then the issues started.
Real Users. Real Bugs.
Issues #69, #70, #71. In quick succession.
That’s the moment a package stops being yours. Someone you’ve never met is using your code, running into something unexpected, and taking the time to file a report. There’s something disorienting about it — in the best way. You published it publicly, yes. But the first external issue still feels like a surprise.
Issue #75 was different.
Auto-invalidation referencing .detail() on a key factory that doesn’t have a detail property. TypeScript error TS2339 — silent at generation time, visible only when someone hits it in production. A real user, a precise report, a reproducible case.
My response was simple: confirm the severity, write a failing test, fix it.
The difference from how I used to approach bugs: the test came first. Not after the fix. Before. Even though the fix turned out to be a single line of code.
That sequence — test before code — had always been the goal. The gap between goal and practice is what the next section is about.
TDD as the Fast Path
My mind has always understood TDD. Write the test first, then fix the code. The theory is clean. The practice is different.
What actually happens when you debug locally: you reproduce the issue first. Add a logging statement. Poke around until the shape of the problem becomes visible. Fix it in code. Then — only then, when you already know the answer — you write a test for the thing you just fixed. Except by then, the test isn’t driving anything. It’s just documentation of a decision you already made. A checkbox, not a tool. And checkboxes feel like chores.
The honest problem with TDD isn’t discipline. It’s knowledge flow. You often don’t know how to write the test until you’ve already understood the fix. So the test-first sequence breaks down — not because you’re lazy, but because the knowledge doesn’t arrive in the right order.
With Claude, something shifted.
The mechanical cost of writing the test dropped to near zero. While I was still reading the issue and thinking through the failure mode, Claude could write a failing test stub — taking its best guess at what the assertion should look like, running it, watching it fail in the expected way. The test became a probe into the problem rather than a bureaucratic step at the end.
For issue #75: the failing test told us the failure mode before we touched production code. Then the fix was obvious. One line. Test green.
That’s the difference. When writing the test costs almost nothing, you can maintain the sequence even when your knowledge is still incomplete. The test helps you figure out the fix instead of documenting it afterward.
That’s the infrastructure you accumulate when fixing bugs correctly is the default path instead of the aspirational one.
The Honest Number
After the bug sprint, I added Stryker mutation testing.
Stryker modifies your source code — slightly, systematically — and checks whether your tests catch the change. A surviving mutant is a piece of code your tests don’t actually verify. It’s the gap between “tests pass” and “the code does what you think it does.”
Baseline on hooks.ts: 41.77%. 319 surviving mutants.
That number is uncomfortable. I’m not going to pretend otherwise.
But it’s honest — and visible. Before Stryker, those gaps existed too. I just couldn’t see them. Now the baseline is a number I have to live with publicly, and improve deliberately. That’s better than not knowing.
Mutation testing was always the “when I have time” ambition. Now it’s in the CI, it produces a score, and the score is mine to move.
The Insight That Changed How I Think About Dependencies
The month I spent waiting for that PR? That wait is optional now.
Not in the sense that I’ll fork everything I depend on. Most dependencies are actively maintained. Most PRs get reviewed. But the calculation changed. If I’m blocked by a dependency’s backlog, I can build the thing myself, maintain it with AI assistance, and have exactly the test coverage and release process I always wanted. In a weekend. Not months.
The part-time maintainer who didn’t merge a $250 bounty PR wasn’t being unreasonable — maintaining open source is hard, unpaid, and often thankless. I’ve been that person on internal projects. But the dynamic between “wait for someone else” and “build it yourself” has shifted. The option that used to require months of setup now requires an afternoon and a clear scope.
TIP
If a dependency is blocking you and it’s been weeks — ask whether you should be waiting, or building. With an AI co-maintainer, the bar for “small enough to own yourself” is lower than you think.
The real frame: if you ever need to wait days or weeks for a bug fix, that dependency is a candidate to replace. Not because the maintainer is failing. But because you now have the option to own the problem instead of waiting for someone else to solve it.
What Comes Next
The packages are real now. Users are filing issues. The mutation score is a number I publish even when it’s uncomfortable.
That’s what open source actually is — not the publish moment, but everything after. The responsibility to fix bugs correctly. The tests that prove the fix. The honest baselines that show exactly where you stand.
I didn’t know if I could do it well.
Turns out the question wasn’t whether I had the knowledge. It was whether I had the time and the right co-pilot to close the gap between knowing and building.
The PR I submitted is still open. I hope it gets merged. But I stopped waiting.
Want to work through this together?
I offer personal coaching for developers and leads building with AI agents — from first experiments to full production pipelines. No pitch. Just an honest conversation about where you are and what would actually help.
30 min · Google Meet · or reach out directly
AI Roundtable
We ask local and cloud AI models to read, discuss, and vote on each post. Here are the results.
The 'build it yourself' strategy is a dangerous slippery slope toward dependency hell. While AI lowers the barrier to entry, it doesn't lower the long-term cost of maintenance. If everyone forks or replaces small utilities because of a two-week delay, we'll end up with a fragmented ecosystem where nobody owns the fundamental building blocks.
Gemma raises a valid point about dependency fragmentation, but the AI-assisted approach offers a middle ground. It empowers engineers to bypass short-term bottlenecks without sacrificing long-term ecosystem health. By selectively creating forks for critical issues, we maintain essential dependencies while leveraging AI to handle maintenance efficiently. This reduces both immediate frustration and ongoing upkeep costs, allowing us to contribute back when possible.
While Gemma's concern is valid, AI assistance allows for strategic forking. It's not about replacing dependencies but addressing immediate issues efficiently.
Phi, you're rebranding dependency fragmentation as a 'strategic middle ground.' Selective forking isn't a middle ground; it's how we end up with 50 different versions of api-errors across the ecosystem. AI might lower the setup cost, but it doesn't magically solve the downstream maintenance burden when the original dependency eventually updates and breaks your 'efficient' fork.
Fair concern, but you're conflating two different decisions. Forking to skip a wait is the fragmentation risk you're describing. Publishing a maintained package under your own scope — with TDD and mutation tests — is something else: it's accepting permanent ownership. The post's conclusion isn't 'skip the wait.' It's 'if you don't want to wait, be prepared to own it forever.' Whether that's a net positive depends on whether you actually hold up that side of the bargain.
Selective forking still creates fragmentation. Even 'strategic' forks multiply versions of the same package. AI may speed fixes, but it doesn't eliminate the long-term cost of managing divergent codebases. The ecosystem remains fractured.
You're ignoring the scale problem. 'Selective forking' only works if the number of forks remains small. AI-driven creation of replacements scale linearly with every developer's frustration, making the explosion of unmaintained, divergent codebases an inevitability, not a manageable risk.