6 March 2024

This is How We Do It

The Charm guide to building popular open source software

By Christian Rocha

If purple smoke and scratched up gold don't say success, I don’t know what does.

GitHub stars are a great indication of sentiment and adoption in open source. On that note, we’re thrilled to announce that we’ve received our 100,000th star. We could not be more proud and we have you, the open source community, to thank.

Here’s are some stats on how our ⭐️100k breaks down:

  • We’ve had 16 major releases with an average of ⭐️6.3k per release
  • Every major release has over ⭐️1k
  • Our open source projects are in use at companies like GitHub, Nvidia, AWS, Microsoft, Shopify, any many others
  • 2022 yielded our highest star growth at ⭐️37k (137% yoy) thanks to the ardent response to Gum and VHS
  • 4 of our projects (Glow, Gum, VHS, and Bubble Tea) have over ⭐️10k
  • Bubble Tea, our most-starred repo, clocks in at ⭐️23k
  • Bubble Tea is also our fastest growing repo, though it regained the lead only after Gum’s electrical-storm-like growth cooled down

How did we do it? Here’s the Charm open source playbook based on our collective learnings over the past four years.

Starting with a Problem

We always start with a problem of our own. This typically means a few key factors are true:

  • We have firsthand knowledge of the problem and are thus well suited to solve it.
  • We are passionate about the problem. It’s a pain point we know personally and something we’re excited to solve for.
  • We have researched the problem and have found no worthy solutions, so our offering will be at least somewhat unique.

Bubble Tea, our text-based user interface framework, is a great example of this. We wanted to be able to build ephemeral, user interfaces inline in the terminal in go without having to concern ourselves with rendering, similar to the experience we had building UIs in the browser with Elm. After a lot of research we concluded that there was nothing in the go ecosystem that met our needs so, out of pure necessity, we began work on what would become bubble tea.

Bubble Tea is now our most popular project with ⭐️23k.

VHS, our terminal session recorder, is another great example. After manually recording an enormous number of GIFs of terminal sessions we knew there had to be a better way. While other good terminal recording solutions existed, we found nothing scriptable, and nothing that produced GIFs. Before we knew it we had a full blown scripting language (complete with a tree sitter grammar) for producing GIFs of terminal sessions.

VHS weighs in over ⭐️13k.

Developer Experience

We build three types of things at charm: TUIs, CLIs and libraries (called packages in Go). In all cases we believe an exceptional user experience is paramount, and for that reason they’re not all that different. After all, “developer experience” is really just user experience. APIs and CLIs are user interfaces at the end of the day and they require the same amount of care and thoughtfulness any good visual UI.

Regardless of the medium, we are striving for the one goal: an intuitive and enjoyable experience for the person using the product.

The process is a bit like making an oil painting. We start with a sketch, build a little bit, then test, evaluate, and adjust. We’re constantly revisiting our design as the project develops, asking ourselves if it’s still appropriate given how and where the project is progressing.

Designing Libraries and APIs

Enjoying and getting value from an API is a visceral experience, so we spent a lot of time and energy on getting the nuances right as the design of the API is so key to adoption. Our goal is to produce an API that’s intuitive, fun, and allows the developer to get a lot of value with as little effort as possible.

We ask questions like:

  • How can we make this API easier to figure out?
  • How can we help developers avoid mistakes?
  • How can we design our API in a way that helps language servers provide effective completions?
  • Will this API lend it’s way to future innovations? Have we made room to grow?

Designing TUIs

a GIF of a terminal session
I still can't believe this TUI runs over ssh.

TUIs bear many similarities to other visual types of user interfaces, but have the wonderful, minimal quality of being just text—at one single size—and color. We believe a good TUI can both exist in a long-running fashion, like vim, as well as in an ephemeral manner that complements the CLI experience, like fzf.

When designing TUIs we ask questions like the following:

  • Should this be inline, use the altscreen, or operate in both contexts?
  • How can we keep the user from ever wondering what key to press?
  • How should the application behave in very small terminal windows? What about very large terminal windows?
  • Does this really need to be a TUI, or would a CLI be more appropriate?

Designing CLIs

An old Betamax recording of one of our CLIs.

CLIs have the wonderful benefit of minimal user interface and the powerful ability to tap into the essence of the command line with pipelines. The lack of a GUI also means CLIs see faster development cycles, with the costs being opaqueness and learning curve for the user.

When building CLIs we ask questions like the following:

  • Are these arguments and flags intuitive?
  • What do helpful error messages look like? How can error messages help users figure out how to use the product?
  • How can --help teach users how to get the most value out of this tool?
  • Can this CLI become more powerful by leaning into pipelines?

We also strive to provide manpage entries and completions for popular shells.


The huh README is chock-full of examples and GIFs.

The README is critically important to the success of an open source product. It’s often a developer’s first point of contact with a project and the place where a developer will, in a matter of seconds, judge whether the project worthy of further consideration. With this in mind, put a lot of effort into README design, optimizing for strong first impressions.

Our strategy is to simply follow the age-old rule of advertising: showing the product. Good products, when presented correctly, will sell themselves, which is why we spend spend so much time on user experience and attention to detail.

With libraries, APIs, and packages we show the product with example code, typically placing some code right at the top of the README. We want to show the reader how intuitive, fun and powerful the API is and help them get started as quickly as possible.

a gif of a terminal session
Do you want fries with that?

When it makes sense, we also insert GIFs of the product right at the top of the README. While GIFs remain a technical nightmare in terms of a file format, they’re the most effective medium we’ve encountered for illustrating how software works in a concise manner. They’re short, silent videos that automatically autoplay and automatically loop with no user interaction, allowing us to paint a meaningful picture of the application in just a few seconds.

As mentioned earlier, we believe so much in the effectiveness of GIFs that we built vhs, a tool for scripting small, high quality GIFs of terminal sessions.

Quick Reference

Beyond first impressions, we also tend to include a quick reference in our READMEs to support the docs. The quick reference gives developers more insight into the package, helps them get started, and highlights some of the common parts of an API. It’s an excellent bridge to the docs that can help the user make connections and gain insight into the API in ways the full, raw documentation cannot.

In go projects, the README is also browsable in the generated documentation (called GoDocs) so having the quick reference in the README is a win-win.

Examples, Examples, Examples

An example in the Harmonica README

We can’t emphasize the effectiveness of examples enough.

We’re very strong believers in learning by example and we believe code examples are one of the best ways to learn about an API. They show developers how to use the API in a concrete way and help them hit the ground running so they can be more productive more quickly. Examples also serve as a cookbook, presenting the developers with solutions to common use cases, whetting their appetites for creative thinking with the product.

We commonly put a set of fully functional examples in a repository for users to reference both online and locally in their clones. In many cases those examples are what we ourselves used to help think about the product while we were building it.


Good branding has the power to differentiate a product in the market with a mere glance. Our strategy is to appeal to developers on a personal level and create something that feels human, approachable, and memorable. We want the branding to stand apart from the common efforts we see from the vast majority of corporations and startups in the technology space. For that reason, we spend a lot of time looking beyond tech and instead drawing inspiration from things like video games, art, beauty, Family Mart, Sanrio and so on.

The essence of our products’ branding is the name. Our goal is to disarm our readers, suggest how they should feel about our product, and make them smile. For these reasons an ideal Charm name is subversive, casual, and tongue in cheek. Sometimes a good name comes quickly. Other times it takes awhile. Because of this we start branding efforts early on in the product development cycle.

Nothing says “handy CLI tool” like a pack of gum

After we’ve settled on a name we produce artwork. Good art goes beyond making the product attractive: it equips both us and third parties with marketing material for use in videos, articles, and so on. In order for third parties to use the art, however, the art needs to be good. It needs to be something people want to showcase. This is why we spend a lot of effort conceptualizing and producing high quality art.

YouTubers using Gum art

Thinking Bigger

While we’re branding individual projects, we’re also playing the larger game of increasing awareness of the Charm brand. Our goal is less about giving individual projects a following, and more about promoting Charm as a consistent producer of wonderful, open source software. A Charm product’s branding should be consistent enough with other parts of the Charm narrative so it feels like like a facet of the brand, yet unique enough so that it takes the brand to new places.

Getting the Word Out

The bulk of of our launch efforts happen in the form of the prep described above. If we’ve done the work correctly, we’ve built a good product and prepared the necessary marketing material. There’s no magic in the launch itself: we simply make the repository public and simply let people know. We star it on GitHub, tell our friends, and post around the internet to the places you’d expect: Twitter, Reddit, Mastodon, and so on.

With any luck, the project starts to find its way around the internet. Maybe it will get posted to Hacker News. Maybe it bubbles up onto to GitHub Trending. Perhaps it’ll be picked up by Golang Weekly. Maybe The Primeagen will mention it. Or maybe developers will find it via more subtle means.

Embracing Open Source

In a lot of ways, a launch is when a project’s story really begins. Good open source software has the unique ability to attract users, feedback, and contributions en masse, so we put a significant effort into spending time with the open source community post-launch.

Part of this is just paying attention. We look for patterns in feedback. We think critically about what developers do with our projects, as well as what they’re want to do, but can’t. We ask questions.

Another part of this is gracefully saying no. Sometimes the community will excitedly request features or submit pull requests for things that don’t align with our vision for the project. When saying no to a feature or pull request, we’ve found the community generally understands that they don’t always have the full context that we do. With this in mind, we always explain our reasons for saying no, but we also keep listening because often we don’t have the context that the community does.

For that reason, it’s equally important to say yes. Our biggest projects have taken significant turns for the better exclusively via feedback and contributions from the community. In some cases a single comment has inspired critical changes to our work (“You know, if Bubble Tea worked this way then we could…”) and in other cases we’ve received major, game-changing features and optimizations from developers with incredible talent and skills far beyond our expertise.

Sticking With It

The most important thing we can do for the project after putting it out into the world is to continue working on it. Consistent work on an open source project post-launch is, in fact, the most important factor the project’s success. A big part of this, as mentioned earlier, is working with the community. The rest is using the product, thinking about it, and continuing to improve it. A launch is only the first step in a software project’s path to maturity. It takes time, effort, and introspection for a project to reach its full potential.

Actively maintaining open source projects also presents a sense of security to developers. It suggests that the maintainer sees value in the project, that bug reports will be honored, and feature requests will be entertained. It signals to a developer that they can use the project and expect it to be relevant for contemporary needs, and that they can expect a certain degree of support.

Bashbunni talks about Pop our CLI-based email tool.

In the same vein, we also keep promoting our work after launch. We’ll produce short and long form video. We’ll talk about it in blog posts. We’ll feature individuals and companies using the product. And every release, no matter how small, is an opportunity to toot a project’s horn.

Sometimes our projects don’t take off immediately. Sometimes they’re not met with the amount of acclaim we’d like. We know, however, that if we stick with it, our software will find its way. Success doesn’t always come quickly but it can always be found with enough persistence.


Read this post in your terminal with Glow:

glow -p https://charm.sh/blog/100k.md Copied!

By Christian Rocha

6 March 2024

Christian is one of the Charm founders. he enjoys silence, bitter melon, and applicative functors.

Lets chat!

Have a question about a command line thing you’re building? Got an idea for a new feature? Just wanna hang out? You’re always welcome in the Charm Discord.