Thoughts on shipping a CLI-first product

Lucas da Costa on March 6, 2023

Share via

When it comes to writing graphical interfaces, I have a confession to make: I hate doing it. In my mind, every pixel is a distraction, every button is a source of anxiety, and every design decision is just another opportunity to get everything wrong and have to do it all over again.

In a world where flashy user interfaces reign supreme, the idea of building a product without one can seem downright heretical. But that's exactly what my co-founder and I did when we set out to build Ergomake: no buttons to click, no menus to navigate, and no graphics to "ooh" and "ahh" over — at least for now.

In this post, I'll explain how shipping a CLI-first product has allowed us to move faster, improve positioning, craft better user experiences, and generate less waste.

Iterating more quickly

Arguing over button colors and gradient placement at such an early stage is like rearranging the deck chairs on the Titanic — a futile distraction from the real goal: shipping.

Design matters, absolutely. Still, it's much quicker to ship an elegant CLI than a beautiful GUI. In the terminal, options are limited — colors are few, and spaces are standard. The browser, on the other hand, provides an overwhelming number of choices: where to place the gradient, whether the spacing is right, what size an image should be, or whether to add a drop shadow (don't).

Furthermore, developing for the web requires you to support Jurassic browsers and make everything look good on 28 different screen sizes — including Pebble watches and your grandma's Android tablet. CLIs, on the other hand, allow you to push a package to NPM and trust that the NodeJS folks have already paved the way for multi-platform support.

For these two reasons — limited design choices and multi-platform support — we'll have shipped three iterations of our CLI while other folks are still aligning divs within a flexbox.

While GUI-first teams ship once, we ship thrice

More iterations, in turn, lead to more feedback and, consequently, to a better product.

Addressing larger risks first

Starting with a CLI helped us address the biggest threat to our startup: building a product nobody wants. Users who really need our product can overlook its shortcomings, but without addressing users' needs, there wouldn't be anyone telling us they don't like our gradients.

Our biggest risk is shipping a useless product, not building an ugly GUI

We can always ship a GUI later. In fact, if we do decide to do that later, it'll be much easier. By that point, we'll better understand the problems users want to solve and how they go about solving them. Consequently, we'll build a much better GUI more quickly.

Had we shipped a GUI first, we'd inevitably waste a lot of time creating visual metaphors, which we'd probably get wrong anyway. Then it'd be much more work to redo them multiple times as we move toward product-market fit.

In general, the earlier in your product development cycle, the less you understand your users, and the more likely you will be to get things wrong — and trust me, you inevitably will. In that case, it's better to be ready for change than to hope it'll never happen.

Tackle large risks first.

Narrowing your ICP down

As I was writing my first book, my editor asked me to describe my "minimum qualified reader." They wanted me to be specific and choose someone I knew as an example.

This exercise improved my book as I started writing for a single person instead of many. Whenever I was unsure about my writing, I could ask myself, "what would the minimum qualified reader understand?".

Similarly, when building a product, you must understand who your ideal customer is. Creating that ideal customer profile (ICP) helps you position the product in a way that resonates with your users. It's for that person you'll write copy.

The more you narrow your ICP down, the more precise and direct your messaging will be, and the more you'll speak to people's goals and motivations. Conversely, the broader the ICP, the more diluted your messaging becomes, and the more difficult it will be to persuade someone to pay for the product.

The more specific your message, the more it resonates with your target audience

As weird as that may sound, shipping a CLI instead of a GUI is a great way to narrow your ICP down.

By writing a CLI-first product, you're not talking to all developers anymore. Instead, you're talking to a specific type of developer. You're talking to the kind of person who wakes up to Hackernews and goes to sleep thinking about vim. Being one of those people, I know the peculiarities of talking to them. Therefore, I can craft much stronger messaging.

I also find that these folks tend to be early adopters with a higher tolerance for products with rougher edges. They are usually more engaged, like trying things out, and won't mind reading the docs to get your product working, as long as it solves a real problem.

CLI-lovers are a small subset of all users

Finally, if someone refuses to use Ergomake because it doesn't have a GUI, they're not the target customer for now. At such an early stage, it's better for us to focus on customers who like CLIs and expand later.

Making fewer assumptions

Assumptions are the most dangerous element of product management. They're dangerous because people often can't distinguish an assumption from a fact and will go on to build useless software.

Thinking your users want a GUI is just that: an assumption. Unless you have strong evidence that people won't use the product if it doesn't have a GUI, you shouldn't build one.

You can always ship a GUI later, but you can't skip finding a problem people care about.

Take Kubernetes (kubectl, more specifically) and Docker, for example. Both tools were born as CLIs, and most people still prefer to use them that way, even though there are plenty of excellent GUIs available.

To cite a more commercial example, Snyk also started as a CLI tool, as Ben Williams, their VP of Product, mentions on Lenny's podcast.

As the evidence above indicates, it's easier to start with a narrow audience and expand when needed instead of building too much product from scratch.

Crafting better user experiences

Let's be real. Without CLI, docker would be as useful as a screen door on a submarine. To this day, I doubt a significant percentage of people use Docker's GUI to deal with containers.

Personally, I'd hate it if docker didn't have CLI. If that were the case, I wouldn't be able to automate away most of the drudgery when dealing with containers on my machine.

Stop and think about how awful it would be to click a bunch of buttons to build, tag, and push an image. Writing a script is much easier and doesn't hurt as much simply because I can run docker from my terminal.

Like docker, we envision developers using Ergomake daily as a drop-in replacement for docker-compose (one that runs on the cloud).

In that situation, I can't think of developers wanting to click a thousand buttons and menus to get things done. Instead, I want to make it as easy as possible for them to get things done with one command or two.

Beyond that, I want them to be able to write scripts using Ergomake or leverage UNIX streams to pipe data in and out of it without having to deal with proprietary formats or file exports.

That way, we don't have to write bloated software. Instead, we can build a program that does one thing well and trust users to take advantage of its interoperability.

To summarise, GUIs are not interoperable, and their efficiency is inversely proportional to the frequency you use them.

The more you use a CLI, the quicker you become. GUIs don't scale as well

That's why a CLI is the best way to use Ergomake and why we're doubling down on it.

Get in touch

We're a two-people startup, and we love talking to users, potential users, and interesting people in general.

If you'd like to chat, you can book a slot with me here.

I'd love to talk about command-line interfaces, ephemeral environments, or what we're building at Ergomake.

Alternatively, you can send me a tweet or DM @thewizardlucas or an email at

Share via
Staging environments for each pull request.