← Blog

UI concerns are verticals

#engineering #ui #product

Recently, I came across a tweet asking if nice UI makes any difference if the product is already solving real problems. This is a sentiment I’ve seen many times over, often expressed by engineers who identify as “backend” engineers. There are many variants of this claim, including that “frontend” is much more interchangeable than “backend.”

I disagree. I reject that UI is limited to “frontend” and the real product lives in “backend”. This is a false dichotomy. Why? Because UI is what makes a system a product. Because UI engineering is product engineering.1 Because product solutions are end to end.

The following is a bundle of disparate yet interconnected and overlapping thoughts revolving around an idea that, when it comes to product engineering, UI concerns are verticals. They span the full spectrum. “Backend” cannot hide from it, and “frontend” cannot monopolize it. I really hoped to compose these thoughts into a single, coherent line of argument but got thwarted by my skill issue. I’m choosing pragmatism and letting these thoughts out as an essay instead of waiting for the perfect prose—a fool’s errand. Maybe a grand theory of UI and product will come to me later.

UI makes a system a product

One time I’ve heard a joke that goes something like, all useful programs are necessarily impure, 100% pure programs are only good for heating up the CPUs. I cannot recall exactly where I heard the joke or who first said it, but I’m fairly certain that this came from the functional programming community. As obsessed with purity as they are, these functional programmers know the importance and inevitability of interfacing with the world. Indeed, software that makes no difference to the world offers no value to the said world. The rubber must meet the road to move a vehicle forward.

I believe the same applies to UI. What is UI? It is user interface. And when I say user interface here, I mean all sorts of interface for a product, not just the graphical kind. CLI is UI to those who access a product via terminal. API is UI to those who consume a product programmatically. Even programming language syntax is UI to those who program in it and take advantage of the language features. Think about it.

And what is the job of an interface? I/O. A product’s UI defines how its users interacts with the system and how the system reaches the users. UI makes possible for users to act on the system and the system to provide feedback to users. Put differently, UI is what turns a software system into a product—a product with users, a product that delivers value to its users and generates revenue in return. No UI, no product. No product, no business.

UI engineering is product engineering

The whole point of any product is the serve the needs of its users. As product engineers, we cannot build something first and hope it sells. We start from user needs and build software solutions to satisfy them.

And that is why UI is such a core piece of any product. As the popular saying goes, users do not care about the code. But they care about the product and what it can do for them. I am not that crazy to equate a product fully with its UI. But from users’ point of view, a product’s UI comes close to everything they know about the product. This is not just about aesthetics although it’s a factor—a product’s UI constitutes the very structure of how the product and its users interact. UI dictates how users experiences the product and how the product comes in contact with its users.2

Therefore, UI cannot be a mere afterthought, an interchangeable skin to a system. A system built giving no thoughts to the structure of its interface would make a poor product. In turn, UI built with no understanding of the system it serves would prevent it from reaching its full potential as a product. A great UI and a great product go hand in hand.

It follows that UI engineering cannot be separated from product engineering. Most product concerns are also UI concerns because product concerns necessarily originate from user needs and UI is how system reaches users to address those needs. Not all UI engineers are responsible for implementing the entire stack to support UI features. But they all should have a good understanding of the product’s data model, access control, user input validation, and more. Better yet, whenever possible, UI engineers should be actively involved in designing and developing them to ensure that the system continues to address user needs effectively. UI engineers are product engineers.

Product solutions are end to end

One of my all-time favorite videos on YouTube is “Microservices” by Krazam. In the way only an excellent work of comedy can, this skit captures an absurd essence of the common software engineering experience albeit in an exaggerated manner. If you have never watched it before, please treat yourself and take a moment to enjoy it. Yes, this is a spoiler warning.

In the skit, a product manager approaches an engineer with a seemingly simple feature request: displaying user’s birthday on the settings page. In response, the engineer draws an elaborate diagram of multiple system components to explain how this is, in fact, an impossible task. The skit touches on many points, including the popular practice of giving silly names to internal services (“Bingo”, “Wngman”, “Galactus”, and “Omega star”), the frustration of being blocked by another team’s work (“Until Omega star gets their f*ing sh*t together, we’re blocked!”), the hidden complexity of make what appears to be a simple change (“Look, I’m sorry, we’ve been over this. It’s the design of our backend!”), and tons more.

Importantly, “Microservices” shows that product solutions are end to end. The system does not have to be as convoluted as the one shown in the skit. Still, a request from UI (e.g. the settings page) needs to get to the necessary data and surface it to users in a valid format. This round-trip of course involves authenticating users, checking their authorization for accessing birthday data, validating the requests and payloads, properly mapping requests and responses between internal services, formatting the date value to match the correct locale and time zone, and so on. And we all know that I’m barely scratching the surface here.

Of course, not everything has to change for each and every new and updated product feature. Some features may only require superficial changes to the existing system. But it requires an understanding of the full system even to see that. In other words, an engineer should know the product as a whole be able to determine which parts of the system need to change in order to support this or that feature.

Beyond the frontend/backend dichotomy

What changes together should live together. This is one lasting lesson from object-oriented programming that still resonates throughout software engineering. The OOP people call it cohesion in the context of modules. In today’s web development, colocation is the name for the the same idea applied to application state, logic, and view. Yet another name for this idea is locality of source code—and of behavior.

While many call themselves as “backend” or “frontend” engineers, the timeless principle of what changes together should live together tells us that the backend/frontend split is more accidental than fundamental from the product engineering point of view. We may tear apart a product into multiple layers of apps and services, but for instance, if collecting and displaying new information on UI requires a new column in a database table, they all are part of the same solution, frontend and backend. And if product solutions are end to end, those who build them should be too.

In practice, as product grows, it becomes untenable to keep the whole system under the responsibility of a single team, let alone a single engineer. Lines must be drawn, however arbitrary they may be. Drawing a good line is an art not a science, but generally, each team should be able to implement and own product solutions end to end. For individual engineers, it’s okay to start with a specific tool or technology and develop expertise on it. Feel free to dig deep and become React guru, Rails master, CSS captain, or Spring wizard. But don’t miss the forest for the trees—while we may not be excellent at everything, we should absolutely expand our horizons and grok the product we build as a whole.

And today, more tools and technologies are turning up to help cover the full specturm. In the JS world, the new React architecture is among the latest and most interesting approaches to it,3 but already Next.js offered some form of solution for years (API routes and getServerSideProps). And new projects and services keep launching to make it even easier to build product features end to end by productizing common concerns and offering ease of (vertical) integration.

The zeitgeist is also shifting. Using the JS world as an example again, the once ubiquitous notion of Jamstack insisting on decoupling JS client and JSON data API has fallen away. Meanwhile, solutions like tRPC have shown the benefit of server responses tailored to specific client needs. Extend this idea far enough, we arrive at serving UI directly over the wire—in the form of React Server Component or hypermedia as uniform interface. We’re rediscovering that UI and product concerns—often the same thing—cut across network boundaries.

Footnotes

  1. I owe the notion of “product engineering” to The Software Engineer’s Guidebook by Gergely Orosz. And it appears that he first heard it from this article by Sherif Mansour.

  2. To me, this explains why “thin wrappers” over other existing products continue to appear and find success. Different interfaces can turn the same “core” system solving the same problem into different, competing products.

  3. I’ve written on this and two other such approaches in my last post, “React, Solid, htmx”.