HardArchitecture
Third parties need to extend your app's UI with their own panels. Design the extension point
Define the contract narrowly and version it: named extension slots the host renders into, a declared manifest of what each plugin contributes, and a host-provided API object for everything a plugin may do. Run untrusted plugin code in an iframe or a worker rather than in your page — a plugin sharing your origin can read your tokens, your DOM and your storage, and no code review scales to prevent that. Communicate over `postMessage` with an explicit message schema and strict origin checks. Isolate failures: a plugin that throws, hangs or renders nothing must degrade to an empty slot, never take down the host.
The central decision is trust, and it determines everything else. Trusted first-party plugins can be modules in your bundle with a simple registry; untrusted third-party code cannot, whatever the review process claims. The iframe boundary is what makes the security story real, and it is also what forces the API to be explicit — anything a plugin needs has to be a message, which is inconvenient and is exactly why the contract stays small enough to version. The version is the other half: once a third party ships against your API you cannot change it, so it is worth deciding on day one which parts are stable and which are explicitly experimental.
MediumBest practice
How do you recognise that an abstraction is leaking, and what do you do about it?
The signs are concrete: callers pass options that exist only to reach the underlying library, they import the underlying type to satisfy your signature, they work around the wrapper for one case, or every new requirement adds another flag. At that point either widen the abstraction to genuinely own the concept, or drop it and let callers use the library directly — the worst outcome is a wrapper that adds indirection without adding independence.
The useful question is what the abstraction is actually for. If it exists to allow a future swap, a leaking one has already failed at that and is pure cost. If it exists to enforce a house policy — always attach auth, always report errors this way — then a leak is a hole in the policy and worth closing. Naming the purpose first is what turns the decision from taste into something you can argue about with evidence.
HardWrite code
A third-party SDK is used in forty components. Design the boundary so it can be replaced
The common failure is a pass-through wrapper: it looks like an adapter while leaking every vendor concept, so a swap still touches everything.
MediumCode review
Review this provider nesting
<ThemeProvider>
<AuthProvider>
<QueryProvider>
<FlagProvider>
<ToastProvider>
<App />
</ToastProvider>
</FlagProvider>
</QueryProvider>
</AuthProvider>
</ThemeProvider>
The nesting itself is not a performance problem — providers do not re-render children merely by existing — so the honest review is about maintainability rather than speed. The real risks are ordering dependencies that are implicit (Auth probably needs Query, Flags probably need Auth) and invisible until someone reorders them, and the rightward drift making the file hard to edit. A `compose`-style helper that takes the providers in order flattens it and makes the ordering a single declared list you can comment. The thing genuinely worth checking is whether any of these providers pass an inline object as `value`, which WOULD cause app-wide re-renders — that is the defect to look for, not the indentation.
The valuable part is declining to call this a performance bug: a reviewer who flags nesting depth as a re-render problem has the wrong model.
EasyConcept
When is duplicating code the better choice than extracting a shared abstraction?
When the two pieces only LOOK alike. DRY is about a single source of truth for a piece of knowledge, not about eliminating similar-looking characters — two functions that happen to have the same shape today but belong to different features will diverge, and a shared abstraction turns that divergence into parameters and flags until nobody can safely change it. The practical guidance is to wait for the third occurrence before abstracting, so you can see which parts genuinely vary, and to prefer duplication when the two callers are owned by different teams or change for different reasons. Un-abstracting later is much harder than abstracting later, which makes waiting the cheaper mistake.
The distinction is between a single source of truth for a piece of knowledge and mere textual similarity. Two functions with the same shape but different owners will diverge, and the shared abstraction absorbs that divergence as flags and parameters until changing it safely becomes impossible. Waiting for the third occurrence works because by then you can see which parts genuinely vary.
HardMultiple choice
What is the practical risk of a module-level singleton?
A module instantiated once persists for the process lifetime. In tests that means state carried between cases; in SSR it can mean one user's data visible to another. Injecting the dependency, or creating it per request, avoids both.
MediumScenario
Three teams have each written their own date formatting helper, all subtly different. How do you converge them?
Find out how they actually differ first — timezone handling, locale, relative vs absolute — because one of them is probably correct and the others are bugs wearing a different shape. Write the shared implementation to cover the union of real requirements, adopt it in one team's code first to prove it fits, then migrate the others incrementally with the old helper delegating to the new one so nothing has to move at once. Delete the shims when the call sites are gone.
The delegation shim is what makes this tractable: it lets the migration be a series of small, independently revertible changes rather than one commit touching three teams' code. The diagnostic step matters as much — converging on a shared helper before understanding the differences usually means picking one team's bugs and shipping them everywhere, and then the other two teams re-fork within a quarter.
EasyConcept
What does dependency injection buy you in frontend code, where there is no DI container?
It means a module receives what it depends on instead of reaching out and importing it — an API client passed as a prop or through context, rather than a hard-coded import of a singleton. The payoff is substitution: tests pass a fake without module mocking, a second instance can be configured differently, and the dependency is visible in the signature rather than hidden in the import list.
Frontend code does this constantly without naming it — a provider supplying a client, a component taking an `onSave` callback, a function taking `fetch` as an argument. Recognising it as injection is useful because it tells you when reaching for `vi.mock` is a symptom: if the only way to substitute a dependency is to intercept the module system, the dependency was never really a parameter.
The remaining 7 Design Patterns questions — plus mock interviews, spaced revision and progress tracking — are in the free interview prep workspace.