{"slug": "claude-code-on-a-react-native-codebase-guardrails-that-actually-change-the", "title": "Claude Code on a React Native Codebase: Guardrails That Actually Change the Output", "summary": "A developer detailed how to improve Claude Code's output on React Native codebases by using guardrails, including a concise CLAUDE.md file and permission rules to prevent unwanted native project edits. The approach measurably reduces 'compiles but wrong' output by providing context and constraints.", "body_md": "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.\n\nThe 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.\n\nGuardrails close that gap. Here are the ones that measurably changed what Claude Code hands me on RN projects.\n\n`ios/`, `android/`, and native config with permission rules AND an instruction. Belt and suspenders.\nUnguided agent output on an RN codebase fails in predictable ways:\n\nNone of these are intelligence failures. They are context failures, and context is exactly the thing you control.\n\nClaude 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:\n\n```\n# CLAUDE.md\n\nThis is an Expo (managed workflow) app. TypeScript strict mode.\nNavigation: expo-router, file-based. State: zustand stores in src/stores/.\nData fetching goes through src/api/client.ts only. Never call fetch directly.\nUI: use the components in src/components/ui/. Do not write inline styles;\nuse the theme tokens in src/theme/.\n\nNever modify anything in ios/, android/, or app.json without asking first.\nBefore finishing any task, run: npx tsc --noEmit && npm run lint && npm test.\nIf any of those fail, fix them before presenting the diff.\n```\n\nThat 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.\n\nThe 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.\n\nOn 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.\n\nI fence this at two layers.\n\n**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:\n\n```\n{\n  \"permissions\": {\n    \"deny\": [\n      \"Edit(ios/**)\",\n      \"Edit(android/**)\",\n      \"Write(ios/**)\",\n      \"Write(android/**)\",\n      \"Bash(pod:*)\",\n      \"Bash(npx expo prebuild:*)\"\n    ]\n  }\n}\n```\n\n**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.\n\nIf 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.\n\nThe 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.**\n\nBad: \"Add a profile feature.\"\n\nGood, as a sequence:\n\n```\n1. Add a ProfileScreen at app/profile.tsx using the ui/ components.\n   Static layout only, hardcoded data. No navigation changes yet.\n2. Add a useProfile hook in src/hooks/ that loads the profile\n   via src/api/client.ts. Include a loading and error state.\n3. Wire useProfile into ProfileScreen. Handle loading/error with\n   our existing <Spinner /> and <ErrorView />.\n4. Add the profile tab to the router layout.\n```\n\nEach 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.\n\nThe compounding benefit: step 2's output is written against step 1's real code, not against an imagined version of it.\n\nAn 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):\n\n```\nnpx tsc --noEmit   # strict TS catches most RN agent mistakes\nnpm run lint       # convention drift, unused imports, hook rules\nnpm test           # whatever exists; even thin coverage catches regressions\n```\n\nStrict 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.\n\nWhat 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.\n\nHonest accounting, because \"AI writes the whole app\" is the claim and it is only half true.\n\nThe 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.\n\nThe 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](https://www.applighter.com/?utm_source=devto&utm_medium=blog&utm_campaign=claude-code-react-native-guardrails) exists precisely so the React Native + Expo + Supabase skeleton is a solved problem you download instead of a week of agent diffs you review.\n\nThe 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.\n\nPutting it together, the loop that has consistently produced mergeable RN diffs for me:\n\n```\n1. CLAUDE.md: stack, conventions, boundaries, exit criteria (short)\n2. Permission rules: native dirs and dangerous commands fenced\n3. Tasks sized to one-sentence diffs, sequenced\n4. Agent runs tsc + lint + tests before presenting\n5. Human review narrowed to: screen looks right, flow feels right\n```\n\nNone 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.\n\nWhat 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.", "url": "https://wpnews.pro/news/claude-code-on-a-react-native-codebase-guardrails-that-actually-change-the", "canonical_source": "https://dev.to/sophie_fa_6ed935b0601d76/claude-code-on-a-react-native-codebase-guardrails-that-actually-change-the-output-3pd2", "published_at": "2026-09-09 13:19:12+00:00", "updated_at": "2026-09-09 13:41:56.671345+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["Claude Code", "React Native", "Expo"], "alternates": {"html": "https://wpnews.pro/news/claude-code-on-a-react-native-codebase-guardrails-that-actually-change-the", "markdown": "https://wpnews.pro/news/claude-code-on-a-react-native-codebase-guardrails-that-actually-change-the.md", "text": "https://wpnews.pro/news/claude-code-on-a-react-native-codebase-guardrails-that-actually-change-the.txt", "jsonld": "https://wpnews.pro/news/claude-code-on-a-react-native-codebase-guardrails-that-actually-change-the.jsonld"}}