Joseph Pacheco is a software engineer who has conducted over 1,400 technical interviews for everything from back-end to mobile to low-level systems and beyond. He’s seen every quirk, hiccup, and one-of-a-kind strength you can think of and wants to share what he’s learned to help engineers grow.
If you're a software engineer, you're also a UX designer — or at least you should be. While you may identify first and foremost as a problem-solver, your ability to allow other developers to use your solutions is just as important. Embracing the mindset of UX design will make you a better developer in about every way, and it will help you advocate for sacred engineering principles like “clean code.” Allow me to explain.
We All Know Why Product UX Is Important
Imagine you work for a large company that relies on custom software tools, a health insurance company for example. You're one of the non-technical employees that uses those tools. Management cares about features and nothing else. They see no business case for investing in good UX and think they can just force their employees to receive trainings and read manuals.
As such, the tools are a nightmare to use. They're convoluted, monolithic, and ugly. Employees hate them. Even the simplest of tasks take an ungodly amount of time (and have all sorts of unintended consequences that are completely counter intuitive). Some employees feel anxiety when faced with the prospect of working with these tools for any amount of time. Some just find the experience arduous and frustrating.
In short: The tools themselves are a barrier to productivity.
'Clean Code’ Is UX for Developers
If you're a developer, this probably sounds familiar. Go back to that example above and swap health insurance worker for developer, a “user” of code. The design and architecture of code is the equivalent of user experience for engineers. When code is poorly designed, or sloppy, or confusing, it leads to all the same sorts of problems as poorly-designed enterprise tools. This is why developers care about so-called
clean code. Bad UX is bad UX, no matter the context, and it always leads to major losses in productivity, which I suspect is more costly than investing in good UX up front.
For example, I once came across a class designed to make a network call to a server for a list of articles to display in an app:
The problem here is that I can’t just use this
Articles class, I really need to think about it before I even attempt. First off, judging by the name itself, this class could do a bunch of different things. It could be the class that fetches articles from the server, or it could be the class to access where the articles are stored on disk, or a wrapper for an array of articles in memory, or who knows what else. So, I now need to confirm that this is the class I want to use by analyzing the code before I’m even sure I should be using it for my intended purpose.
But when I start to dig into the code, I run into another problem: the function names are basically indistinguishable. That is, if you had to guess on the names alone, what would be the difference between
getArticles? Perhaps one uses “get” to refer to making a GET request from the server, but then what exactly does “fetch” mean? To find out, we now need to spend more time analyzing the method parameters. Yippee!
fetchArticles returns a dictionary of strings to strings in a completion block, which probably (but not certainly) means it’s returning some representation of JSON returned by a server.
getArticles on the other hand, returns an array of
Article objects in its completion block, which probably means I should use this one if I want proper model objects I can use in the rest of my code. But again, we still need to now analyze the code itself to be sure. And all this analysis is taking a toll on my daily allotment of brainpower and sucking up god knows how much time I could be doing other things like using the actual class. That is, this lack of UX is materially slowing down my dev time. But what if it didn’t have to?
While even this example has its problems, it’s orders of magnitude better than the
Articles class. For one,
ArticleBackend immediately communicates that this is probably the way to fetch something from the back-end service that hosts the articles. And in case that wasn’t clear, you can double check the comment without having to analyze the code at all. On top of that, the functions are grouped into two sections (“Public” and “Private Helpers”), which makes it immediately clear where I should look if I want to simply use this class elsewhere in my code. But even if they weren’t grouped as such, the function names are unlikely to be confused.
fetchArticles is a clear descriptor of the stated purpose of this class, while the helpers clearly describe their roles in the overall goal of fetching articles. As such, less time wasted analyzing, more time using. Better yet, less time wasted being frustrated and confused, more time being motivated to add features and use the code.
‘Clean Code’ Doesn't Always Resonate
In my experience, the ideas of
clean code and
good vs bad code have a bit of a branding problem. Terms like this don't really do a whole lot to communicate the virtues behind the idea. They're a bit too abstract. Rather, they just create basic-sounding dichotomies that flatly reduce to good vs bad: clean vs dirty code, beautiful vs ugly code, good architecture vs bad architecture, elegant vs inelegant, and so on. The problem is that developers really struggle to see the deeper meaning until they are so seasoned that the ideas become self-evident. To someone who's junior, it just feels like finger wagging:
do the good thing, don't do the bad thing, because good is better. This just isn't an effective way of getting buy-in, so a lot of developers resist until reality repeatedly knocks them for a loop.
For example, from a junior developer’s perspective, the need to break larger functions into multiple, smaller functions might not seem like an urgent necessity. It actually takes quite a lot of effort (at first) to interrupt your thought process to reconsider the ways you might regroup your chunks of logic. And when there’s pressure to get features out the door, the looming nature of the deadline tends to override any tolerance for niceties. And frankly, when we frame the problem as simply “we should write ‘clean’ code,” it can very easily be interpreted as nothing more than an abstract nicety that we can get to later. We need a way of talking about this that speaks more readily to the point of why we care.
‘UX’ Is a More Helpful Framing
We have finally reached a point where the importance of user experience in software products seems to be at the forefront of everyone’s minds. There was a time when a lot of engineers scoffed at design requirements they didn’t understand, and UX principles even had to be explained to visual designers that were working on the UI. But now, more than ever, all teams involved seem to understand that products cannot and will not succeed without excellence in user experience.
The problem now is that a lot of us seem to think this only matters for consumer products. But what about internal tools used by teams? Just because they have captive audiences that are compelled by their jobs to use the tools, doesn’t mean the problem goes away. Take our earlier example of the health insurance company. Every time an employee unconsciously resists the internal tools and checks Facebook, that's a cost. Every time an employee makes an all-too-common mistake with the tool, that's a cost. Every time an employee just can't figure out what to do and has to ask a more senior person, that takes time away from the productive output of two employees (or three or who knows how many). Every time an employee (or bunch of them) feels angst triggered by the tool, it affects morale, which is a cascading cost of epic proportions. When UX is not prioritized in the design of internal tools, it costs companies millions and millions in peripheral costs that go far beyond hiring a decent UX designer.
Again, these exact examples can be swapped for situations developers experience as a result of poorly designed code. If we don’t prioritize good UX in the design of our internal tools (the code itself), then we face the same business repercussions and costs as any other example of bad UX.
To me, thinking (and talking) about clean code as a form of UX has several key benefits. First, we get to reference all the well-documented lessons of bad UX that are widely discussed and reinforced by everyone beyond just the engineering team. This allows us to understand implications that go beyond mere inconvenience for ourselves (which is easier to put on the back-burner) and reframe in terms of the broader implications for the company. And in doing so, it might make it easier for us to make the case to prioritize time for clean code to other teams who look at code like it’s a secret alien language because “UX” gives us common ground to stand on. On top of that, the mindset needed to write clean code is indistinguishable from the mindset of designing great UX, and developers who are able to do so are wearing their UX hats whether they realize it or not. They aren't thinking in terms of arbitrary rules to follow, but rather the deeper goal that goes beyond more abstract ideas like
readability. That is, mentally anchoring on
ease of use for each other suddenly makes principles in books like Clean Code and Beautiful Code make intuitive sense.
UX Skill (in General) Has Wide-Reaching Benefits
Besides, steeping oneself in traditional UX principles has benefits across the board, not just on the way you design your systems and write code. If you really train yourself to have a habit of empathy for those using both your code and the software that results from your code, it will help you better meet the business goals that keep the whole ship running.
That is, when everyone on the team deeply understands the point of UX as well as at least the basic nuances, there's a lot less miscommunication between product and engineers (and a lot less rework). Suddenly the intent behind requirements from your product owners and designers become more intuitively obvious, so even in the many cases when they forget to communicate a fine detail, you end up landing on exactly what they were intending in terms of your implementation of the component that ends up impacting the end-user experience. Of course, this is especially relevant to front-end developers, but it also has unexpected consequences for those further from the front-end in terms of a willingness to go out of one’s way to fully accommodate business requirements that are often imperfectly articulated. “He who has a why can bear almost any how,” (h/t Nietzsche) so it benefits everyone to be aligned on the deeper whys behind product decisions.
If you're a software engineer, a foundational aspect of your role is user-experience design for other developers. Problem-solving is of course the central focus, but you cannot do that effectively on teams without a significant concern for making your solutions usable by others. The mindset of user-experience design is a great way to anchor yourself on the goal behind ideas like
clean code. Beautiful code is beautiful for the same reasons beautiful UX is beautiful: they both empower their users with a sense of uninhibited possibility through a design that is obvious in its utility and relatively frictionless in its use. The user can therefore connect directly with their imagination and let it flow in a torrent of energy until their vision becomes reality. What's more fundamentally beautiful than that?