Mobile · Release engineering

Owning a React Native release end to end

Writing screens is the part of mobile development that gets taught. Everything that stands between a working build and an app users can install is the part that actually stalls projects.

What "owning the release" means

On a cross-platform app I authored the large majority of, ownership covered the whole path: version bumps, signing certificates and provisioning profiles, store metadata, submission, and the review back-and-forth on both platforms.

This is worth stating plainly because "React Native developer" describes two quite different jobs. One writes components against an existing pipeline. The other is responsible for the app existing in the store at all.

The native layer isn't optional

A cross-platform framework promises you rarely need native code. In practice you need it whenever the platform changes underneath you:

  • Camera and media capture, where the framework abstraction leaks and platform behaviour differs.
  • Permission models, which diverge sharply between platforms and shift with each OS release.
  • Push notification routing, where the payload shape and the tap-handling entry point are platform-specific.
  • Deep linking, which needs a signed association file on one platform and manifest configuration plus domain verification on the other.

Being able to read and write Kotlin and Swift isn't an advanced extra here. It's the difference between fixing a problem and filing an issue about it.

Migrations you don't get to opt out of

Three that landed in the same period:

  • A target-SDK bump with a hard store deadline, bringing new edge-to-edge layout behaviour that pushed interactive controls under system bars. Fixed with proper safe-area inset handling rather than magic padding.
  • A camera library replacement, because the incumbent had stopped keeping pace with platform changes.
  • The framework's new architecture, which surfaced component-level incompatibilities — pickers that wouldn't open, native views that rendered differently.

None of these were features. All of them were mandatory. Mobile has deadlines set by other people, which is the single biggest scheduling difference from backend work.

On the web you decide when to upgrade. On mobile, the platform decides, and the consequence of ignoring it is removal.

Making the pipeline do the boring work

Two changes that paid for themselves quickly:

Two changes that paid for themselves quickly. The first is a build per pull request, uploaded to a channel testers can install from, with the link posted back onto the PR:

build-android:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with: { node-version: 20, cache: npm }
    - run: npm ci
    - run: bundle exec fastlane android internal_share   # signed → upload
    - uses: actions/github-script@v7
      with:
        script: |
          github.rest.issues.createComment({
            ...context.repo, issue_number: context.issue.number,
            body: `Install this build: ${process.env.SHARE_URL}`,
          })

The point isn't automation for its own sake. It's that "does this actually feel right on a real device?" stops requiring a working local toolchain, which is what previously made design review on mobile so much worse than on web.

  • A build per pull request, with an install link posted back to the PR. Reviewers stopped needing a local toolchain to try a change. "Does this actually feel right?" became a tap instead of a half-hour setup.
  • Linting only changed files on each PR. Full-repo linting on a large codebase is slow enough that people learn to ignore it. Scoping it to the diff keeps it fast enough to stay enforced.

Stability work

The bugs that matter most in a mobile app are rarely in the feature code:

  • Non-deterministic crashes that only reproduce on specific devices or under memory pressure.
  • Cold-start crashes — the worst class, because the user cannot get past them to reach anything.
  • Notification taps landing on the wrong screen, or nothing at all.
  • Badge counts drifting out of sync with actual state.

None of these look impressive in a changelog. All of them are the difference between an app people keep and an app people delete.

← All engineering notes