I onboarded two junior devs onto a React Native app last week. Same Expo starter template, similar backgrounds: both had shipped web React apps, neither had touched mobile.
I kept a diary of every friction point. This is it.
The pattern held the way it always does. The first week was dominated by problems the template could have prevented, not by the problem the app actually solves.
- Environment setup: estimated 2 hours, took about 5 per dev
- Nobody knew the difference between Expo Go, dev builds, and EAS builds
- Auth was implemented correctly in three places, so both devs picked differently
- One deep link cost 6 hours for one dev and 8 for the other
- Total avoidable loss: roughly 15 hours each, all of it fixable with docs and one shell script
Day 1: the environment install cliff
Estimated 2 hours. Took about 5 per dev.
-
Wrong Node version. One had 20, one had 16. The README said "Node 18+" while
.nvmrcpinned 18.17. Nothing surfaced the mismatch; things just failed oddly downstream. -
Missing Xcode command line tools. Silent until
expo run:ioserrored with a message mentioningxcrunand not what to install. -
CocoaPods version mismatch. Silent until
pod installfailed four layers deep in a trace. -
Wrong Android SDK path. Both devs. Symptom was
expo run:androidunable to findadb. Fix was settingANDROID_HOMEproperly in the shell profile.
None of these are template bugs. They're template UX failures. A setup script that checks Node version, xcrun availability, CocoaPods version, and ANDROID_HOME up front, then fails loudly with an actionable message, would have saved three hours each.
The pattern in all four: the failure surfaced far from its cause. That's the property that turns a five-minute fix into a two-hour one.
Day 2: the build system nobody explained
Task: add a screen, preview it on a phone.
Both devs added the screen without trouble. Both got stuck getting a build onto their device.
- Neither knew the difference between Expo Go, a development build, and an EAS build. These are three different things with three different use cases and the README described only
expo start. - One got a build that installed and crashed on launch, because the required environment variables weren't set in EAS secrets.
The fix isn't code. It's a page explaining how the build system works: what each build type is for, when to use which, and where environment variables live. Five minutes to write. Three hours to skip.
Day 3: auth that was correct in three places
Task: add a protected route.
Both implemented it. Both did it slightly wrong, in different ways, and both were defensible readings of the codebase.
- The auth context was wired through a provider, but route protection existed in the root layout, in individual screens, and in a custom hook. Three valid patterns, no indication which was canonical.
- Token refresh happened invisibly through middleware. Nothing documented that it existed. One dev spent two hours building a refresh mechanism that was already running.
- The RLS policies governing the protected data lived in a separate repo. Neither dev knew, and both were confused when a correctly authenticated request returned an empty array.
That last one is worth sitting with. Nothing errored. Auth worked, the request succeeded, the array came back empty, and the actual cause was in a repository they hadn't been told about.
Fix: one auth architecture page. A diagram of token flow, one worked example of protecting a route, and a pointer to where RLS policies live. Fifteen minutes to write once.
Day 5: the deep link that ate a day
Task: magic-link email flow. User taps a link, app opens, lands on a specific screen.
One dev took about 6 hours. The other about 8. It should have been 90 minutes.
- The template used imperative routing, so deep linking required a manual linking config that wasn't documented anywhere.
- Dev builds and production builds had different bundle identifiers, so a link opened one and not the other. Neither dev knew this was normal, and both assumed they'd broken something.
- Testing required
xcrun simctl openurlon iOS andadb shell am starton Android. Neither had used either command.
Had the template shipped with file-based routing, deep linking would have worked automatically for any route file. This was the most expensive lesson of the week and the one most attributable to an architectural choice rather than a documentation gap.
What the template should have shipped
In priority order:
- A doctor script. Checks every dependency, fails loudly, tells you what to install.
- A build system page. Expo Go vs dev build vs EAS build, 200 words, with a decision tree.
- An auth architecture page. Where tokens live, how refresh works, where RLS policies are, one worked example.
- File-based routing. Every concern you can defer to convention is a concern you don't have to teach.
- A testing on device page. Simulator, emulator, physical device via EAS, physical device via QR.
Four of those five are documentation. None is a code change. Combined they'd have cut roughly 15 hours from each dev's first week.
This is the part I keep relearning: the expensive gaps in a template are rarely architectural. They're the things everyone on the team already knows and nobody wrote down.
The onboarding checklist we ship now
Every project gets a docs/ONBOARDING.md:
- Run the doctor script. Fix every red item before continuing.
- Copy
.env.exampleto.env. Ask your lead for values. - Run in Expo Go. Scan the QR. Confirm you see the home screen.
- Read
docs/architecture/auth.md. Five minutes. - Read
docs/architecture/builds.md. Five minutes. - First PR: add your name to
docs/team.md. Get it reviewed. Merge. - Second PR: add a new route. Get it reviewed. Merge.
- Now start real work.
Eight steps, roughly four hours if the tooling cooperates. It replaces about a week of ad-hoc onboarding, and steps 6 and 7 exist purely so someone's first merge is trivial rather than load-bearing.
Templates that ship this kind of thing by default, like the Expo starters at Applighter, remove most of week one before it starts. But you can write the four pages above for your own codebase this afternoon, and you should.
The closing note
A good template isn't only about code architecture. It's about the first week of somebody's life on your project.
Every minute of documentation skipped in the template costs hours of confusion downstream, and it costs them from the person least equipped to absorb it: the one who just joined and doesn't yet know what's normal.
What ate your first week on a new codebase? Mine was an undocumented env var that made the app silently render an empty state.
Top comments (0)