Nubank has been a mobile-first Fintech from the beginning. We started our mobile development with native apps for our credit card, supporting both Android and iOS platforms back in 2013 and quickly adopted Kotlin and Swift after they were announced. For a while, we supported the Windows Phone platform.
As the company grew (we’re now the largest independent digital bank outside Asia), developing new products beyond the credit card became a priority, and new teams had to figure out how they would ship to our apps.
But how? Well, why not try React Native out?
Nubank’s third product was a digital savings account.
When the Nubank’s account team was staffed in 2016, we were facing a challenge: not enough native mobile specialists available. And it wasn’t easy to hire either, we saw (and still see) fierce competition for these professionals in the job market.
We wanted mobile developers dedicated to the Nubank’s account team because we already knew that specialized teams don’t scale. We believe in autonomous, agile, cross-functional teams, working together from Inception to Production, avoiding handoffs, and being responsible for the quality, operations, and evolution of their products. We believe single teams developing features end-to-end deliver more value, faster.
Writing the same feature twice, only in different languages and platforms, and having to learn all of them, seemed wasteful. Learning one hybrid platform to ship features would reduce the entry barrier for backend developers to contribute to the mobile frontend.
At the time, React Native was an established alternative, backed by some big players. On top of that, our engineering culture is all about continuous learning and improvement (we make it clear it’s everyone’s responsibility to learn and experiment on the job) — so it’s easy to understand why the Nubank’s account team decided to experiment with this cross-platform technology.
Nubank’s account has reached great success, with more than 13 million customers, who saved as much as US$ 305 million over the past five years by not paying a series of fees (as of September 2019). They have all used an app developed with React Native + GraphQL, a very different tech stack from the one used by either native platforms.
The story of React Native at Nubank is something we’re very proud of, and it deserves a separate post.
But today we want to talk about our next step. After all, no matter how successful a tool or platform is, our engineers continue to learn and experiment with new technologies:
“Hello, beautiful people from this channel. After our presentation (myself + @ring) about the Flutter spike during Nubank’s account Hackathon, there were many people interested in the technology and language (Dart). After talking to some of you, I suggested we have a Flutter Dojo for those wanting to be familiar with the language, syntax, patterns, and tests. If you don’t know what a Dojo is, there’s an explanation in the thread. The idea is to pick a simple problem (TodoMVC, maybe) and create an app from scratch using TDD, with everyone who is there. It’s important to highlight that this Dojo is 100% educational; that is, the code that we write will not be used later. The event will be Thursday, during lunchtime, with pizza! …”
A culture of experimenting and learning rapidly
At the beginning of 2019, new product teams, like Business Accounts and Lending, now had a choice to go native or to try React Native.
Around the same time, the industry had already shown significant advancements in mobile technologies (just a few announcements from 2019): Kotlin as the preferred language for Android, Swift 5 ABI Stability, Flutter 1.0, updates to React Native community governance).
So, we found ourselves discussing how to support our engineers’ productivity better when delivering features for our app. Here were some of the issues:
- For engineers who are interested in being more full-stack, the entry barrier was too high. To contribute to the credit card, one had to learn Kotlin for Android, Swift for iOS and, to help Nubank’s account, one also needed to learn React Native.
- Not to mention the fact that the architecture of each of these options was very different! Our hypothesis is that, by reducing the entry barrier for mobile development, Nubank will see more engineers contributing to the codebase.
- Another bottleneck we’ve found when relying on specialized native platform-developers for every new feature or product launch was the “staffing nightmare.” Despite our increased hiring efforts, there were never enough developers to fully staff our product teams.
We quickly realized that our teams were more important than a technology stack and that having all these choices was causing discomfort and confusion. It was time to seriously investigate which of the cross-platform technologies would be a better fit for Nubank’s needs.
So we set out to charter a task force with the mission of investigating and determining, with buy-in from the entire software engineering, which technology we should standardize on, considering Kotlin Native, React Native, and Flutter as alternatives.
The goal was to make such a choice that, regardless of the specialization of their members, teams would be autonomous and productive to develop the mobile application and deliver value on a single architecture, programming language, and set of conventions.
We assembled a small team with experienced mobile developers at Nubank. They determined 11 criteria to be evaluated in a research project. Here’s a short description of questions of the top 5 priorities:
1. Developer experience: What enables a developer to deliver value and to be productive? Examples: hot reload; component visibility; debugger tooling; IDE integration; and test tooling.
2. Long-term viability: Depicts the level of confidence in the future of the platform. Will the maintainer keep supporting it in the long-term (five years)? How likely is the community to support the project if the maintainer decides to drop it?
3. No platform specialization: An engineer should be able to write a mobile code for the product without differentiating between Android and iOS. Does the code look and behave the same on Android and iOS, with a low occurrence of OS-specific problems?
4. Incremental abstraction cost: The cost of extending the platform for each product task and the friction of centralizing the work on extensions, if required. How hard will it be to add a new component? Would we create a dependency on a horizontal platform team?
5. Non-linear abstraction risk: Risk of suddenly requiring extensive, disproportionate rewrites of our internal abstraction. Would we need to make non-trivial changes across the entire codebase to support a new NuDS (Nubank Design System) component?
We then set out to gather evidence and agree on a subjective score for each of them by using different techniques like:
- testing a Flutter version of one of our features in production
- analyzing communities, repositories, and resources available for each platform
- engaging in conversations with specialists, teams, and companies behind the development of the platforms
- implementing a clone of one of our features as a stand-alone app in the three different platforms
- conducting an internal usability test, were novice and senior engineers made changes to the feature in apps described above
- conducting presentations, debates and team visits to discuss our findings, hearing engineers and senior advisors’ opinions, incorporating their feedback and answering their questions
The results of the usability tests were the most interesting. People of all levels and backgrounds (including entry-level engineers with no previous experience developing on mobile) took a one-hour test. They received a working app, development environment, documentation of the hybrid platform and its components, and a few, increasingly more complex tasks to code. They were observed by our team while executing the tasks, and both answered a questionnaire at the end.
As an example, engineers had to add a feature so users could tap “shortcut” buttons with predetermined values to deposit money into their savings account:
The cloned app working through the “boleto” (payslip) bar code creation flow, starting with an input field for the value the client wants to deposit into their account.
The outcome of a programming challenge for developer usability tests:
Engineers had to add three buttons with fixed values to the account’s deposit flow.
We wrote a report of our research, gathering the findings and detailing how we evaluated each criterion. You’ll find a link to request the full report at the end of this article. It was tough to make a decision, even after gathering a great deal of information, we had to focus on the seven most important criteria to come up with the following results:
Drawing from our own experiences (80% of our Android codebase is Kotlin, Nubank’s account is developed in React Native) and evaluating our alternatives against Nubank priorities, we feel like Kotlin is a great language to work with. But Kotlin Native is the only platform that doesn’t provide a UI abstraction, making it dependent on native platform tooling for developing and testing. While it scored higher in our lowest priority criteria, not showing limitations of capabilities or risks for app store restrictions, we felt that, especially when it came to testing support for expert engineers, Kotlin Native is not ready for us.
We feared a bias towards React Native, so we consciously lowered the priority of another criterion: the cost of building the initial abstraction on the platform, where React Native was a clear winner.
When looking at more important criteria, React Native also wins in community support.
We felt no fear of the continuity and evolution of the project and were very happy with the amount of documentation and learning A radar chart, showing each criterion’s score from 0–5 for each of the platforms. available. When it came to breaking changes, however, we found that React Native has more dependencies than the other alternatives. Therefore, it is much more vulnerable to maintenance and upgrading pains.
Our engineering culture strongly encourages test automation, so Flutter shined with its excellent testing capabilities that fit nicely with our mindset (built-in testing infrastructure for Unit, Integration, and End-to-End tests without the need for rendering to the screen). In contrast, React Native requires third-party dependencies, which makes it more prone to breaking changes. We found the Flutter development experience to be superior, with better hot reload capabilities, robust official documentation, and a more stable API.
After a lot of discussion and contention up to the last minute, we decided to use Flutter as Nubank’s primary technology for mobile development. It means we will write new features in Flutter, and as the product evolves, we expect it to become a higher percentage of our codebase.
We are incredibly excited to share this study on the same day we announce a much-awaited feature launch: the Rewards program “transfer points” feature was built with Flutter.
Conclusion: what does using Flutter feel like?
So far, it’s been great to use Flutter, and we expect to have more features built or migrated to Flutter out to our users very soon.
Having to include Flutter in a running app with millions of clients comes with its own set of challenges that we’re gradually overcoming, the first of them being:
- changes in build pipelines,
- creating the main platform channels,
- integrating routing amongst React Native, Flutter, Kotlin, and Swift so we can maintain interoperability.
While Flutter is going to be our primary technology, we still need and value native developers, because each platform has its set of features that require native code (e.g., native plugins like GPS and camera, Apple Watch, Android minimized apps, etc.). Also, as the software engineering team at Nubank grows, individual specialization is welcome.
For anyone considering Flutter, we have made our full report with detailed data, pros and cons available for download. Be warned that what works for Nubank might not work for you.
We also recommend looking at other companies’ experiences. While there are companies using Flutter or React Native, Dropbox dropped its multi-platform technology (C++) because of “The (not so) hidden cost of sharing code between iOS and Android” and AirBnB decided on “Sunsetting React Native.”
Written by Alexandre Freire and Vinicius Andrade
Reviewed by André Moreira, Rafael Ferreira, Ana Paula Maia and Paula Rothman.