React Native Upgrades at Instawork: Challenges, Insights, and Teamwork

Deepty Patel
Instawork Engineering
8 min readJun 8, 2023

--

Embarking on a React Native upgrade can be a thrilling yet challenging adventure for any software development team. In this blog post, we peel back the curtain and share our experience at Instawork, navigating one of the most time-consuming and complex upgrades to version 0.67.4.

Please join us as we recount our battles against elusive bugs, explore the importance of perseverance and cross-team collaboration, and celebrate the triumphs of teamwork in delivering a seamless software experience to our users. So, buckle up and get ready for a deep dive into the world of React Native upgrades!

Background

​​At Instawork, staying up-to-date with React Native (RN) upgrades is a top priority. React Native is evolving quickly, each update brings improved performance and exciting features. Since our apps are built 100% on React Native, we want to take advantage of every improvement and maintain the efficiency of our applications.

By automating RN updates, we aim to reduce the time-consuming manual processes involved in updating both our Instawork Pro and Business apps simultaneously. This also allows us to manage shared dependencies across our three core projects — Instawork Pro, Business, and core-mobile, more effectively.

In addition, timely RN upgrades help minimize the risk of breaking functionality due to unnoticed manual changes in Xcode projects or Android Gradle files, which could lead to hard-to-debug issues.

By keeping our apps on the latest React Native technology, we ensure a seamless experience for our users and stay ahead in the competitive mobile app landscape. Thus, RN updates are a critical aspect of Instawork’s commitment to delivering high-quality software solutions to our clients.

Challenges Faced

During the most recent React Native upgrade to version 0.67.4 update process, our team faced numerous challenges that tested our technical prowess and adaptability.

It proved to be a particularly arduous process, as we confronted compatibility issues across platforms, API stability concerns amidst extensive changes, and difficulties handling third-party dependencies.

Moreover, we encountered elusive crashes that were challenging to reproduce and pinpoint, such as a persistent startup crash affecting certain Android devices and a silent background crash on specific Android versions.

These demanding obstacles required us to conduct aggressive targeted testing, fine-tune our strategies, and rely on seamless cross-team collaboration to find effective solutions. Each challenge presented itself as a gauntlet thrown down, daring us to adapt and evolve our testing strategies, coordinate more effectively, and dive deeper into the complexities of React Native upgrades. Navigating these challenges, our team emerged with valuable insights and a strengthened resolve to continue excelling in the React Native upgrade process.

Strategies and Solutions

We employed a comprehensive strategy to tackle the challenges faced during the React Native update process. With a focus on thorough testing and cross-team collaboration, we addressed the following issues and devised effective solutions:

  • Compatibility across platforms: To ensure that the new React Native 0.67.4 version worked correctly across Android, iOS, and web platforms, we conducted multiple rounds of sanity and regression tests, across different devices and OS platforms also making sure bugs were retested as they were reported and fixed.
  • API stability: React Native 0.67.4 introduced numerous API changes that could potentially cause breaking changes to our existing applications. To mitigate this risk, we ensured that API changes were backward-compatible and hence performed exhaustive testing on critical app features like push notifications, deep links, calendar rendering, and permissions (contacts, location, camera) for every new change and bug fix.
  • Handle third-party dependencies: React Native 0.67.4 brought changes to several third-party dependencies, such as React, React Native DOM, React Native Firebase, and React Native Web. Our team thoroughly tested and verified these changes to ensure they did not cause any unexpected issues across our mobile app ecosystem.
  • Address foreground and background crashes: Throughout the upgrade process, we encountered compatibility issues with third-party libraries that led to persistent startup crashes and elusive background crashes. The lack of compatibility of React native 0.67.4 with third-party libraries caused some libraries to break.

The app was persistently crashing on startup due to a bug in the JSC compiler. This issue was elusive and hard to pinpoint because it was only reported on Android devices running Pie (9.0) and some iOS devices running iOS12. The issue was so specific to the device that it was not caught while executing our automation tests or while performing manual testing on various devices. Our source of truth was the metrics from the Play Store which gave crash logs without much context to investigate. To prevent our users from installing and using a buggy version of our app, we halted the release and did a very aggressive targeted testing to pinpoint the issue. We tested the app on all the available Android 9 devices at our disposal only to find the issue reproducible on Galaxy S9 and S9+ devices. The app would crash immediately after launching.

A second crash was also reported in the Play Store with a stack trace:

Caused by android.system.ErrnoException: open failed: EACCES (Permission denied) at libcore.io.Linux.open at libcore.io.ForwardingOs.open (ForwardingOs.java:563) at libcore.io.BlockGuardOs.open (BlockGuardOs.java:274) at libcore.io.ForwardingOs.open (ForwardingOs.java:563) at android.app.ActivityThread$AndroidOs.open (ActivityThread.java:7758) at libcore.io.IoBridge.open (IoBridge.java:560)

This crash was reported for the Pixel 7 Pro initially but was also prevalent on other Android 12 and 13 devices. It was a silent crash that was backgrounded and we couldn’t reproduce the crash manually on any available Android devices or via executing our automation tests.

Our investigation revealed the need for the so_loader patch to resolve the incompatibility between the library and the new version of the React Native framework. The issue was caused by an incompatibility between the library and the new version of the React Native framework. The so_loader lib was loading debugging files and JSC files that are not required in release builds, so we added a patch to set android:allowBackup to prevent file storage permissions crashes and also added the Huawei HMS SDK. By implementing this patch we were able to prevent crashes, allowing the Android build to succeed.

Through these strategic approaches and tailored solutions, our team was able to surmount the challenges posed by the React Native 0.67.4 upgrade, ultimately ensuring a high-quality, seamless experience for our end users across platforms.

Learnings

Throughout this tumultuous React Native upgrade journey, we unearthed invaluable lessons that not only transformed our technical approach but also inspired personal growth.

Thorough Testing

We discovered the power of perseverance and attention to detail, realizing that overcoming obstacles requires a balance of determination and meticulousness. The challenges we faced served as catalysts for refining our organization and prioritization skills, empowering us to tackle future upgrades with newfound confidence and mastery.

Frequent Releases

We also learned that conducting React Native releases more frequently can be beneficial in spreading out the load and minimizing the complexity of each update. By implementing smaller, incremental upgrades, we can achieve:

  • Easier integration: Smaller updates generally involve fewer changes, making it easier to integrate and test new features and improvements with the existing codebase.
  • Faster bug resolution: With more frequent releases, we will be able to identify and address bugs and issues earlier in the development process, reducing the risk of accumulating numerous hard-to-fix problems.
  • Improved adaptability: Regular updates will enable our team to stay up-to-date with the latest React Native enhancements and best practices, fostering a more adaptable and agile development process.
  • Enhanced user experience: By continuously delivering improvements and new features to our users, we will be able to enhance the overall user experience and keep our applications feeling fresh and up-to-date.
  • Reduced technical debt: Incremental updates can help reduce technical debt by addressing issues as they arise, rather than allowing them to accumulate over time, leading to more significant challenges in the future.

However, it is crucial to strike a balance between the frequency of releases and the resources required to execute them. More frequent releases may demand more development and QA resources, so we will keep evaluating our team’s capacity and prioritize updates accordingly.

Minimize third-party dependencies

Minimizing the use of third-party dependencies can be beneficial in certain situations, as it reduces the risk of incompatibilities, potential security vulnerabilities, and maintenance challenges. However, third-party dependencies also offer valuable functionality and save development time. To have the best experience and reap the benefits, we should consider the following:

  • Evaluate the necessity and choose reputable libraries: Before adding a third-party dependency, we should assess whether the functionality it provides is essential and cannot be efficiently implemented in-house. We will try to opt for well-maintained, popular libraries with a strong community and a track record of regular updates and prompt bug fixes.
  • Monitor updates: Keep an eye on updates and change logs of our third-party dependencies, and ensure that our project stays up-to-date with the latest versions to minimize compatibility issues.
  • Use dependency management tools: Utilize tools like npm or yarn to manage dependencies, track versions, and handle updates efficiently.

Power of Automation

A lot of our manual effort can be minimized by investing in these crucial areas and strategies:

  • Test automation: We need to expand our test automation coverage to include more devices, platforms, and edge cases, thereby reducing manual testing efforts and catching issues early in the development process.
  • Continuous integration (CI): We have started implementing a CI system that automatically builds and tests our project whenever changes are committed, ensuring that issues are identified and resolved promptly.
  • Device testing: Our QA engineers test on a wider range of real devices and emulators to identify device-specific issues that may not be apparent during development or regression phases.
  • Code reviews: We conduct regular code reviews to identify potential issues or areas for improvement, ensuring that the codebase remains clean and maintainable.
  • Beta testing: We want to build and engage a group of beta testers to receive early feedback on new features and improvements, ensuring that our applications are stable and user-friendly before a full release.

By optimizing our use of third-party dependencies and implementing these testing strategies, we feel confident that we will be better prepared for all our future React Native upgrades and will be able to ensure a smooth, efficient release process.

Teamwork

Most importantly, we learned that teamwork is the secret ingredient to triumph, as cross-team collaboration and communication proved to be the cornerstone of our success. This experience has taught us that when teams unite towards a common goal, even the most daunting challenges can be conquered, paving the way for innovation and excellence.

Conclusion

As we reflect on our React Native upgrade saga, we recognize that the road to success is often paved with challenges, but it’s the lessons we learn and the bonds we forge that make the journey truly enriching.

Our experience at Instawork serves as a testament to the power of collaboration, determination, and continuous learning. For all the enthusiastic engineers and teams around the world using React Native to build mobile apps, let this story inspire you to embrace challenges as opportunities for growth and to celebrate the unity that enables you to overcome even the most daunting tasks. As we continue to explore the ever-evolving world of React Native upgrades, we are reminded that no challenge is insurmountable when we stand together, working towards a common goal.

So, let us forge ahead with unwavering determination, equipped with the knowledge that — Teamwork truly does make the dream work!

--

--