Good tools are rare. We should make more!

Most tech companies are full of different custom helper tools. I don’t even mean “big” tools — like frameworks, libraries or programming languages. Think about the little apps we all use to help with debugging or creating test objects. Or your Feature Flag management system — or the inspection tools that Customer Support uses to help your users.

It’s rare that these tools are exciting, and it’s not often they are appreciated or much cared for either.

This is understandable — in some ways, I don’t want my tools to be exciting. I want them to let me do what I need to do, and allow me to get on with my day. From a certain angle, I want them to be invisible.

We need good tools. We deserve good tools! Our users deserve us having good tools.

So what makes a tool good?

Here are a couple of guiding principles that I think are helpful to keep in mind when working on your tools:

Accessible

I think this is simultaneously the easiest and hardest thing to get right. Usually, when working on tooling, we’re hyper-focused on a specific problem.

This makes it easy to also make a hyper-specialized tool that requires a lot of project/team/domain specific knowledge to be able to use well.
To some degree, this is inevitable — if you’re working on a tool that helps with managing microservices, people using the tool need to have a concept of what a microservice is!

But there’s an opportunity here!

Can you make that accessible to people whose day-to-day life doesn’t revolve around Kubernetes, Helm, and Terraform? Can you make your tool hide some of the underlying complexity?

Can you simplify adding a new service, so that a mobile engineer can spin up an experiment easily? It’s not easy, but it’s work that often pays off in the long run.

Easy to use

Another aspect of this is also just making things pleasant to use.

If your underlying model for a field technically accepts arbitrary strings, but 95% of values are gonna be literally “true” or “false” — provide affordances for that. A simple toggle or a button that preselects one of the values is very simple to add, but makes the interactions so much more pleasant.

Typing in “true” once isn’t the end of the world. However, making tens or hundreds of your coworkers do it multiple times a day isn’t great.

Another often forgotten aspect — performance is also an important feature.

You probably don’t have to sweat every last millisecond, but if your tool takes 10s to load a simple list, it’ll be frustrating to use.

Working on making things faster is one of the easiest ways to get into the good graces of your fellow engineers. This extends doubly so to anything used directly when interacting with code — there’s no easier way to slow the company down, than by adding a couple seconds on a critical path when rebuilding the app. Shaving those seconds from an existing state will make you a hero.

Complete

A corollary to the previous principle is that one of the easiest things to make your tools easier and nicer to use, is to just make them do more.

Maybe it’s just my personal pet peeve, but nothing takes me out of a flow quicker than having to jump from tool to tool.

You want to let people stay in one place as much as possible. If you’re working on, let’s say for a sake of argument, a sort of a Marketplace, where people sell and buy items? Let them create new test accounts, fund them, create new items, create transactions, change shipping statuses, send reviews, etc — all from one place. Can you imagine how tiresome it would be if doing each of these steps required you to use a separate tool?

You have to, of course, put the limit somewhere — you don’t want to end up with an unmaintainable kitchen sink of utilities that is impossible to navigate and maintain.

In my opinion, however, that line is probably higher than most people think.

Open

In a company full of engineers, you’ll very quickly have people being annoyed by perceived deficiencies in your tools.

Some of those will just complain to colleagues — but some of them will eventually get fed up with the problem. They’ll try to take things into their own hands and improve the tools, even though they’re not owned by their team.

This is the best thing that could happen to you. Your tools are now better, and you didn’t have to lift a finger.

Alas, engineers are territorial and opinionated creatures.

This is a controversial stance, but I think unless something is an egregious pile of hacks — if it makes the experience of using the tools unambiguously better, you should just accept the changes.

It doesn’t matter if the ~ vibes ~ of the codes are off, if you’d have architected it slightly differently, if you don’t like how the strings are named.

Is the tool better with the PR than without, and likely won’t cause immediate problems? Accept it.

It’s of course absolutely fine to have feedback, and suggest improvements! But if they’re not absolute deal-breakers, they shouldn’t block landing the change.

What it boils down to is: The barrier to accept changes to your own tools should be lower than to the code you’re shipping to customers. If it’s the other way around, something is wrong.

There are, of course, times when this is unfeasible, or tools need to be closely controlled and guarded for security and/or audit reasons — but thankfully, for the vast majority of situations, that’s not the case.

Make your tools easy to contribute to, write basic docs, and your tools will soon start improving without your involvement.

Extendable

This is corollary to the “completeness” argument — your team will never predict all the use cases or issues other teams will hit. It’s great if your architecture allows people to layer their own customization on top of your own tools. But it’s also fine for simple things to be duplicated and live in multiple places.

Think about feature flags — there’s always some “canonical” place to add overrides and whitelist yourself for tests or development. But that very well could (and should!) live inside the app too!

Making a simple interface to allow people to add local overrides takes a couple of hours, but it will very, very quickly pay for itself by people being able to just stay in the app when testing something, without having to jump back and forth between the browser and the app.

Your internal tools aren’t programming languages — it’s fine for there to be more than one way to do something.

Not forever

This is probably obvious to some, and sacrilegious to others.

Tools you build don’t have to be temporary, but it’s fine if they are. If they have served their purpose, it’s fine to let them go.

On the flipside, it’s also completely fine to build them knowing that they will be obsolete soon!

Let’s imagine you’re waiting on a sibling team to finish their API. The API not being deployed makes testing your UI much harder, because you don’t have an easy way to get your app into the required state.

If the surface area of your UI is big, it might make sense to add a little helper inside the app to completely ignore the API, and just set up the correct properties manually.

It might be obsolete in two weeks when the API actually shipped, but you have made more progress in the meantime by not being blocked or slowed down by the lack of it.

Most things in life are temporary. It’s fine for code to be too.

Cost of bad tools

So what’s the worst that can happen when your tools get neglected, or are never cared for in the first place?

Every chef and woodworker knows that blunt tools are dangerous. A blunt knife is more dangerous than a sharp one because it’s unpredictable. You know exactly what a razor-sharp knife will do, and you can position yourself to mitigate any danger.

Thankfully, working on software rarely has catastrophic failure modes of losing a finger; but bad tools can still be costly – but not always in obvious ways.

When a tool is unreliable, slow, or just straight up buggy — it’s very easy to notice (and measure!). But sometimes some things are just unpleasant, or tedious to do. It’s easy to dismiss those — “oh, it’s just an unfinished UX”. But those can be damaging in the long run too.

Having to jump between five different apps — some of them in Slack, some documented in Jira, some living in an internal portal, some requiring extra third-party apps open is not free. Every new interaction adds that little extra bit of cognitive load, that little extra bit of friction. None of them feel like a big deal in isolation, but they do add up pretty quickly!

People have different tolerances for tedium; but everyone has a breaking point. The idea of testing another potential edge-case is unbearable, because it requires clicking through 10 different dialogs, and things get overlooked. It’s death by a thousand paper-cuts.

So how do good tools look in practice?

My favorite improvement this year was adding a completely new debugging layer in our iOS and Android app. We’ve had an internal debug menu; but recently when working on Hassle-Free Car Sales we’ve extended it to be helpful on that project specifically.

This project was, in fact, one of those mentioned above — the client engineers had a couple of weeks of head-start, compared to backend. We very quickly decided to focus on getting the UI right, and leave integration with actual backend services to the very end. We had a rough idea of what the API shape will be when starting, but didn’t spend time focusing on the details until much later.

To let us effectively work on it, my teammates added a sub-menu that let us ignore the network entirely, and just override required properties directly in the app.

This shaved weeks from the project time — we were able to test and QA a good chunk of client-side code, before a single backend service was ready.

The override menu being directly in the app also encouraged us to test things more thoroughly — being able to toggle between all the different states without ever leaving the app dramatically reduced how much friction it took.

Other things we made better this year include significantly cutting down on the amount of disk space and time that our iOS unit test takes; making the UI for the Feature Flags much nicer to use, and adding an on-device visualization of all the analytics calls we make.

That work wasn’t always easy or pleasant — but it has universally paid off.

That’s of course only a small (and very mobile-centric!) chunk of the work we’ve done — and there’s more on the way to ship in 2025.

Hope you’ve had a good 2024, and wishing you the best (tooling) in 2025!

  • X
  • Facebook
  • linkedin
  • このエントリーをはてなブックマークに追加