Streamlining React Native Build Sharing and Branching Strategy: Overcoming Common Challenges
Tags: reactnative, tutorial, productivity, devops
Published: 2022-01-23 11:58:59

Introduction:
Transitioning from a React web-based project to a React Native project brought forth new challenges for our team. In this blog, we will discuss three major obstacles we encountered regarding build sharing with QA engineers and implementing an efficient branching strategy. Fortunately, we found effective solutions using React Native build-sharing and branching strategies. Let's delve into these problems one by one and explore how we overcame them.
Problem 1: Build Sharing with QA Engineers
Sharing builds (APK or IPA) with QA engineers proved to be a significant challenge in mobile app development. Frequent build sharing for minor bug fixes or changes became suboptimal as the team grew. However, we discovered a solution: Over-The-Air (OTA) updates through CodePush. CodePush allowed us to provide updates without creating new builds.
To integrate CodePush in React Native, refer to GitHub Repository - React Native CodePush
To simplify the process of delivering CodePush updates, we set up an automated pipeline that triggered CodePush whenever we merged code into the master branch. This streamlined the workflow for everyone involved.
Advantages of this approach:
No need to download updates larger than 50 MB.
Reduced build time.

Problem 2: Feature Delivery and Reverting
As our team expanded and the demand for delivering new features increased, we faced challenges when it came to removing already merged features and reverting changes through source control (Git). This process often required additional regression testing, resulting in delays in app shipment.
Once again, CodePush came to the rescue. We established ten CodePush environments and created a manual pipeline to deploy branches to any of these environments. We modified our merging process to include testing by a dedicated test engineer before merging into the master branch. The test engineer would run the pipeline for feature branches in their designated environment, thoroughly test the feature, and provide sign-off. Only after sign-off was obtained, the feature would be merged into the master branch.
Implementation steps:
Create ten CodePush environments and obtain the corresponding CodePush keys.
Implement a menu option for test builds only.
Include an environment selection option.
Store the selected environment in async storage.
Restart the app and use the stored environment key to download CodePush updates.

Sample code for environment selector:
```
<FlatList
    data={[
        {
            value: '',
            label: 'Default',
        },
        ...environments,
    ]}
    renderItem={({ item }) => {
        const isChecked = environment === item.value;
        return (
            <TouchableOpacity
                onPress={async () => {
                    await AsyncStorage.setItem("SELECTED_ENV", environment);
                    CodePush.restartApp();
                }}
                style={{ padding: 10, flexDirection: 'row' }}>
                <Checkbox
                    variant={antDesign}
                    checked={isChecked}
                    label={item.label}
                />
            </TouchableOpacity>
        );
    }}
/>
```
By adopting this approach, we achieved the following benefits:
Test engineers gained full control over when to test a feature.
No need to revert buggy features.
Timely app releases.

Problem 3: Coordinating Multiple Testers
With the team growing larger, coordinating the testing of the same feature by multiple testers became a challenge. The previous approach of deploying a new branch to the same environment would override the previous update. To address this, we introduced squad-wise builds.
What is a squad-wise build?
A squad refers to a small group of individuals working on a specific mini-project within the app. Each squad was assigned dedicated environments, and we created separate branches for each squad along with an automated pipeline for deploying CodePush updates when merging changes to these squad branches. However, we didn't want to burden developers with the task of merging their code into the squad branch and creating pull requests against the master branch. To simplify this process, we developed a simple shell script and added it to our package.json.
What does the shell script do?
Pushes the feature branch to the remote repository.
Switches to the squad branch.
Merges the feature branch into the squad branch.
Pushes the squad branch to the remote repository.
Switches back to the feature branch.

Shell script:
```
#!/bin/sh
branch_name=$(git symbolic-ref -q HEAD)
branch_name=${branch_name##refs/heads/}
git branch --set-upstream-to origin/$branch_name
git push origin $branch_name
git checkout squad-branch
git fetch
git reset --hard origin/squad-branch
git merge $branch_name 
git branch --set-upstream-to origin/squad-branch
git push origin squad-branch
git checkout $branch_name
```

Developers need to use the npm push:squad command instead of the regular git push, and the script will handle the rest.
With this approach, we addressed problem 3 and enjoyed the following advantages:
Automated pipelines eliminated the need for test engineers to manually run the pipeline.
Multiple testers could independently test all features of their assigned squad using dedicated environments.
Testing could be conducted before merging code.
Developers were relieved of the burden of merging with two branches, thanks to the simplified push script.

Conclusion: By leveraging React Native build sharing and implementing an effective branching strategy, we overcame common challenges encountered during our transition from a React web-based project to a React Native project. We successfully addressed issues related to build sharing with QA engineers, feature delivery, reverting changes, and coordinating multiple testers. These solutions not only streamlined our development process but also improved collaboration and ensured timely app releases.