跪拜 Guibai
← All articles
Frontend · Architecture

The Real Gap at 3 Years of Frontend: Project Organization, Not Framework APIs

By Liora_Yvonne ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Frontend tooling churn creates a market for templates and starters, but templates can't supply engineering judgment. Developers who only know how to fill in existing project structures hit a hard ceiling when they need to design one—and that ceiling is what separates senior contributors from mid-level feature writers.

Summary

Many frontend developers hit year three able to write pages, connect APIs, and fix bugs, but freeze when asked to scaffold a long-term project from zero. The gap isn't a missing framework API—it's project organization: knowing which things will change, where to draw boundaries, and how to prevent a codebase from rotting under its own weight.

Five common failure patterns recur: directories organized by file type instead of responsibility, request layers that grow into omniscient god-objects, environment variables read silently everywhere with empty-string fallbacks, build-tool details leaking into business code, and team standards that live only in a README no one enforces. Each pattern traces back to the same root cause—boundaries that were never designed, only inherited.

LYStack, a newly open-sourced Vue3 Monorepo foundation, tackles these problems explicitly. It locks build tools behind adapter contracts so applications describe what to build without knowing how, uses dependency inversion so the service layer never touches localStorage or the router directly, and enforces explicit failure when configuration is missing instead of silent fallbacks. The project serves as a worked example of the principle that architecture isn't about picking a tech stack first—it's about deciding what should be isolated, what should be unified, and where change is allowed to happen.

Takeaways
Directories split only by file type (components, utils, api) become trash cans because they answer "what type" instead of "whose responsibility."
A request layer that knows about tokens, routing, UI messages, and business codes becomes an implicit god-object that no one dares to refactor.
Reading environment variables everywhere with `?? ''` silently swallows misconfiguration; missing values should throw immediately so errors surface before production.
Build-tool details (Vite, Rsbuild, Webpack) leaking into business code lock the project to a specific tool and turn migration into surgery.
Team standards that live only in a README decay because they rely on human memory; constraints must be embedded in toolchains, directory structures, and code.
Monorepo's real value isn't colocating projects—it's giving shared capabilities (build config, services, shared types) a dedicated place to live outside any single application.
Wrapping build tools behind an adapter contract lets applications declare what to build without knowing which tool does it, making future migration a config change, not a rewrite.
Dependency inversion in the service layer means the request layer exposes injection points for auth and error handling rather than importing localStorage or the router directly.
A single page-config manifest as the source of truth for multi-page entries prevents the drift that happens when entries are scattered across files or discovered by directory scanning.
Architecture isn't making everything complex upfront; it's identifying which things will change and ensuring those changes have a clear, bounded place to happen.
Conclusions

The article identifies a structural gap in how frontend careers develop: most jobs train you to operate inside an existing architecture, never to design one. This isn't a personal failing—it's a consequence of how work is assigned in teams where the scaffolding predates most members.

The five pitfalls all share one mechanism: a boundary that should exist but doesn't. When the request layer knows about routing, or business code reads build-tool-specific env vars, the problem isn't the code itself—it's that two concerns that should evolve independently have been fused.

LYStack's adapter pattern for build tools is a concrete implementation of the dependency inversion principle applied to tooling. It's notable because most projects accept tool lock-in as inevitable, when a thin abstraction layer can decouple the application's build declaration from the tool that executes it.

The explicit-failure philosophy—"configuration missing should blow up immediately"—runs counter to the defensive `?? ''` pattern that linters and tutorials often encourage. The argument is that silent fallbacks defer failure to the worst possible moment: production.

The framing of Monorepo as a "place for shared capabilities to precipitate" rather than "multiple projects in one repo" reframes the tool choice as an architectural decision about where common code lives, not a repository structure preference.

Concepts & terms
Dependency inversion
A design principle where high-level modules define abstractions, and low-level modules implement them. In LYStack, the services layer exposes injection points for auth and error handling rather than directly importing localStorage or the router, so the same service code can run in a browser, Electron, or SSO context.
Single source of truth
The practice of ensuring every piece of knowledge (version numbers, page entries, environment variables, shared types) has exactly one authoritative representation in a codebase. Duplicating these facts across files creates maintenance debt that compounds as the project grows.
Adapter pattern (applied to build tools)
Wrapping build-tool-specific configuration behind a tool-agnostic contract. Applications declare what they want to build (SPA, MPA, entry point, app name), and adapters translate that declaration into Vite or Rsbuild configuration. Swapping tools means writing a new adapter, not touching application code.
Monorepo
A repository structure where multiple applications and shared packages coexist in one version-controlled workspace. The architectural benefit is not colocation but giving shared capabilities (build config, services, UI primitives, shared types) a dedicated package namespace that applications can depend on without copying code.
Explicit failure
A design philosophy that prefers immediate, loud errors when configuration or preconditions are missing, rather than silent fallbacks like empty strings or default values. The goal is to surface misconfiguration at build time or startup, not in production where debugging is expensive.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗