Dev.to · 7 min read

Claude Code on a React Native Codebase: Guardrails That Actually Change the Output

Claude Code on a React Native Codebase: Guardrails That Actually Change the Output

Every React Native developer has run the same experiment by now: point an AI agent at the codebase, ask for a feature, watch it produce something that compiles, and then quietly rewrite half of it before merging. The demo works. The diff does not. The gap is not model quality. It is that an agent with no constraints makes the statistically average choice at every fork, and the statistically average React Native choice is frequently wrong for your app: wrong navigation pattern, wrong state library, a fresh useState where you have a store, an inline style where you have a design system. Guardrails close that gap. Here are the ones that measurably changed what Claude Code hands me on RN projects. TL;DR A short CLAUDE.md beats a long one. Two paragraphs on stack and conventions do most of the work. Fence off ios/, android/, and native config with permission rules AND an instruction. Belt and suspenders. One screen or one hook per task. If you cannot describe the diff in a sentence, split it. Make the agent run typecheck, lint, and tests itself before showing you anything. Agents are great at gluing and modifying. For standard app skeletons, a template is still faster. The gap between "writes RN code" and "writes RN code you'd merge" Unguided agent output on an RN codebase fails in predictable ways: It invents a second way to do something you already do one way (a new fetch wrapper next to your API client). It reaches for dependencies you do not have, or worse, installs them. It edits generated or native files that should never be touched by hand. It styles components from scratch instead of using your primitives. None of these are intelligence failures. They are context failures, and context is exactly the thing you control. CLAUDE.md: the two paragraphs that change the defaults Claude Code reads a CLAUDE.md file from your project root at the start of every session and treats it as standing instructions. Most people either skip it or write a 300-line novel the model has to dig through. The high-leverage version is short and opinionated: # CLAUDE.md This is an Expo (managed workflow) app. TypeScript strict mode. Navigation: expo-router, file-based. State: zustand stores in src/stores/. Data fetching goes through src/api/client.ts only. Never call fetch directly. UI: use the components in src/components/ui/. Do not write inline styles; use the theme tokens in src/theme/. Never modify anything in ios/, android/, or app.json without asking first. Before finishing any task, run: npx tsc --noEmit && npm run lint && npm test. If any of those fail, fix them before presenting the diff. That is the whole file. Every line kills one category of "compiles but wrong" output. The first paragraph replaces the model's generic defaults with your actual stack. The second paragraph sets the boundaries and the exit criteria. The test for whether a line belongs in CLAUDE.md: have you corrected the agent for this twice? Then it goes in. Otherwise it is noise. Guardrail: keep the agent out of native land On RN specifically, the highest-damage failure mode is the agent "helpfully" editing native projects: a Podfile tweak here, a gradle change there, an Info.plist permission it decided you needed. These changes are invisible in a JS-focused review and surface as build failures days later. I fence this at two layers. Layer 1: permission rules. Claude Code supports allow/deny permission rules in .claude/settings.json at the project root, so the constraint ships with the repo: { "permissions": { "deny": [ "Edit(ios/**)", "Edit(android/**)", "Write(ios/**)", "Write(android/**)", "Bash(pod:*)", "Bash(npx expo prebuild:*)" ] } } Layer 2: the instruction. The "never modify ios/, android/, or app.json without asking" line in CLAUDE.md. This is not redundant. Permission rules are enforcement; the instruction changes planning. Without it, the agent plans a native change, hits the wall, and flails. With it, the agent routes around native work from the start or stops and asks, which is what you actually want. If you are on Expo managed workflow, you get this guardrail almost for free: keep the agent in JS, keep config in app.json under human control, and let EAS handle the native layer. Task shape: small, verifiable, one screen at a time The size of the task changes the quality of the output more than any prompt wording. My working rule: if you cannot describe the expected diff in one sentence, the task is too big. Bad: "Add a profile feature." Good, as a sequence: 1. Add a ProfileScreen at app/profile.tsx using the ui/ components. Static layout only, hardcoded data. No navigation changes yet. 2. Add a useProfile hook in src/hooks/ that loads the profile via src/api/client.ts. Include a loading and error state. 3. Wire useProfile into ProfileScreen. Handle loading/error with our existing and . 4. Add the profile tab to the router layout. Each step is one screen, one hook, or one wiring job. Each is verifiable on its own: you can open the diff, look at one file, and say yes or no in under a minute. The agent also self-corrects better at this size because failures are local. The compounding benefit: step 2's output is written against step 1's real code, not against an imagined version of it. The testing loop: what the agent runs before you see a diff An agent that hands you untested code has done half a task. The loop I make explicit (in CLAUDE.md and in the task prompt for anything nontrivial): npx tsc --noEmit # strict TS catches most RN agent mistakes npm run lint # convention drift, unused imports, hook rules npm test # whatever exists; even thin coverage catches regressions Strict TypeScript is doing the heaviest lifting here. A big share of agent errors in RN are shape errors: wrong prop types, wrong navigation params, a store selector returning the wrong slice. tsc --noEmit converts those from runtime surprises into a loop the agent resolves on its own before you ever look. What the agent cannot do is see the screen. So my manual review step for UI work is deliberately narrow: run it, look at the screen on one device size, tap through the flow once. Logic correctness has already been machine-checked; I am only reviewing what machines cannot check. When the agent is faster than a template, and when it isn't Honest accounting, because "AI writes the whole app" is the claim and it is only half true. The agent wins when the work is specific to your app: modifying existing screens, wiring a new flow into established patterns, refactors, integrating that one weird SDK, writing tests against your actual code. This is where templates cannot help you by definition. The agent loses on standard app infrastructure. Auth flows, onboarding, navigation shells, Supabase wiring, settings screens: an agent will generate these from scratch in an hour and you will spend another two reviewing boilerplate that thousands of apps share. That is what starter templates are for; a catalog like AppLighter exists precisely so the React Native + Expo + Supabase skeleton is a solved problem you download instead of a week of agent diffs you review. The pattern that actually compounds: template for the skeleton, agent for everything that makes the app yours. The template also quietly improves the agent, because now every task starts from consistent, conventional code, which is exactly the context agents extrapolate from best. The pattern that beats "just ship what it wrote" Putting it together, the loop that has consistently produced mergeable RN diffs for me: 1. CLAUDE.md: stack, conventions, boundaries, exit criteria (short) 2. Permission rules: native dirs and dangerous commands fenced 3. Tasks sized to one-sentence diffs, sequenced 4. Agent runs tsc + lint + tests before presenting 5. Human review narrowed to: screen looks right, flow feels right None of this is sophisticated. That is the point. The teams getting real throughput from agents on RN codebases are not writing clever prompts; they are removing the degrees of freedom where the agent's average choice diverges from their codebase, and letting it sprint inside the fence. What is in your CLAUDE.md? I am collecting RN-specific guardrails, so drop yours in the comments, especially the rule you added after an agent burned you.

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News