A UI might not make it easier

Posted on February 22, 2025

Building self-serve access to internal systems is generally a good idea. It would be silly to do engineering work to make a change when pressing buttons would do. The fact that it’s generally (and not universally) a good idea means that there are cases where it isn’t a good idea. The idea of making a task easier by making it self-serve, no code, in a UI shouldn’t be applied when the unavoidable complexity and nuance of the task to be done exceeds what a UI can reasonably handle.

You probably have examples of a story like this: A company has a problem to solve. They build a system to solve that problem. It does a good job of solving that problem, though only for that variant of the problem. Time marches on and the system gets expanded to handle more variations of the problem. This expansion requires bespoke engineering work each time. Some systems grow to the point where the expansion becomes routine. The internals of that system (assuming sensible leadership in that engineering organization) evolve into general-purpose pieces that can be reused in the different variations of the problem. Handling new cases may be as simple as just plugging together the existing pieces of the system in a new configuration. As more and more variations are handled, the unique engineering work fades and configuration work takes over. At this point it’s reasonable to ask the question, why does this require engineering at all? Wouldn’t it be nice if the person who came up with the requirements could open some admin tool and punch in what they need? Let’s build a self-serve UI!

As stated above, building this UI often is a good idea. As a rule of thumb, the simpler the configuration, the better this idea is. When systems are more complex, it doesn’t make as much sense to have a self-serve layer in front of them. If you find yourself saying, “this is trivial, why do we need engineers involved?” then by all means create a self-serve UI. But if you are saying, “this is hard, let’s add a self-serve layer to make it easier,” you might find yourself running into some problems. Having self-serve access to the system doesn’t actually make the hard parts easier, but it does make the system more rigid, and it risks giving a false sense of security while actually making the system more dangerous.

Here are some reasons why you might not want to build a self-serve interface:

It may not solve the problem

The work to be done is configuration. Or at least, it is labeled as configuration work. But the people doing the work know that writing the config file is easy. The hard part is everything else leading up to that point. The actual engineering part of the work doesn’t go away just because there’s a self-serve way to handle the configuration portion.

A project where the end result is a new configuration might start with changing the underlying system to support that new configuration. It may have already been possible to specify that configuration, but it might not have actually worked (perhaps there is an option for languages, but it didn’t yet support right-to-left languages, or the phone number validation doesn’t work for that country). In other cases it may not even have been possible to specify the desired logic and it takes engineering work to create a way to describe what you want in the configuration. The required engineering work could be subtler than that: it might work fine for any one user (or even a thousand) but run into some kind of scaling or cost problem when shipped to production traffic.

Any time a system is asked to do a new thing that requires engineering work, regardless of whether that new thing is described as a feature or as a configuration. Even determining whether engineering work is needed requires the input from an engineer.

Practical downsides

There are other practical downsides to structuring a config as data created by a UI and stored in a database instead of a config structured as a file checked in to git. You don’t (automatically) get revision history or deploy controls over the config.

Debugging can be harder as well. In a development environment you may not have a copy of the configuration as it exists in the production database. Accurately reproducing the bug now requires extra work to reproduce the configuration.

You can’t write tests for something that’s not there. Testing requires a copy of the logic being tested to live in the codebase. Often configuration written using DSLs will be treated like any other code and be subject to unit tests. This both tests that the configuration does what was expected and the system that is configured handles that config correctly. Without these tests, the new config is tested in production.

Building self-serve systems to handle complex cases is also hard work that takes a very real amount of engineering time. An accurate accounting of the real cost of building such a system (figuring out usecases, designing, building, iterating, documenting, teaching and learning) may reveal that it would cost far more to build than the work it could save - and it’s easy to overestimate how much work it will save. (See the previous point about solving the problem.)

The computer can’t think for you

When there are important decisions to be made surrounding the launch of a new variant of a thing, those decisions can’t be left up to the computer (at least not with today’s AI…). The computer hasn’t talked to customers, it doesn’t understand the goals of the company’s leaders, knows nothing of the company culture, isn’t qualified to understand today’s legal constraints, and doesn’t have experience with what has worked well or poorly in the past. It just doesn’t have the context to make decisions.

A self-serve UI can’t save you from novel engineering work, and it also can’t save you from thinking. You still have to understand goals, customers, priorities, communication channels, and everything else that goes into defining how this functionality will work and will be launched. Not only must this irreducible work be done, but someone also has to understand how to do it. They’ll have to reason it out from first principles or talk to people who have done it before. Computers can’t replace experience.

Experience gives an understanding of the implications of decisions. Experience can take you from thinking “we’ll refresh the data on all of the accounts” to “we need to start a conversation with the team that handles this contract to get our limits increased for this API that we call when refreshing that data.” Even if you have a 1-click way to start that data refresh process, someone still has to understand the implications of doing it.

It makes the system too rigid

Projects don’t exist in a vacuum. They impact the things around them. When self-serve tooling is bolted on top of a system, it tends to change that system. The self-serve tooling depends on the system, but the system also tends to start depending on that tooling: you often end up being forced to use that tooling if you want to interact with the system. If this doesn’t happen when the tooling is built, it can happen (intentionally or accidentally) later as the two systems co-evolve. This is fine if you are always happy with using that UI. Sometimes you aren’t.

A UI can do the things that it was built to be able to do, and only those things. A UI can’t make the underlying system more general, but it can fail to expose the full breadth of that system’s generality. This means you can run into things that you want to do, and that the underlying system is able to do, but that you can’t do because the UI (that you now have to use) can’t handle it. This is a case where the UI intended to make the process easier is ironically making it harder.

This situation tends to be handled either by working around the self-serve tools (perhaps doing sketchy things like changing values in the database by hand) or by taking the time to update the UI to support this particular use case. Some systems see a constant stream of issues due to the workarounds. Others cause a pattern of delayed projects due to the time needed to stop and update the UI.

A false sense of security

This isn’t so much a reason to avoid creating a self-serve UI as something to be aware of after creating it: the presence of a UI can make tasks look easier and safer than they actually are. Changes made to code are given a level of planning, thought, and scrutiny that presses of buttons are not.

When there’s a class of project that looks like it doesn’t require engineering effort - perhaps because there’s a nice shiny UI or perhaps because this kind of project sometimes really is possible without engineering effort - plans get made assuming the project will not involve any engineering effort. That might not be true in this particular case. The relevant engineers are finally consulted late in the project, and everyone is dismayed to find out that this actually will require engineering work, work from an engineering team has no spare capacity this quarter (“look at all the super important things we already had to cut!”) and even if they did, there’s no way it could be done in the 2 days before this thing launched. Some readers may have just felt their blood pressure increase.

To generalize that idea, it is dangerous to make a type of project appear too easy because that will set an expectation that it will actually be that easy. Take lessons from Star Trek’s Scotty.

There’s a point that is worth bringing up again: projects often require expertise and experience in that kind of project. For someone new, there can be a lot of unknown unknowns. Tooling that abstracts over details that are actually important can result in a project being undertaken by people who, through no fault of their own, are lacking critical knowledge to make that project successful. They may not even know all the things that they don’t know. The people who built this UI have this institutional knowledge, but since they aren’t involved in the project, that expertise doesn’t get transferred to the people doing the project. That institutional knowledge can also rot if they continue to not be involved in these projects. Simple interfaces give the incorrect impression that only a simple understanding of the project is needed. Planning and staffing decisions made based on that incorrect assumption.

Making a task self-serve doesn’t inherently make it safe. It certainly can, and this can be one of the strongest arguments for adding UIs. You can write any kind of logic you like to validate and safety-check, to require second-person approval, and anything else you can think of. For a lot of projects this really does make the operation quite safe! However, there are some projects that you can never make truly safe. No matter how nice of a UI you put on a flamethrower, it’s still a flamethrower. But that UI could make it seem safe, at least until someone’s car is on fire.

You can incrementally make UIs safer over time by adding more warnings and safeguards. However, know that those changes will be written in blood. You will have to accept making big mistakes with a UI before you’ve found all the ways it can be dangerous.

Self-serve UIs can even introduce safety problems of their own. This can happen when it is too easy to misclick something you can’t undo. Another common pattern is an operation having very unexpected side effects (think along the lines of, you update a minor setting only to fnd out that doing so resulted in it emailing thousands of users).

Conclusion

Many if not most projects to give self-serve access to systems don’t run into these problems. If your goal is to minimize tedium, and there is genuinely very little decision-making, planning, expertise, engineering, or risk management required each time, then you are probably fine. By all means make the process easier.

But if you are trying to make hard work easier by adding a self-serve UI, consider whether you have a complete understanding of what was making that work hard in the first place. You might only be making it easier to accomplish the nominal task (“create a new config”) but not to do the actual work behind it.

When a project to build a self-serve UI is on the table, two things are clear: (1) there’s a problem to solve, and (2) there’s some engineering effort that can be thrown at the problem. When those are both the case, it may pay better dividends to approach the problem by getting a little bit deeper into the weeds. If there are sharp edges in the existing system that keep causing issues or deficiencies that keep having to be worked around, fix those (especially if everyone has given up on those problems being solved and considers them to be an intrinsic part of this kind of project). Make the system more general, make it easier to understand, write friction logs and fix that friction, fix the underlying data model - there’s an endless set of projects that might make future changes easier.

If you do build self-serve tools, make them as specific and orthogonal as possible. Let users recombine them in ways you had never expected.