A previous discussion highlighted the importance of acknowledging knowledge gaps. This perspective does not advocate for mediocrity; rather, it recognizes the vastness of the field. It is widely believed that learning can commence at any point, without a prescribed order for technologies. However, acquiring expertise is highly valued, particularly in the realm of user interface creation. The truly valuable lessons derived from experience are often difficult to articulate. This article attempts to catalog and describe some of these fundamental insights.
Numerous learning roadmaps exist for various technologies and libraries, often leading to confusion about which tools to prioritize (e.g., Vue vs. React, Redux vs. Rx, Apollo, REST vs. GraphQL). This focus on specific technologies can be misleading, as significant learning breakthroughs often occur when grappling with specific UI challenges. Solutions, whether discovered in existing libraries or developed independently, emerge from this struggle. The most valuable learning experiences stem from understanding the underlying problems, experimenting with the solutions, and employing diverse strategies. This discussion primarily addresses these core problems.
Those involved in user interface development have likely encountered some of these challenges, either directly or through the use of libraries. It is recommended to build a small application without libraries to experiment with reproducing and resolving these issues. There is no single correct solution for any of them; true learning arises from exploring the problem space and evaluating various tradeoffs.
-
Consistency. When a “Like” button is clicked, the displayed text updates (e.g., “You and 3 other friends liked this post”), and reverts upon a second click. This seemingly simple interaction involves several complexities: similar labels may appear elsewhere, other visual cues (like button backgrounds) might need to change, and a list of “likers” should reflect the new state. Navigating away and returning should not reset the “liked” status. Maintaining local consistency presents its own difficulties. Furthermore, other users can modify the same data (e.g., liking a post being viewed). The challenge lies in synchronizing data across different screen areas and ensuring consistency between local application state and server data, and vice versa.
-
Responsiveness. Users have a limited tolerance for delays in visual feedback following their actions. For continuous interactions, such as gestures or scrolling, this threshold is very low (a single dropped 16ms frame can cause a “janky” experience). For discrete actions like clicks, delays under 100ms are often perceived as equally fast. Longer delays necessitate a visual indicator. However, challenges arise: indicators that cause layout shifts or multiple loading stages can paradoxically make an action feel longer. Similarly, processing an interaction quickly (e.g., 20ms) but dropping an animation frame might feel slower than a slightly longer processing time (e.g., 30ms) with no dropped frames. User perception, not raw benchmarks, dictates responsiveness. The question is how to maintain application responsiveness across various input types.
-
Latency. Computational processes and network requests both consume time. While computational costs can sometimes be overlooked if they do not impact responsiveness on target devices (testing on lower-end devices is crucial), network latency is an unavoidable factor, potentially lasting several seconds. Applications cannot simply freeze while awaiting data or code. Consequently, any action relying on new data, code, or assets is inherently asynchronous and must manage a “loading” state, a scenario common across many screens. The challenge involves gracefully managing latency without presenting an overwhelming number of spinners or empty content areas, preventing disruptive layout shifts, and modifying asynchronous dependencies without extensive code refactoring.
-
Navigation. Users anticipate a stable user interface during interactions, meaning elements should not unexpectedly vanish. This stability principle also applies to navigation, whether initiated within the application (e.g., clicking a link) or externally (e.g., using the browser’s “back” button). For instance, switching between tabs on a profile screen (e.g., /profile/likes and /profile/follows) should not clear a search input located outside the tabbed area. Similarly, navigating to a different screen implies an expectation to return and find the previous state largely preserved, possibly with new updates. Losing one’s position in a feed after navigating to a profile and returning, or having to wait for it to reload, can be frustrating. The architectural challenge is to manage diverse navigation scenarios without losing critical context.
-
Staleness. Implementing a local cache can enable instant “back” button navigation by storing data for quick retrieval. However, caching introduces its own set of issues, primarily staleness. If an avatar is updated, the cache must reflect this change. A new post requires immediate appearance in the cache or its invalidation. Managing these scenarios can be complex and prone to errors. Questions arise regarding handling failed posts, cache retention duration, whether to merge newly fetched data with cached data or discard the cache, and how pagination or sorting are managed within the cache.
-
Entropy. Similar to the concept of entropy, user interfaces tend towards complexity over time. Predicting the precise sequence of user interactions is impossible, leading to an immense number of potential application states at any given moment. The goal is to ensure predictable outcomes, constrained by design, to avoid perplexing bugs. With N possible states, there are N×(N–1) potential transitions. For instance, a button with five states (normal, active, hover, danger, disabled) requires correct handling for 20 possible transitions, or some transitions must be explicitly disallowed. The challenge is to manage this combinatorial explosion of states and ensure predictable visual output.
-
Priority. Certain elements or tasks within a UI hold greater importance. For example, a dialog might need to render visually above its originating button, potentially exceeding its container’s boundaries. A new task, such as a click response, could take precedence over an ongoing background task like rendering off-screen content. As applications scale, code from various developers and teams often competes for finite resources including processor time, network bandwidth, screen space, and bundle size. While a shared “importance” scale, like CSS z-index, can be used, it rarely ends well. If every component is deemed critical, then none truly are. The challenge lies in fostering cooperation among independent widgets rather than resource contention.
-
Accessibility. Inaccessible websites represent a significant issue, not a niche concern. In the UK, for instance, disability impacts one in five individuals. (Here’s a nice infographic.) Personal experiences often highlight the difficulty of reading sites with thin fonts and low contrast, or navigating poorly implemented interfaces via keyboard. Applications must be designed to accommodate users with difficulties, and many straightforward improvements are available. This begins with education and appropriate tools. Furthermore, product developers need to be empowered to implement accessible solutions easily. The goal is to establish accessibility as a default practice, not an afterthought.
-
Internationalization. Applications must function globally, necessitating support for diverse languages and right-to-left layouts with minimal effort from product engineers. The challenge is to implement language support without compromising latency and responsiveness.
-
Delivery. Delivering application code to user devices involves selecting appropriate transport and format, a decision with numerous tradeoffs. Native applications typically pre-load all code, resulting in larger initial sizes. Web applications often have smaller initial payloads but may incur more latency during use. Key questions include determining the optimal point to introduce latency, optimizing delivery based on usage patterns, and identifying the data required for an efficient solution.
-
Resilience. While bugs are undesirable in software, some will inevitably reach production. Some bugs lead to incorrect but predictable behavior, such as displaying inaccurate visual output under specific conditions. However, a rendering code crash can halt meaningful operation due to inconsistent visual output. A single post rendering failure should not incapacitate an entire feed or lead to a cascading series of crashes. The objective is to write code that isolates rendering and data fetching failures, allowing the remainder of the application to continue functioning. This raises the question of what fault tolerance entails for user interfaces.
-
Abstraction. In small applications, many special cases addressing the aforementioned problems can be hardcoded. However, applications typically expand, necessitating the ability to reuse, fork, and join code segments, and to collaborate effectively. Clear boundaries between components, understood by different team members, are essential to prevent frequently changing logic from becoming overly rigid. The challenge involves creating abstractions that conceal the implementation details of specific UI parts and preventing the reintroduction of previously solved problems as the application evolves.
This list of UI engineering challenges is not exhaustive; other areas like designer-engineer collaboration, debugging, and testing remain unaddressed. It is tempting to consider these problems with specific view or data fetching libraries as immediate solutions. However, it is beneficial to approach them as if such libraries did not exist, contemplating how one might solve these issues independently. Experimenting with these problems in small applications is encouraged. These challenges are notable for their presence across all scales, from minor widgets like typeaheads to large applications such as Twitter and Facebook. Think of a non-trivial UI element from an app you enjoy using, and go through this list of problems. Can you describe some of the tradeoffs chosen by its developers? Try to recreate a similar behavior from scratch! Gaining a deeper understanding of UI engineering tradeoffs can be achieved by experimenting with these problems in small, library-free applications.

