< expLog

DevTools > A checklist for a well-built tool

Building anything involves trade-offs: this particular checklist describes the direction I try to push trade-offs towards, to the best of my ability. I strongly recommend designing and building your tools carefully and thoughtfully – the craft applied will shine through.

Make it as simple as possible

As toolsmiths, we have way too much space in our heads for the tools we use; we're constantly evaluating how and why they work. And that we could have done a much better job. Obviously.

Engineers trying to do their day jobs have a significantly different perspective: they encounter dozens of tools every day. Each of these tools is yet another annoyance they need to navigate as quickly as possible. Several web apps, some form of IDE, source control, some compilation/build mechanism – just to get started. Then there'll be tools for deployment, data management, and actual business logic.

Make usable tools that use the least amount of energy and time to operate correctly. Aim to build a minimal and functional user experience and lightweight design with minimal cognitive overhead. In other words, make sure you add affordance to your tools.

Make it magical but also transparent

I greatly appreciate tools that configure themselves to my circumstances automatically and tells me the best options – tools that push me into the pit of success. At the same time – they should also explain why these options are the best.

Make tools that are amazing even if I want to do something you didn't anticipate. Provide information around why it's taking certain decisions – potentially with inline logs, tooltips, or even an easily accessible "run log" that captures everything that happened. Give users the freedom to choose the (hopefully) rare instances they need to go against defaults.

Make the common workflows easy

Work that needs to be done frequently, particularly by new users, should be trivial and intuitive; without overwhelming them. Things should just work – and be magical. Push your n00bs into the pit of success, and teach them along the way.

Structure tools in a way that layers complexity: everyday work should require minimal effort spent learning how to do it.

Make the rare workflows possible

Leave space for power users who need to do complex work: don't neuter the tool to keep it easy for new users. It's surprisingly easy to accidentally hobble tools too much with the idea of avoiding complexity.

Leave space for people to completely override the tool's decisions if they're willing to accept the consequences of doing so, which is why transparency plays a vital role. Respect your power users as the ultimate experts and let them do what they need to.

Enable customization

One of the balances tool builders need to strike is between building a generic but not particularly useful tool – or one that satisfies the very custom needs of every single team and looks like an aeroplane cockpit.

Instead of painfully trying to navigate this tight-rope with unsatisfying compromises everywhere, make it possible for users to customize tools to their satisfaction. Your customers are engineers too, and you should let them use their skills to maximize the value they get.

As always, there are trade-offs with this approach – particularly around ownership when things break and the added cost of maintaining a reliable interface for customers. You can mitigate this partially by sandboxing customer-specific changes to each customer – they're allowed to make the tool their own but can't break it for anyone else.

Enable composition

Command-line tools almost get this for free, thanks to pipes, shell scripting, and the ability to quickly duct tape together text parsing. Explicitly supporting composition – with JSON outputs, clean interfaces, or treating flags as an explicit API – makes tools far more valuable and useful.

UIs, unfortunately, have generally been terrible in terms of easy composition. You still have options – make it embeddable to allow reuse, allow accessing the underlying data with an API, explore options like Jupyter Notebooks – or Retool; keep the UI decoupled from the inputs and outputs, making it easier to compose.

Some tools also allow for both a CLI / usable library and a browser-based interface, which helps significantly. Ensure the two implementations are tied together cleanly, and ideally, each interface coaches you on achieving the same results using its counterpart.

Persist navigation state

It often takes several clicks to find the right view in a UI, making shareable and persisted state extremely important. Good UIs make it trivially easy to bookmark a particular state – to return to or to share.

If you frequently find instructions that ask users to follow several steps walking through a UI, you probably need to make it possible to link to different desired states directly.

For a CLI, an equivalent is helpfully printing out a direct command – or generate a file – after a user navigates through many menus and chooses options. A good example is how menuconfig complements other more direct mechanisms.

Fast, or at least asynchronous

Fairly self-explanatory – the whole point of building tools is to make people more effective, and a painfully slow tool that eats into everyone's time is hardly valuable (except for when the alternatives are even worse).

Never make customers explicitly wait for results – allow them to exit and return when convenient, possibly with a notification on completion. The last thing I want is to spend hours waiting and then lose all progress because my browser tab crashed.

If it's prohibitively costly to be faster, try to hide the slow parts instead, possibly by pre-computing and caching results.

Reliable & Trustworthy

Broken tools lead to unhappy engineers, particularly those investigating something complex. You never want to be in a position where you're not quite sure if you've identified a problem or it's simply an artifact of a tool you're currently using.

It's unfortunately easy to have subtle yet extremely troublesome problems. For example, a tool I wrote to render JSON would silently mangle large numbers beyond Number.MAX_SAFE_INTEGER and corrupt large numeric IDs.

Identify these and fix them as soon as possible; better yet, anticipate the ones that could break the central value of the tool and solve them before release.

Make it Work, Make it Right, Make it Fast – Kent Beck


Add your comments to this Twitter thread, or drop me an email.