Product Thinking for Open Source Library Design

ByKrisMar 26, 2026

What I discovered about library design while working on a dying open source project.

Product Thinking for Open Source Library Design.png

Background photo by Shahabudin Ibragimov on Unsplash

The best open source libraries are built like products. They need user research, competitive analysis, onboarding strategy, and distribution. The same things any product needs to win. But library authors rarely think this way. They go straight to code.

I learned this the hard way when I joined the web3.js team back in 2024, the oldest Ethereum JavaScript library, already largely replaced by viem. The API had aged, a newer competitor had taken the market, and the team had spent years on a TypeScript rewrite without asking why developers left in the first place. Nobody had a clear strategy for winning them back, and leadership wasn't helping. The project was a mess, so I picked a task for myself: understand the market. Map the ecosystem, study the competitors, figure out if there was a niche we could own. The library was sunset before anything meaningful came out of it. I shipped one plugin and improved the type system, but nothing that would turn things around. Still, that thinking process shaped everything I'll lay out here.

Define Your Product Vision

Before touching any code, I wrote down what the ideal library should be. Not a feature list, but a set of qualities. What would make a developer actually want to use this thing?

Stable and performant. This is the baseline. No surprise breaking changes, no unnecessary overhead. Developers need to trust that updating a patch version won't ruin their weekend. For frontend libraries this extends to bundle size. If your library adds 200KB when someone only needs 10KB of it, they'll find an alternative.

Intuitive and hard to misuse. A great API lets you guess the method name and be right. Sensible defaults, descriptive errors, TypeScript types that guide you through the API. Consistency matters too. Same naming patterns, same argument order, same return shapes across the whole API. Once a developer learns one part of your API, the rest should feel familiar. Catch mistakes early: through types at compile time, through validation when the function is called. Never fail silently.

This matters for humans and machines alike. An API with clear types, predictable naming, and structured error responses is one that AI coding assistants can use correctly on the first try too. Make the simple cases simple and the complex cases possible.

Inviting to explore. A great library makes you want to play with it. Interactive sandboxes, starter templates, CLI tools that let you experiment from the terminal. CLI matters more than ever. Developers now scaffold and iterate from the command line more than they copy-paste from docs pages. If your library has a CLI, consider exposing it as an MCP server too, so AI tools like Claude Code can call your commands directly. The easier it is to experiment, the faster developers go from "I'll check it out" to "I'm using this."

Quality feel. Developers curate their tools. When two libraries solve the same problem equally well, they pick the one that feels alive. Good docs site. A logo that doesn't look like it was made in Paint. Active GitHub, recent commits, someone actually responding to issues. This isn't vanity, it tells you someone is paying attention. A library that looks abandoned probably is.

Know Your Users, Then Design for Them

A typical library redesign starts with "what modules do we have?" That's backwards. Start with who's using your library and what they're actually building.

When I did this for web3.js, I found six distinct developer personas, six types of people using the library for very different reasons. The interesting part wasn't the list itself, but the contradictions between them. A frontend developer needs tiny bundles and reactive state updates. A backend developer needs robust error handling and doesn't care about bundle size at all. A dashboard builder only reads data and shouldn't have to import authentication and write logic just to display a chart.

Same library, completely different jobs. Some needs overlapped (most personas needed to call the core API), but the priorities and constraints were wildly different. Once you see that, you realize a single monolithic API is trying to please everyone and ends up pleasing no one.

And I'd add one more persona to that list today: AI coding assistants. A coding assistant that integrates your library on behalf of a developer has its own needs: machine-readable output, structured errors, discoverable tool schemas. It's a user worth designing for.

Product teams call this segmentation, and it should drive your architecture. When your personas have genuinely different needs, a monolithic library forces everyone to pay for what they don't use. A modular core handles the fundamentals. Specialized packages build on top, each importing only what it needs. Modularity isn't just clean architecture, it's a product decision. Map your users first, then design your modules. Not the other way around.

Study the Market

There's a step that's easy to skip: really understanding the alternatives. And here's the thing: your competitors' code is open source. You can read every line of it. That's more powerful than any competitive analysis framework.

Here's what I did. For each competing library, in my case ethers.js and viem, plus web3.js itself, I wrote short examples for the same use case and compared them side by side. Not "does it support this?" but "what does the developer experience actually feel like?"

The real opportunities show up here. Not in feature comparison tables, but by writing the same code in three different libraries and feeling the friction. The same kind of friction that made me switch from web3.js to ethers back when I was building my first dapps in 2020. The goal isn't to copy the best parts. It's to find where each library falls short and look for a niche you could own. That's what I was doing for web3.js: looking for gaps the next version could fill.

Find Your Position in the Ecosystem

A well-designed library doesn't just serve its direct users. It can become a platform that other tools build on top of. In the Ethereum JS space, wagmi started out built on ethers.js but kept hitting its limitations: bundle size, performance, TypeScript support. Eventually the team built their own low-level library, and that's how viem was born. No opinions about UI or frameworks, just core chain communication done well. That focus made it possible for an entire stack to grow around it. UI libraries like RainbowKit and ConnectKit on top, lower-level primitives like Ox underneath, framework bindings in between. None of this was planned from day one. It happened because viem was focused enough that others could build on it without fighting its opinions.

Something to ask yourself: are you trying to do everything, or are you focused enough that others can build on top of you? Where does your library sit in the stack? What could grow around it if you got the boundaries right?

Plan for Adoption

Now a question that's easy to overlook: how do people actually start using it?

For new projects, the first experience matters. Good examples and a clear README help developers decide if your library is the right fit. But increasingly, the actual implementation is done by a coding assistant. If your library has an llms.txt file, the coding assistant will get it right from the first go and create a working integration in minutes.

For existing projects, migration is the bottleneck. Developers have working applications. Hundreds of call sites, tests built around existing behavior. If the answer to "how do I migrate?" is "rewrite everything," most will stay where they are. Doesn't matter how much better your library is.

You need clear migration guides and incremental adoption that lets developers try your library for one feature without rewriting everything else first. In the past you'd also ship codemods to automate the tedious renaming. Today, developers just point a coding assistant at the migration guide and let it do the work. Which means the quality of that guide matters even more than before.

I saw what happens when this goes wrong. The ethers.js v5 to v6 migration frustrated a lot of developers: breaking changes like replacing core data types, renamed APIs, and documentation that wasn't ready. Many developers, instead of migrating to v6, just switched to viem. Not coincidentally, viem offered a clear ethers migration guide from day one.

Get Found and Keep Earning Trust

You can build the best library in the world and nobody uses it. Distribution is part of the job. Developers find libraries through social media, Discord, conference talks, a colleague's recommendation, or increasingly, a coding assistant's suggestion. Popularity is a proxy for "this probably works and someone will help me if it doesn't."

Getting found is one thing. Keeping developers is another. When I worked on the Lens Protocol SDK and later web3.js, I spent a lot of time in Discord, Telegram, and GitHub issues. Answering questions, posting updates, sharing code samples. But the real value wasn't the support itself. It was what I learned by being there. Which use cases kept tripping people up? What workarounds were developers building that the library should handle natively? As a core contributor, I could take what I learned and put it back into the product. It's easy to miss this feedback loop. You're not just doing support, you're doing product research.

When developers feel heard, they stick around. Some start contributing. Some become your biggest advocates. That's the real advantage of open source.


I never got to apply all of this at web3.js. Two months after I joined, the Ethereum Foundation ran a study of web3.js usage, looked at the numbers, and decided to stop funding the project. Viem had two people building a library that was winning the market. Web3.js had a team of ~9 people at ChainSafe that spent years on a TypeScript rewrite and was still losing it. I still don't fully understand why they hired me in the first place. The very first Ethereum JavaScript library, a piece of the ecosystem's history, died not because the technology failed but because the people running it lost touch with reality.

If you're building or maintaining an open source library, I'd genuinely like to hear where this lands for you. What maps to your experience and what doesn't.

Find me on Bluesky or X.