Vite
Vite is the development server and build tool underneath the Vue projects here. Its defining decision is using different strategies for development and production, which sounds like a compromise and turns out to be the right call.
In development it serves native ES modules directly. The browser requests a module, Vite transforms that one file and returns it. Nothing is bundled, so startup time is roughly constant regardless of project size, and an edit invalidates one module rather than triggering a rebuild. This is why the dev server starts instantly on a project where a webpack setup took thirty seconds.
For production it bundles with Rollup, because unbundled ES modules mean hundreds of requests and that is genuinely slow over a real network. Code splitting, tree shaking, and minification all still matter once the code leaves your machine.
Why the asymmetry is defensible
The objection is obvious: development and production now run different code paths, so a bug can exist in one and not the other.
That happens, and it is rarer than expected because the transformation of individual modules is the same in both. What differs is how they are combined. In several years I have hit the asymmetry mainly around dependency pre-bundling — Vite converts CommonJS dependencies to ESM once and caches the result, and a dependency that changes without a version bump can leave a stale cache. Clearing node_modules/.vite fixes it, and knowing that exists saves an afternoon.
The trade is worth it. The development loop is the thing you interact with hundreds of times a day, and optimising it separately is the correct priority.
Practical notes
Configuration is small — usually a plugin list and a dev-server proxy pointing /api at the backend so the frontend and API appear same-origin and CORS never comes up in development.
Environment variables need the VITE_ prefix to reach client code. This is a safety feature rather than an annoyance: it means server secrets in the same .env cannot be accidentally bundled into JavaScript shipped to browsers.
The generated-client step from OpenAPI generation belongs in the build pipeline here, not as a manual command, since a stale client is worse than none.
See also: Vue 3, Node.js underneath, Nuxt 3 which builds on it, and a small project using it.