So here's a problem I kept running into.
I'm building two features minimum at the same time. One needs testing on Device A, the other on Device B. But every time I switched branches, I had to kill Metro, rebuild, reinstall, wait — and by the time everything was back up, I'd already lost the context I was working in.
There had to be a better way.
Turns out, there is. It's called Git Worktrees — and it's probably the most underrated tool to boost productivity especially with AI.
What Even Is a Git Worktree?
Think of it like running two copies of your project — each on a different branch — at the same time. No switching. No stashing. No "wait let me rebuild."
Git Worktrees let you check out a branch into a completely separate folder. Each folder has its own Metro, its own build, its own life.
git worktree add ../app-feature-a feat/feature-a
git worktree add ../app-feature-b feat/feature-bThat's it. Two branches. Two folders. Two devices. Zero context-switching.
The Traps Nobody Warns You About
This sounds clean in theory. In practice, Android development throws three specific wrenches at you.
Trap 1: Don't nest worktrees inside your project.
If you create your worktree inside the project root — like project/.worktrees/feature-a — Metro's file watcher picks up both trees. It gets confused, causes cache collisions, sometimes serves the wrong bundle entirely.
Keep them as siblings:
~/Developer/
my-app/ ← main branch
my-app-feature-a/ ← worktree A
my-app-feature-b/ ← worktree BTrap 2: --deviceId doesn't actually isolate Gradle.
Running:
npx react-native run-android --deviceId DEVICE_A --port 8081looks like it targets one device. But Gradle's installDebug ignores --deviceId entirely — and happily installs the APK on every connected device. So Device B gets Feature A's build. Fun times.
The fix is ANDROID_SERIAL:
ANDROID_SERIAL=<DEVICE_A_ID> npx react-native run-android --deviceId <DEVICE_A_ID> --port 8081ANDROID_SERIAL restricts everything — including Gradle — to one device.
Trap 3: adb reverse stomps across devices.
When React Native runs, it executes adb reverse to forward Metro traffic from device → machine. Without specifying which device, it runs against all connected devices — overwriting the port rules you just set up for the other one.
Fix this before each build:
adb -s <DEVICE_A_ID> reverse --remove-all
adb -s <DEVICE_B_ID> reverse --remove-all
adb -s <DEVICE_A_ID> reverse tcp:8081 tcp:8081
adb -s <DEVICE_B_ID> reverse tcp:8082 tcp:8082And verify after every install run — because a rogue adb reverse can silently redirect a device to the wrong Metro.
The Full Setup That Actually Works
Here's the whole flow, no hidden steps:
git worktree add ../app-feature-a feat/feature-a
git worktree add ../app-feature-b feat/feature-b# .env files are gitignored — copy them manually (if using ai add them in rules)
cp .env ../app-feature-a/.env
cp .env ../app-feature-b/.env# Install deps in each
cd ../app-feature-a && npm install
cd ../app-feature-b && npm install# Terminal 1
cd ~/Developer/app-feature-a
npx react-native start --port 8081# Terminal 2
cd ~/Developer/app-feature-b
npx react-native start --port 8082adb -s <DEVICE_A_ID> reverse tcp:8081 tcp:8081
adb -s <DEVICE_B_ID> reverse tcp:8082 tcp:8082# Feature A → Device A
ANDROID_SERIAL=<DEVICE_A_ID> npx react-native run-android --deviceId <DEVICE_A_ID> --port 8081# Feature B → Device B
ANDROID_SERIAL=<DEVICE_B_ID> npx react-native run-android --deviceId <DEVICE_B_ID> --port 8082Why This Matters
Context-switching is silent time theft. Every branch switch, every Metro restart, every "wait for the build" — it adds up. And for Android especially, rebuilds aren't exactly instant.
Once this setup clicked, my parallel development flow actually felt parallel. Two devices live on my desk. Two Metro instances running. Two features being built and tested simultaneously.
It's one of those setups where once you have it, you can't go back.
