Lightning Web Components arrived in 2019, and by 2026 every Salesforce front end of any real complexity is built with them. The framework itself is genuinely good. The code people write in it is uneven, and the unevenness is predictable. Having either built or rescued production LWC across twenty client engagements, we keep seeing the same handful of patterns prove themselves at the twenty-four month mark, and the same handful of clever ideas prove expensive. This is the field-notes version, with no boosterism.
01 / SHAPEThe pattern that works
Component trees should be three levels deep, not seven. A parent that knows about the world, a small number of stateful children that each own a piece of behaviour, and beneath them presentational components that take props and emit events. Anything deeper than three levels turns data flow into archaeology. Shared utility modules, formatters, field-metadata helpers, permission checks, an event bus where genuinely needed, live in their own folders and are imported by whoever needs them, but never import back into a component. And the wire framework does the reading: use the wire decorator for reads of Salesforce data and reserve imperative Apex for conditional execution or writes, because reimplementing cache invalidation and refresh by hand is a category mistake.
02 / FAILURESWhat keeps going wrong
Components that are too smart
A component that fetches its own data, manages its own loading state, owns its own error UI, and emits events upward will pass code review and then become impossible to test in isolation, because every fixture has to mock the world. Split intelligence from rendering. The stateful component owns data and behaviour and passes plain props to a presentational child that has no imports beyond Lightning standards. Both halves become easy to test.
Message Service as a default event bus
Lightning Message Service is right for cross-tab communication and for components genuinely in different DOM trees. It is wrong for siblings under the same parent, where it turns debugging into archaeology. Use parent-mediated events for siblings and reserve the message service for the cases where you have ruled out the alternatives.
Imperative Apex in loops
It appears in every codebase eventually: a list rendered with a loop, and inside the loop each row calling Apex to enrich itself. It ships fine with three items and becomes unresponsive at thirty. Batch the data with one Apex call at the parent and pass resolved data down as props. The win is usually a factor of ten or more.
The components that survive a year in production are the ones you would describe in two sentences. The ones that need a long explanation are the ones that get replaced.
03 / SPEEDPerformance habits that earn their keep
- Lazy load below the fold with the conditional render directive, because the initial render budget on a Lightning Experience page is tighter than people remember.
- Throttle wire refreshes. A component that listens for record changes should debounce the refresh, or it will reload on every keystroke during inline edit.
- Avoid expensive work in getters, which run on every render. Cache the result and recompute only when the inputs change.
- Be deliberate about the rendered callback, which fires after every render. Side effects there can produce render loops, so add guards.
04 / TESTSThe testing story
LWC ships with Jest support, and the useful split is snapshot tests for presentational components, behavioural tests for stateful ones, and integration tests for the parent. Aim for around seventy-five percent coverage; above ninety percent the tests start testing the framework rather than your logic. Mock wired data with the helpers in the Jest tooling, and never import real Apex into a test file, because the point is to verify your component's behaviour given inputs, not to verify Salesforce.
05 / ACCESSAccessibility is not optional
The standard Lightning components are accessible by default. Custom components inherit none of that. Every button needs a label, every form field an associated label element, every interactive element keyboard reachability. We run a Lighthouse audit on each component before production and treat a score below ninety on accessibility as a blocker, not a suggestion.
If a component takes more than two seconds to render on initial page load, it has a problem you can fix. Profile it on the Lightning performance tab before assuming the platform is at fault. It rarely is.
06 / AHEADWhere the framework is heading
The 2026 releases brought wire reactivity, signal-like primitives, and tighter App Builder integration. These are good changes, and they reward codebases that stayed close to the recommended patterns while penalising the ones that drifted. The investment in keeping components small and utilities cleanly separated pays back at every release, because each upgrade becomes a migration you can reason about rather than a surprise you have to survive.