The UI Looked Simple, The Engineering Wasn't

When I first reviewed the UI guide for SmartThings Find for iOS, I thought the project would be relatively straightforward. There weren't many UI components, and most of the screens looked familiar.
However, as development progressed, I realized that I had underestimated the complexity behind those screens.
SmartThings Find was already a mature experience on Android. Bringing similar functionality to iOS meant dealing with years of accumulated product behavior, platform differences, and requirements that weren't immediately apparent from the interface.
As the frontend team lead, this experience taught me a few lessons about evaluating project complexity and designing software architecture.
The complexity behind a simple interface
As frontend engineers, we often start evaluating a project by looking at its interface. We identify reusable components, consider state management, and start thinking about how to structure the application.
That's exactly what I did.
Consider a feature that displays a device's location. The interface might only need a map, a marker, and a few buttons. But behind those components, there are numerous scenarios to handle.
What happens when the location is unavailable? What if the device hasn't reported its position recently? What if the user hasn't granted the necessary permissions?
Each scenario introduces additional logic that isn't necessarily reflected in the UI design.
I had focused too much on the visible parts of the application without fully understanding the behavior behind them.
Platform differences add another layer of complexity
Another challenge was reproducing functionality from an established native application in a web environment.
Although different platforms may offer similar capabilities, their implementations aren't always compatible.
Take cryptography as a general example.
Both Android and modern browsers provide APIs for encryption and decryption. However, differences in supported algorithms, parameters, and key formats can make interoperability challenging.
Even when both environments support the same algorithm, their implementations must agree on the necessary configuration and data representation to exchange encrypted information successfully.
When working with an established system, changing the existing cryptographic mechanism may not be an option because compatibility needs to be preserved.
And cryptography is only one example. Similar challenges can arise with permissions, operating system capabilities, and other platform dependent functionality.
Reproducing an existing interface doesn't necessarily mean reproducing its behavior will be straightforward.
This is why I now believe that understanding platform limitations should be part of the initial project evaluation, rather than something discovered during implementation.
Good architecture helped us adapt
Despite underestimating the initial complexity, one decision proved particularly valuable: investing in the application's architecture early.
Throughout development, we encountered changing requirements, unexpected limitations, and issues that required us to adjust our implementation.
Having clear architectural boundaries made those changes easier to manage. Engineers could work on their assigned features with fewer dependencies on unrelated parts of the application, while changes could be accommodated without constantly restructuring the codebase.
Without that foundation, I believe meeting our delivery commitments would have been considerably more difficult.
Of course, architecture alone wasn't responsible for our delivery. Testing, communication, and the team's effort played equally important roles.
But this experience reinforced something I strongly believe:
Good architecture becomes particularly valuable when requirements change.
That doesn't mean introducing abstractions everywhere. Unnecessary complexity can make development harder too.
The challenge is identifying which responsibilities should remain separate and where flexibility is worth the additional effort.
Sometimes spending more time designing the foundation can save considerably more time later.
What I would do differently
If I were assigned a similar project today, I would spend more time understanding its behavior before estimating the implementation.
Rather than relying primarily on UI guides, I would examine the existing product, identify its dependencies, and investigate potential platform limitations.
I would also make a clearer distinction between what we already know and what we're assuming.
An unfamiliar requirement might resemble something we've implemented before, but that familiarity can lead to inaccurate expectations.
At the same time, I wouldn't spend an unlimited amount of time planning. Unexpected problems will always emerge during development.
The goal is to identify the most significant risks early enough to make informed decisions, while maintaining enough flexibility to adapt when circumstances change.
Final thoughts
I'm proud that our team successfully delivered our part of SmartThings Find for iOS, especially considering the challenges we encountered along the way.
Looking back, the project taught me two important lessons.
First, the complexity of an application cannot be reliably judged by its interface alone. Understanding its behavior, dependencies, and platform constraints is equally important.
Second, thoughtful architecture gives a team room to adapt. We couldn't anticipate every challenge, but the foundations we established helped us respond to changes without losing control of the implementation.
As a team lead, I also learned that these responsibilities are closely connected. The better I understand a project from the beginning, the better I can plan its architecture, distribute responsibilities, and support the team throughout development.
It's something I'll keep in mind the next time a project looks simpler than it actually is.