Skip to content
Back to Learn
Developer toolsJuly 26, 20265 min read

Building developer tools that do not punish you

Depth and legibility are separate axes. Abstraction that carries versus abstraction that traps, progressive disclosure, defaults as shipped opinions, and error messages as the most-read writing a tool produces.

Kushagra SharmaFounder and product engineer

Powerful tools earn a reputation for being hostile, and the folk reasoning is that if a thing is hard to use it must be serious. That is backwards. Punishing tools do not filter for skill, they filter for tolerance.

The lie that hard means powerful

Somewhere along the way "expert tool" became a synonym for "you will figure it out or you will not". Cryptic flags. Error messages that name a problem without naming a fix. Documentation that assumes you already know the answer to the question you are asking it.

None of that is power. It is friction that never got designed away. A tool can be deep and legible at the same time: the depth lives in what it can do, the legibility lives in how it tells you what it is doing. Those are separate axes, and most tools that brag about the first are quietly neglecting the second.

Accessibility in developer tools is not about removing the deep end of the pool. It is about not making people guess where the steps are.

Abstraction that carries, and abstraction that traps

There is a difference between an abstraction that carries you and one that traps you. The bad kind hides the underlying system so completely that the moment you step off the happy path you are stranded, with no model of what is actually happening. The good kind gives you a working surface and keeps the real thing reachable.

The test is one sentence: can you reproduce by hand everything the abstraction does for you? If the answer is no, it is hiding rather than helping, and the day it fails you will have no ground to stand on.

Three commitments follow from that test, and they are the ones our own tooling is being held to:

  • The guided path is the default, never the ceiling.
  • Every generated action is viewable as the command it represents.
  • Nothing the interface does is something you could not do yourself.

The third one has teeth. It rules out the convenient shortcut where a tool reaches into a private API or a hidden state file to make a flow feel smooth, because that is precisely the flow that strands you when it breaks.

Progressive disclosure as a first principle

Most interfaces put every option in front of you at once and call it powerful. It is not powerful, it is loud. Progressive disclosure means the common case is immediate and the long tail is one deliberate step away, never gone.

In practice a deploy form opens with three fields rather than thirty, and the advanced panel (custom health checks, rollout strategy, resource limits) sits behind an explicit toggle: reachable the moment you need it, documented where it lives instead of in a separate manual you have to go and find.

The same principle scales down to a single command. Defaults handle the common case and flags handle the rest, so you never pay the cost of the second line until you need it.

# the common case: no ceremony, defaults already chosen for you
$ deploy

# the same command, with the long tail made explicit
$ deploy --strategy=canary --health-timeout=90s --max-surge=2

deploy there is a stand-in for whichever tool you are holding to this standard. The shape of the argument is the point, not the binary.

Defaults are opinions you ship

A default is an opinion you ship to everyone who does not override it. Treating defaults as an afterthought, whatever the config parser happened to initialise to, is how a tool ends up technically configurable and practically unusable.

The way to choose one is to choose it the way you would for a careful colleague: the option that is safe, reversible, and right for the common case. A rollout defaults to incremental rather than all at once. A destructive operation defaults to a dry run. A timeout defaults to a value that survives a slow network instead of failing on the first hiccup.

Good defaults mean the shortest path is also a reasonable path. Nobody should have to read the manual in order to avoid shooting themselves in the foot.

Error messages are the real documentation

Nobody reads the docs when things are working. They read your error message when things break, which makes it the most-read piece of writing your tool will ever produce. Treat it that way.

A good error answers three questions: what happened, why, and what to do next. A cryptic one answers the first and abandons you on the other two.

# cryptic: names a symptom, strands the reader
Error: ECONNREFUSED 127.0.0.1:5432

# helpful: names the cause, gives the next command, points at context
Error: could not connect to the database at 127.0.0.1:5432.
  The postgres service for this project is not running.
  Start it:   docker compose up -d postgres
  Check logs: docker compose logs postgres

Same underlying failure. One leaves you to translate a TCP error code into an action, the other hands you the action. Writing the second version costs a few minutes, once. The first version is paid for by every user, every time they hit it, forever.

The rule every message gets held to:

  • Name the cause, not just the symptom.
  • Give a concrete next step, ideally a command that can be run exactly as printed.
  • Link to the relevant context, not the homepage.
  • Never blame the user for a state the tool allowed them to reach.

Where this goes

Accessible does not mean simple, and powerful does not mean hostile. They were never opposites. We just got used to tools that treated friction as a feature and called the survivors experts.

This is the standard the systems work here is being built against, including the operating system, which is in development and has nothing to download. Publishing the standard before the software is deliberate: a commitment written down in public is much easier to argue with, and much harder to quietly walk back, than a product that already shipped.

Share this article

Need a product built?

Describe the problem in your own terms. You will get a straight answer on what building it actually takes, and how an engagement would run.