Maintaining two long-lived variants of React Native and Expo web project - ChatGPT
So, your plan is perfectly aligned with common practice:
-
Main version →
app-release.apk -
Guest version →
guest-release.apk(good choice) -
If you want to be extra clear →
lms-guest-v1-release.apk
You can have two versions of the same Expo project, each deployed to its own [EAS] channel:
Channels:
-
production→ Main app -
guest→ Guest variant app
With channels, you can simply do:
Deploy main app:
Deploy guest variant:
Even for web builds:
Build web:
Then:
-
Upload production build to production hosting
-
Upload guest build to guest hosting
You can host each variant at different URLs:
Or, if using Expo Hosting:
-
Production → Expo URL assigned to production channel
-
Guest → Expo URL assigned to guest channel
Here’s the reasoning in a clean and practical way:
✅ Use a separate branch (recommended)
Why?
-
Both variants share the same codebase
The guest version is not a full rewrite — it’s a configuration variant.
Branching keeps shared changes easy to merge. -
Forking creates unnecessary duplication
You’ll have two separate repos, two issues lists, two places to maintain. -
Branch workflow is simple
-
main→ your main app -
guest→ your guest app -
You can merge fixes from main → guest easily
-
-
You can use build-time environment variables to select behavior, so both variants can live in the same project.
Suggested branch layout:
If the guest variant is small, this is the simplest, cleanest setup.
❗ When forking is better (not your situation)
Forking makes sense if:
-
The guest app will evolve into a completely different product
-
The UI / features will diverge significantly
-
You plan to show them as two separate repos for clarity to others
-
You want different commit histories
Based on your current plan, none of this is true.
๐ Best Recommendation for You
Use a branch, not a fork.
...
Me: I don't need to have: const isGuest = process.env.APP_VARIANT === 'guest';
const isGuest = process.env.APP_VARIANT === 'guest';
because in your workflow, the selected .env file itself determines the variant. That makes your logic far simpler.
✅ Your approach works perfectly without variant flags
Since each build explicitly copies the desired .env.* file into .env, you already achieve:
-
Different API endpoints
-
Different server URLs
-
Different feature toggles
-
Different Clerk configs
-
Different app behavior
simply by having the correct .env file at build time.
So in your case, instead of:
if (isGuest) {
// guest logic
}
you can configure everything directly in the env files:
⭐ How to structure your env files
.env.main.web
MODE=main
ENABLE_GUEST_LOGIN=false
API_URL=https://main-api.example.com
.env.guest.web
MODE=guest
ENABLE_GUEST_LOGIN=true
API_URL=https://guest-api.example.com
Then in React:
const ENABLE_GUEST = process.env.ENABLE_GUEST_LOGIN === "true";
or
const MODE = process.env.MODE;
No need for APP_VARIANT.
๐ Your build scripts already enforce the variant
Examples:
Build main web version:
npm run export-web:main
Build guest web version:
npm run export-web:guest
After the copy, Expo sees the final .env and that’s all it needs.
⭐ Benefits of your approach
✔ No extra packages
✔ No extra global env variables
✔ No config logic needed inside app.config.js
✔ Completely deterministic builds
✔ Works the same for Android + Web
This is actually the cleanest setup for a 2-variant app like yours.
...
Me: For the APK variants and their local builds, I want to keep config changes to a minimum. So I think I will use same app.json. After the build is done, I will rename the app-release.APK file suitably.
CG: That is exactly the simplest and cleanest approach for your situation.
For local Android builds, keeping one app.json and then renaming the generated APK is completely fine — and many solo developers do exactly this.
Here’s a clear breakdown so you feel confident about this choice:
✅ Your plan is correct and practical
1. Keep a single app.json
This avoids:
-
changing icons
-
changing app names
-
maintaining separate configs
-
risk of mismatched bundle identifiers
The local build process remains stable and predictable.
✅ 2. Build once, then rename the APK manually
Expo local Android builds always output:
./android/app/build/outputs/apk/release/app-release.apk
You can simply rename it after the build:
Example:
For main app:
app-release.apk → lms-app-main-release.apk
For guest app:
app-release.apk → lms-app-guest-release.apk
This is perfectly valid because the APK filename has no effect on:
-
signing
-
package name
-
updates
-
Android installation behavior
Only the internal manifest matters, and you’re keeping that the same.
๐ Advantages of your approach
-
Zero extra configuration
-
Zero risk from modifying
app.json -
One codebase and one build process
-
Guaranteed consistency between variants
-
Easy to maintain in Git because the app.json never changes
And since these are local builds, you are in full control.
๐ง Optional — If ever needed later
If you want the guest version to:
-
have a different app name, or
-
appear separately in the app drawer,
then you’d need different android.package values or a dynamic app.config.js.
But for your current requirement (“keep config changes to a minimum”), this is not required.
⭐ Final confirmation
Your chosen workflow is good:
-
Same codebase
-
Same
app.json -
Same local build command
-
Rename the resulting APK file to reflect the variant
-
Use different
.envfiles to control behavior internally
This keeps the system simple, robust, and totally under your control.
...
But u said something about sharing future fixes/mods in main branch.
So I will be having two long-lived branches (it is not a temporary dev branch which is later merged into main and deleted).
How can I then share updates to main branch with guest-login branch.
CG: Great question — and yes, when you have two long-lived branches (a “multi-variant” repository), you absolutely can and should share fixes from main into guest-login.
This is standard practice in many projects.
Here is the cleanest and safest workflow.
✅ Recommended workflow: Keep guest-login rebased or merged onto main regularly
You have two stable branches:
main
guest-login
The idea is simple:
-
All core fixes, UI improvements, bug fixes, and shared code changes go into main.
-
The
guest-loginbranch periodically pulls in updates from main.
You have two equally valid ways to do this:
๐
ฐ️ Option 1 (recommended): Merge main → guest-login regularly
This is the safer and simpler method.
Steps:
-
Checkout guest-login
git checkout guest-login
-
Pull latest main
git pull origin main
-
Merge main into guest-login
git merge main
-
Resolve conflicts if any
-
Commit and push
git push origin guest-login
Benefits:
-
Very stable
-
Good for long-lived branches
-
Easy conflict resolution
-
History remains simple and chronological
Most teams with multi-variant apps use merges.
๐ ฑ️ Option 2 (more advanced): Rebase guest-login on main
This keeps the branch history clean but rewrites commits.
Steps:
git checkout guest-login
git fetch
git rebase origin/main
git push --force-with-lease
Use this only if you are fully comfortable with git and you are the only developer.
If you push rebase changes publicly, others get conflicts.
๐ฏ Which one should you use?
Given your workflow (solo developer, two variants, long-lived branches):
✔️ Use merge, not rebase
It's safer, easier, and perfect for long-lived variant branches.
๐ How frequently should you merge?
You can adopt any of these rhythms:
-
After every important fix in main
-
Every time before building a new guest APK
-
Every few days if you're actively working on both
There is no harm in merging often.
๐ Important practice
On the guest-login branch:
-
Only put guest-specific logic
-
Accept all incoming changes from
main -
Keep differences minimal (env files + small logic differences)
This keeps merges smooth.
๐งฑ Example workflow summary
main
↑
| (merge main → guest-login)
|
guest-login
You never merge guest-login back into main unless:
-
you added shared fixes that belong in main.
If that happens:
git checkout main
git merge guest-login --no-ff
(But ideally avoid putting shared fixes into the guest branch.)
⭐ Conclusion
Your two-branch structure works perfectly:
-
main→ canonical project -
guest-login→ variant, regularly updated from main -
Use
git merge mainwhile on guest-login to keep it updated -
Very clean, very stable for multi-variant apps
...
overwrite guest-login files by what main contains
CG: Short answer: No — git pull origin main will not overwrite your guest-login files.
Long answer with clarity below ๐
✅ Why your files will NOT get overwritten
Let’s say you are currently on:
guest-login
and you run:
git pull origin main
What Git actually does is:
-
Fetch the latest changes from the
mainbranch -
Merge those changes into your current branch (guest-login)
-
Git then shows a merge result:
-
automatic merge if no conflicts
-
conflict markers if both branches modified the same lines
-
Git will not blindly replace the files in your branch with the files from main.
๐ Important: Git merge is not "copy main over guest-login"
During a merge, Git tries to combine changes, not overwrite entire files.
Only lines that changed differently between main and guest-login will cause conflicts.
Example:
guest-login/file.ts
function login() { return 'guest login'; }
main/file.ts
function login() { return 'regular login'; }
After:
git checkout guest-login
git pull origin main
Git will show a conflict like:
<<<<<<< HEAD
guest login line
=======
regular login line
>>>>>>> main
It will not delete the guest logic; it asks you to pick one (or combine them).
๐งช Example scenario: files unchanged in guest branch
If the file wasn’t touched in guest-login, but changed in main:
-
Git will bring the updated main version into guest-login with no conflict.
-
This is safe and expected.
๐งฉ Example: guest-login changed lines AND main changed different lines
Git will merge everything cleanly.
Example:
In main you updated a component’s styling.
In guest-login you added a guest feature (ENABLE_GUEST_LOGIN flag).
Git will merge both because they are different areas of the file.
❗ When conflict happens
Only when both branches edited the same lines.
Git will show a conflict block.
You manually resolve it, save, and continue.
This is normal and expected for long-lived branches.
๐ฏ Bottom line
Merging main → guest-login does NOT overwrite your work.
Instead:
-
Git combines changes where possible
-
Git asks for your help only when both branches edited the same lines
This is exactly how multi-variant projects are maintained.
๐ Suggested workflow for you (safe)
git checkout guest-login
git fetch origin
git merge origin/main
This is the safest merge sequence.
...
Me: Fascinating! So my guest-login specific code in guest-login branch will not be automatically overwritten with git pull from main branch. If that guest-login specific code has a conflict with some lines in current main branch, I will be prompted for what should be picked. I can ensure that I don't pick anything that overwrites my key guest-login specific code. Did I get that right?
CG: Exactly right — you’ve understood the workflow perfectly.
...
Comments
Post a Comment