Embracing React Native for Lean Mobile Development (Part 1 of 2)
A look into how and why we migrated the TenX Wallet apps to React Native
Disclaimer: I’m no longer working at TenX. This post is the original article I wrote and published on the official company blog, while I was an employee. Since the company decided to move their blog from Medium to a self-hosted site (the moved version is here), this article is now published under my personal account.
In 2018, my team at TenX decided to rewrite our flagship app, TenX Wallet, from standalone native iOS/Android apps to a unified React Native codebase.
Launched two years prior, the Wallet app went through multiple design iterations, and accumulated significant technical debt.
In the second part of this story, we’ll dive into the criteria we used to evaluate this decision, which will hopefully help teams in a similar situation decide the best mobile development approach for them.
🌟 Background and motivation
About one year ago, the Wallet team started to get into quite a tricky spot.
Support tickets were piling up. Our backlog of bugs was growing faster than we could chew through it. With every new feature we built, new bugs emerged.
Each new feature shipped felt like we were moving one step forward and two steps back.
With each release, we were no longer delivering significant value, but found ourselves reacting to issues as they came in. We were fixing them as quickly as we could, but never quickly enough. We were running sprints, but it didn’t feel agile at all.
Saddled with the burden of maintaining two codebases and developing the same features twice, we constantly ran into platform-specific bugs that we had to resolve, and business logic inconsistencies that led to us laboriously specifying desired behavior up front.
If we continued working like this, I thought, we’d be running headlong into a wall of UX and technical debt.
This led me to ask:
How might we deliver more user value with less effort?
Specifically, we wanted to find out if we could:
- Write more cross-platform code, to avoid repeating our efforts twice in design, QA, and dev
- Write more modular code that’s easier to reuse, so that we can quickly experiment and iterate
- Write more predictable code that’s easier to test and debug, so that we can release faster
At the same time, we’ve already been reaping these benefits with React on the web, having built our internal tooling, website, and (now-deprecated) webapp with it.
That prompted us to seriously consider React Native as a solution for the problems we faced with mobile development as a small team.
We knew that React is a different beast on mobile than it is on the web, so this required a deeper look.
💼 Business Context
No one technology choice can change the way that you work, but the right technology, aligned with the size of your team, can enable you to work in a way that’s more suited to what you’re trying to build.
Before we go any further, it’s important to note that blog posts about technology choices are contextual. What works for one company will not work for the next. Case studies from Airbnb, Discord, and Pinterest show just how much your mileage will vary in adopting a new technology such as React Native.
At TenX, we have a small frontend engineering footprint (most of our business logic lives on the server side), and a product that’s in the early stages of its lifecycle.
We’re also a small team. There are five of us building and maintaining the TenX Wallet app. Our team consisted of a single software engineer, a product manager, a QA engineer, and two product designers working across iOS and Android.
That meant we had to bias our product development process towards cross-functional collaboration, speed, and flexibility. Specifically, we needed to adopt a “lean” approach to development, which let us:
- Treat code as the source of truth. Designers, product managers, and developers collaborate closely on building the product. Everyone can make pull requests against a single codebase to modify how it looks or how it works.
- Focus on learning, not pixel-pushing. Designers can spend time doing user research and prototyping to discover what our users want, rather than spending time pixel-pushing in Sketch.
- Test and release often. Write automated tests, so that you can continually release new features with the assurance that it won’t fail in production. Maintain a modular, reusable design system so that you can quickly mock up prototypes to test with real users.
Ultimately, when considering our particular business context, we realized that native mobile development is antithetical to agile software development, and did not work for a team our size:
- Code cannot be the source of truth, since native code is split across two codebases. Some specification is needed to align the two.
- Similarly, Designers and PMs spend time specifying designs and desired behavior, to ensure consistency between platforms. It’s also not realistic for Designers to contribute code, as styling/layout is significantly more challenging in Swift and Java than it is with the CSS Box Model.
- Tests have to be implemented twice: regression tests, acceptance tests, unit tests. While we’re able to leverage Gherkin and Cucumber to write our acceptance criteria once, this also falls into the nebulous territory of “over-specification”. Additionally, App Store releases require Apple approval, which limits how often we’re able to release.
With this knowledge, we decided to go ahead with the decision to sunset our native iOS and Android apps, and port them over to React Native.
This decision wasn’t made lightly, given that we’ve maintained separate native mobile apps for iOS and Android for over a year, a conscious decision at the time because we valued a well-designed user experience.
At the time, the kind of high-quality UIs that we desired was only possible with pure native apps. React Native wasn’t stable enough on Android for us to consider, and high quality libraries like Expo, React Navigation,
react-native-gesture-handler weren’t released yet.
🐣 Migration Strategy
Once we decided to make the switch, there was still the question of how best to adopt the technology.
Together with a senior frontend engineer, I looked at how other companies adopted React Native into their development workflow. Ultimately, we decided to rewrite our apps from scratch (a “greenfield” app), instead of taking the brownfield approach of bridging React views over to our existing native apps.
Our reasons for going with a “greenfield” approach were:
- The size of our team: We have a small team maintaining the mobile app.
- An app with a relatively small footprint: A large proportion of our business logic lives on the backend.
- Technical debt: Our existing iOS and Android apps were accreting design and technical debt that fixing them up to play nice with React Native would be much harder than starting from scratch.
We also considered the difficulty of integrating React Native into a brownfield app, as opposed to starting from scratch with a greenfield app.
Finally, the developer experience of maintaining a brownfield app is less well-supported, not to mention the current stability of the React Native JS bridge. Since these are unresolved issues with the underlying technology, we decided it was best to go with a greenfield approach.
💡 What we learned
One of the things we realized early on as a product team is how reality and expectations don’t often align when it comes to technology choices.
Most crucially, we learned that the “default” choice of how to do something — in our case, native iOS and Android development—might not be the best for our particular context.
Similarly, libraries, tools, and frameworks marketed as alternatives to development teams (such as React Native and Expo) can look better than they actually perform in reality.
Sometimes, the best decision is to do nothing and stick to the status quo. It only makes sense to consider alternatives when the “default choice” no longer works.
Chasing “trending” technologies can get you into trouble quickly, since trends change. In the time since we decided to rewrite our apps in React Native, Google‘s Flutter has emerged as an alternative.
If we had evaluated our decision by looking at the hottest framework, we would not have benefitted from the rewrite as much as we have.
The lesson is this: to decide on an alternative, the specific nuances of how your team works matters more than what’s trendy right now!
This post is Part 1 of a two-parter story, and continues here.
Part 2 of this post will dive into the nuances of how we evaluated React Native as a technology, by considering the various implications of changing your technology stack: how it impacts product design, product management, QA, and the larger engineering org.
Thanks for reading! If you liked it, clap for this article using the button below.