Writing · 8 min read

What does a developer need from an interface on a bad day?

What anyone needs when work has stopped: what broke and what it affects, what changed and what is safe to do next. Developers open their tools mostly when something has failed. So the error message and empty state, the log and the first ten minutes are the product. Every incident below happened to this site.

A large dusty slate breaker panel cropped by the frame, two columns of identical rocker switches all flipped up except one near the middle of the left column, which is flipped down and solid black.

Key idea 1 of 7

The screens a developer opens most are the ones that open when something broke

A developer does not open the dashboard when the deploy works. They get a green line in a chat window and go back to the editor. They open it when the deploy fails, the build is red, the service is returning errors at two in the afternoon or the bill doubled.

Traffic to the interface runs opposite to how well things go, and that decides the design. The screens a person reaches already stressed, short on time and short on patience get the most attention. They have to be designed for that person, not for the demo.

Most postings for a designer on a developer product say a version of this. Mine is that failure is a first-class state, not an edge case. This small site has taught me the same thing three times.

Key idea 2 of 7

An error message is an interface, and most are written by the thing that failed

The error message is the interface the developer actually reads. Most are written by the thing that failed, by an engineer handling the exception, not by the person who will read it. “Deploy failed” is a status. “The artifact could not be built because one tracked path is a symbolic link the runner cannot follow. Here is the path, and here is the check that would have caught it before the push” is an interface.

The second is what the deploy check I wrote for this site says now, in substance. It names the link, where it points and why the upload cannot follow it. The first is what the deploy said the day it failed fourteen seconds in, on a tree that had passed every check. A folder had been committed as a link to a path that existed on one Mac.

A status, and an interface

A status

  • Says that something happened
  • In the words the machine used
  • Written where the exception was caught
  • Ends the conversation

An interface

  • Says what happened, to what, and why
  • In the words the person uses
  • Written by the person who designs the product
  • Ends on the one thing to do next

An error is a state of the product, and every state gets a decision. So the error gets one too: what does the person know, what do they need, and what is the one thing to do next.

That decision gets made in a design review of the strings. The engineer who owns the code that throws them has to be in the room, because only they can change it.

Key idea 3 of 7

Silence is the worst failure, so nothing should look like nothing

Worse than a bad error is no error. The build that produces one of this site’s stylesheets emits only the classes it has seen a page use. A class the markup names and the build never saw is not an error. It is nothing at all: the type measures wrong at every width and the console says nothing.

A merge that passed every check and never deployed did not look broken. It looked unchanged, which is indistinguishable from a change nobody made. Silence is the failure a developer cannot work with, because there is nothing to search for.

So make nothing look like something. An empty list says why it is empty and what would fill it. A skipped step says it was skipped. A deploy that did not run is a row that says so, not an absence in a list.

The empty state is the product telling the truth about a moment when it has nothing to show, and the truth is almost always a reason.

Key idea 4 of 7

The first ten minutes decide whether the tool is believed

A developer arrives with a repository and a deadline: sign up, connect the repository, first deploy. Everything the product will ever be believed about is decided in those minutes. It is decided not by whether the first deploy works but by what happens when it does not.

If the first failure explains itself, the person learns that the tool tells the truth, and every later green line is read as true. If it is a status, they learn the opposite. They keep a terminal open beside the product for as long as they use it.

That is the same calibration the AI articles on this site are about, arrived at from the other side. Trust in a tool is less a feeling about the brand than a record of whether the thing said what it was doing. The person keeps that record from the first ten minutes on.

Key idea 5 of 7

The log is the interface of last resort, so design it as one

Most of what a developer’s tools say, they say in a log: a plain record of what ran, in order, with what result. It is the least designed surface in software and usually the most literal. The developer reads the record when the answer is in it. Two things follow for the designer.

The first is that the log is read at the worst moment, so it should be designed as an interface. The failing line comes first. The noise is folded. The step names and timestamps sit in a column the eye can run down.

The second is that the screen above the log should be built from the same record, not a summary that can drift from it. The record is not infallible either. A log can be incomplete, stale, or pointed at the wrong thing.

Two of the browser checks on this site once measured the wrong tree and printed their results under the right name. Now every one prints the path, the page count and the commit it measured before it does anything else. A summary is an interpretation of the record, so the screen has to let the person reach the evidence it was derived from.

The dusty slate breaker panel with every switch flipped up and in line, and beside the one that had been down, a single small round lamp filled butter yellow.
The tripped breaker, found: the whole panel said nothing, and the one switch out of line was the answer.

Key idea 6 of 7

Density is a kindness when attention is short

A developer under pressure is short on attention, not on data, the same as a dispatcher with a load to place. So the page has a hierarchy for scanning:

  • What failed, at the top, in one line
  • What it affects, next
  • The detail, folded under it

Progressive disclosure is order, not hiding. The page also runs at two densities, comfortable and compact, because the floor is denser than the demo.

Three numbers from this site’s own bad days
14
seconds into the deploy that a green tree failed, on one file the runner could not follow
0
errors the stylesheet build printed for a class it had never seen, while the type measured wrong at every width
10
minutes the host keeps serving the old page after a deploy, so a fetch straight after one proves nothing

The Deploy Console is my attempt at that screen, on synthetic data. Six services sit in one table. Every column is sortable, and every row opens on its history with the failures kept in it. The rows run at comfortable or compact.

The failed build is gathered onto one card:

  • The command and the line it stopped on
  • The commits since the last good deploy
  • What depends on it
  • The log, one press down, with its failing line marked again

A summary sits on top, and nothing is collapsed behind it.

Key idea 7 of 7

The questions come in an order, and the screen can follow it

Ship through a developer’s tools for a season and a pattern shows. Whatever went wrong, my questions were the same, in the same order. Each tool exists to answer one:

  • What is live right now? The deploy log.
  • What failed? The check.
  • What changed since it last worked? The diff.
  • What depends on the thing that changed? The history.
  • Can I put it back safely? The branch, because a change that lives on one commit is a change you can revert with one.

That order is an information architecture, and the Deploy Console is arranged in it, top to bottom. The first question turned out to be the one most screens answer badly.

A list sorted newest-first puts a failed deploy at the top, and a developer reads it as the one serving traffic. So the console keeps the word live for the deploy that is serving, and never in the same cell as the newest attempt. When a build fails, its first sentence says nothing is down.

The order is mine and comes from shipping one site alone. The console’s own page lists it as an assumption. On a team the first question may be who pushed, and someone who has carried a pager may want the log before the summary.

It is also close to the order an operations floor asks in when a transaction stalls, which is another article. Knowing the questions in order, from having asked them yourself, is the best qualification I know for designing the answer.

The honest map, since a team finds it out either way. What I run daily, on this site: the branch, the commit, the pull request, the checks and a static deploy. Every incident above is mine.

Containers, cloud infrastructure and observability across many services are the part I have read and not run. The way in is the same as it was for the rest: ship something through it, and read what the log says.