.agents/skills/frontend-large-feature-architecture/references/big-feature-rules.md
Use these rules when a feature keeps growing and the instinct is to add one more state variable, prop, callback, or effect to the same component.
Here, "controller" means a component or hook that owns feature logic: state, effects, data loading, subscriptions, actions, and derived data. Some controller code is necessary. The failure mode is one controller owning too many changing responsibilities and waking too much UI.
actions/*.ts or store actions.useState initializer, not
useMemo. The store is a per-mount instance, not a render-time derived
value.react-without-useeffect.md: gate render on
loaded data, seed state from initialValue props, and derive values in
render instead of syncing them with effects. When a change improves a
feature boundary, update the feature README or migration note with what
changed and the next slice.Most large frontend features are not yet in the ideal shape. Traces,
observations, experiments, prompts, evals, datasets, and session views all have
controller-heavy surfaces, and there are hundreds of cases still to fix —
including hundreds of useEffect calls that derive or sync state. Do not copy
an existing large component just because it works today; treat it as a
migration candidate. Do not scale ambition down either: broad, coherent
improvements are welcome as long as each changed boundary is deliberate.
The golden example — gate render on loaded data, seed state from props, keep
actions outside React — is in react-without-useeffect.md. For the
step-by-step controller path, read controller-migration.md.
Effects are for named external-system integrations: subscriptions, observers, browser event listeners, timers, and imperative third-party APIs, plus their cleanup. Keep them in containers or feature hooks, not view components. If an effect writes state repeatedly, make the store action idempotent.
Complex actions should be callable without rendering a component. Put workflows
in actions/*.ts or named store actions. Components wire hooks and pass
dependencies; actions own the workflow.
await applyBulkAction({
store,
queryClient,
projectId,
});
Actions must not call React hooks. Pass hook results, query helpers, the local store instance, or narrow callback dependencies into the action. If an action needs substantial data preparation, export a pure helper next to it so the transformation can be tested independently.
Do not pass twenty props through the tree so a button can do feature-level work, and do not leave complex workflows inline in a page controller because that controller happened to have all dependencies in scope.