Reusability

When I started learning BASIC as a child on a Timex/Sinclair 1000, my code was a wonderful mess in the “big ball of mud” sense. I was having fun and it didn’t matter exactly how everything was put together. I hadn’t yet developed the wisdom to think about code at that level anyway.

When you write like that, nothing is reusable and every new program starts with a blank canvas. Of course you end up repeating yourself when you need to do something you did before. But that’s probably a good way to start because it drills in the basics and gives you opportunities to improve as you repeat yourself.

That only gets you so far. At some point you need to reuse your code or lean on someone else’s. That’s why libraries and frameworks exist and abound for every major language, for opinionated values of “abound” and “major”.

I’ll just lump them together under the word “library” today.

Overgeneralization

Once you start writing a library, it’s easy to catch the generalization bug. You assume, incorrectly, that your “customers” think the same way you do, and that they’ll appreciate your cleverness as you make your library solve every possible problem in its domain. Before long, your thought process is taken over by “what if the developer wants to…” And complexity creeps in.

Generalization is fine. Heck, that’s the whole point of reusable code. The line where it becomes overgeneralization is situational and fuzzy. When you cross that line, your library becomes less valuable.

Overgeneralized -> Complex -> Less Valuable

Trying to cover every possible situation with your library leads to complexity, which can leak into every interaction that developers have with your library.

“Doing the job” is only part of the value that your library provides. A good Developer Experience (DX) is the rest. Your library is a means for the developer to accomplish some other goal, whatever that is. Any friction imposed by a library impedes the developer from reaching that goal.

Extra friction can come from lots of places, like:

This friction adds up fast in the form of cognitive load on the developer. And the more load imposed by your library, the less capacity the developer has to solve their real problem.

Simplicity -> More Valuable

On the flip side, if your library is simple, then everything becomes easier:

For your users:

For you:

A Matter of Taste and Judgment

Sometimes your library really needs to handle every possible situation. If so then:

  1. This post’s advice may not be helpful
  2. Good luck!

Otherwise, it’s probably worth pulling back and asking yourself:

Your definition of “done” should depend on these answers. There’s judgment involved - e.g. where do you draw the line on “most common”? How do you even know what’s most common? The sweet spot for how much you should address each item is situational. 80% might be a good starting point for each. 90% for some items and 60% for others might be right for your project. Be thoughtful, explicit, and deliberate.

👴 Speaking from experience...

If you are writing your library for personal purposes or for fun, and enjoying the freedom of working without the “requirements” you deal with at work, I have bad news for you: you still have requirements, even if they're just in your head. They need to come out.

Anyone who reads your code, including future you, will wonder why you made certain decisions. Writing out your goals, requirements, assumptions, and non-goals, even as a short bullet list, will make everything much more understandable later.

Future you will thank present you.

Painful Lessons

If you tell a toddler not to touch a hot stove, they might listen. They might even remember the warning. But if they actually touch one, they’ll definitely remember.

That’s where this is coming from: I’ve touched the stove over and over again in my career. In my defense, I don’t remember getting a lot of warnings about these things, so I’m writing them down for others. They might listen. They might even remember. 🙂

I still need to be reminded of this from time to time. I’ve been programming for forty years, and just yesterday a colleague eliminated a whole pile of headaches for me on one specific requirement by simply asking “what if we just… don’t do that?” This wasn’t a lazy call, it was the right call, both for the tool and for the users. Palm, meet forehead. (and great call, Jim!)

The best library might not be the one that does the most. It might be the one that gets out of the way. Good luck with yours!


The same thinking can apply to larger software beyond libraries. As one example, I invite you to take a look at ChatKeeper, a desktop tool for turning ChatGPT exports into local Markdown files and images you can own.