How would you roll the React Compiler out across a large existing React codebase, and what would you require before enabling it everywhere?
answer
- survey first, with real numbers
- the lint gate is the rollout
- pilot narrow, widen when boring
- measure before and after, or it did not happen
- opt-outs need owners and expiry
basics
~20 sAdopt in stages: run the compiler's health check to size the work, make its ESLint rule blocking and burn down the violations, compile a low-risk slice first via annotation mode, verify with tests and before-and-after profiling, then widen. Opt-outs get owners and expiry.
solid answer
~50 sI would treat it as an invariant change, not a config flip. First, survey: `npx react-compiler-healthcheck` reports how many components would compile and which rule violations and incompatible libraries stand in the way, which turns the rollout into a sized backlog. Second, make `eslint-plugin-react-compiler` blocking in CI and burn that backlog down — every violation fixed is a component that moves from skipped to compiled, so lint progress *is* optimisation progress. Third, pilot: run the plugin in `compilationMode: 'annotation'` and opt a low-traffic, well-tested area in with `"use memo"`, or scope the plugin to one directory. Verify with the test suite and a before/after profile of a real interaction, not a vibe. Then widen to inference mode. Throughout, expect latent bugs to surface where code relied on fresh identities each render, and require that any `"use no memo"` carries an owner and a removal ticket.
go deeper
Know that adoption is staged rather than a single switch: add the plugin, fix what its lint rule reports, and verify the app still behaves before expanding the scope.
Be able to name the concrete levers — the health check, the ESLint rule, annotation mode with "use memo", and scoping the plugin to a directory — and say what each one buys during a rollout.
Show the verification discipline: tests plus a before-and-after profile of a real interaction, and active watch for bugs that only appear once identities become stable. Be able to say when the gain is too small to claim.
Argue the invariant: the compiler is safe only while the codebase holds the Rules of React, so the blocking lint gate and the violation backlog are the actual commitment. Own the reporting standard — measured deltas, coverage numbers, and opt-outs with expiry.
## Frame it correctly Enabling the React Compiler is not adding an optimisation; it is adopting a *precondition*. The compiler is safe exactly to the degree that the codebase obeys the Rules of React, so the real question a principal is being asked is whether the organisation can hold purity as an enforced invariant. Answering with a rollout order that never mentions the lint gate misses the point of the question. ## Step 1 — survey before committing Run the health check the project ships: `npx react-compiler-healthcheck`. It reports roughly how many components in the repo would successfully compile, which files violate the rules the compiler depends on, and which libraries in use are known to be incompatible with the memoization it introduces. That converts "should we adopt this" into three numbers you can plan against: expected coverage, violation count, and dependency blockers. Also confirm the version baseline. On React 19 the plugin needs no extra runtime; supporting React 17 or 18 requires the plugin's `target` option together with the `react-compiler-runtime` package, which is an extra dependency decision to make deliberately. ## Step 2 — make the lint rule blocking `eslint-plugin-react-compiler` runs the compiler's own analysis and reports violations in the editor and in CI. Turning it on as a warning and letting the backlog sit is the failure mode: violations are the reason components get skipped, so an unenforced rule means adoption quietly stalls at partial coverage. The practical sequencing is to make the rule blocking for changed files immediately, so no new violations land, while the existing backlog is worked down as scheduled tickets. Every fix has a double payoff — a real latent bug removed, and a component promoted from skipped to compiled. ## Step 3 — pilot narrowly Two ways to narrow, and both are legitimate: - `compilationMode: 'annotation'`, where nothing compiles unless the function carries a `"use memo"` directive. Maximum control, good for a first exposure. - Scoping the Babel plugin to a directory in the build config, so an entire well-tested feature area compiles at once. Pick an area with real test coverage and low blast radius, not the checkout flow. Then widen to the default inference mode when the pilot has run in production long enough to be boring. ## Step 4 — verify with evidence Two distinct checks, and teams routinely skip the second. **Correctness:** the test suite, plus manual exercise of the pilot area. Watch specifically for the bug class that only appears once memoization exists — code that unknowingly depended on a fresh object or function identity every render, an effect whose dependency now stops changing, or an impure render whose mutation was previously masked by constant recomputation. **Benefit:** profile a real, representative interaction before and after. A well-composed app that already moved state down and passed subtrees as children may gain very little, and that is a legitimate finding — it means the adoption argument rests on removing memoization *maintenance*, not on speed. Reporting "we enabled the compiler" with no measured delta is not a result. ## Step 5 — policy for the escape hatch and the old memoization `"use no memo"` is a bisect tool. Left in place it marks a component that is both unoptimised and known to misbehave under caching — that is, one with a latent impurity. Require an owner and a removal ticket on every occurrence, and grep for them periodically. On existing `useMemo`/`useCallback`/`React.memo`: do not run a big-bang deletion. The compiler understands and preserves them, so they are harmless. Remove them opportunistically as files are touched, and review each one for whether it was ever about render cost — a few encode identity requirements that must survive. ## What separates a strong answer Sequencing (survey → enforce → pilot → measure → widen), naming the lint rule as a gate rather than advice, acknowledging that gains are proportional to how much waste existed, and putting an expiry on opt-outs. A weak answer is "add the Babel plugin and ship it"; the compiler makes that mostly survivable, which is exactly why the discipline has to be argued for rather than assumed.
- What kind of bug should the team expect to surface during the pilot?The ones masked by constant recomputation. Code that relied on a fresh object or function identity each render — a dependency array that used to change every time, a child that re-rendered for the wrong reason and got away with it — plus impure renders whose mutation of a prop, context value or module object was previously hidden. They are pre-existing defects that auto-memoization stops paying to conceal.
- If profiling shows almost no improvement, was the adoption a waste?Not necessarily, but you have to change the argument. If the app was already well composed, there was little wasted rendering to prune, and the remaining case for adopting is maintenance: no more hand-written dependency arrays to keep in sync and no more memoization judgment calls in review. Say that explicitly rather than claiming a speed win the numbers do not support.
- Would you delete existing useMemo and useCallback calls as part of the rollout?Not in one sweep. The compiler understands and preserves them, so they cost nothing but noise, and a mass deletion is a large untested diff during an already sensitive change. Remove them opportunistically when a file is touched, and check each one for whether it was encoding an identity requirement rather than an optimisation.
- How do you decide the rollout is complete?When the lint rule is blocking repo-wide with zero standing violations, the plugin runs in inference mode on the whole app, DevTools shows the expected share of components compiled, and no unowned opt-out directives remain. Compiled-component coverage from the health check is the number to track, since partial coverage is the quiet failure mode.
saying these in an interview costs you the question
- Adds the Babel plugin repo-wide on day one with no survey
- Leaves the compiler's lint rule as a non-blocking warning
- Claims a performance win without before-and-after profiling
- Ships "use no memo" opt-outs as the permanent answer to bugs
- Plans a mass deletion of existing memo hooks in the same change