Senior craft

Team process, security and operations

Monorepo layout, release discipline, the security posture expected of someone who ships code into every app, and how support actually works.

This is the page that separates "I built a module once" from "I maintain the tooling twenty developers depend on". None of it is Nuxt-specific cleverness; all of it is the boring machinery that makes shared code safe to depend on.

Know — repository and release

  • Monorepo with pnpm workspaces is the usual shape: packages/layers/*, packages/modules/*, apps/*. Apps depend on workspace:* while packages are young, which gives hot reload and one lockfile; publishing to an internal registry comes when consumers live in other repositories.
  • Release discipline: conventional commits, a CHANGELOG per package generated by changelogen or changesets, migration notes for every breaking change, and a support window for the previous major that you state publicly.
  • Renovate or Dependabot with a CI matrix as the safety net, so upstream Nuxt releases arrive as pull requests that either pass or tell you exactly what broke.
  • Adoption metrics. A script that reads each app's lockfile in CI and reports which version of each shared package it is on. This is what turns deprecation from a debate into a decision.

Know — security

  • Nuxt shipped security releases in 2026. The 4.4.x line included security-focused releases, and 4.5.1 followed on the 4.5 line. Knowing that they exist, and that you track them, matters more in the room than reciting numbers.
  • Habits that actually reduce risk:
    • npx nuxt upgrade --dedupe on a cadence, plus subscribing to the GitHub security advisories for nuxt/nuxt.
    • Purge CDN and edge caches after upgrading if you use cache, swr or isr; stale HTML and payloads built against the old bundle are a real failure mode.
    • Validate all input with readValidatedBody(event, schema.parse) and getValidatedQuery; never trust a path segment.
    • Treat server-island props as untrusted, since they arrive from the client on the island request.
    • Secrets only in private runtime config, never in runtimeConfig.public, app.config or module options, all three of which reach the client or the repository.
    • Never cache personalised routes without varying on the identifying cookie or header (performance).
    • Consider nuxt-security for CSP and security headers, and keep a dependency review in the release checklist.
  • As a layer author you inherit every consumer's risk surface: a debug route, a permissive CORS rule or a vulnerable transitive dependency you ship is deployed everywhere at once.

Know — support

  • Reproduction-first intake. "It does not work" gets a fixture request; the reproduction becomes a test in your repository, so the bug cannot come back. Over a year this is what keeps the maintenance cost flat.
  • A known-pitfalls document consumers can self-serve from (layer pitfalls) removes most of the repeat questions.
  • Triage labels and a stated response time are what make a platform team's queue predictable instead of a stream of interruptions.

How it works

The workspace layout and the lockfile-based adoption report:

repo/
├─ pnpm-workspace.yaml
├─ packages/
│  ├─ layers/base/           @team/nuxt-layer-base
│  ├─ layers/auth/           @team/nuxt-layer-auth
│  └─ modules/toolkit/       @team/nuxt-toolkit
└─ apps/
   ├─ shop/                  extends: ['@team/nuxt-layer-base', '@team/nuxt-layer-auth']
   └─ admin/                 extends: ['@team/nuxt-layer-base']
# Which app is on which version of the base layer? The answer to "can we remove it yet?"
rg -n '"@team/nuxt-layer-base": "([^"]+)"' apps/*/package.json -or '$1' --with-filename

Input validation and safe caching in a route a layer ships to everyone:

layers/base/server/api/_team/search.get.ts
import { z } from 'zod'

const querySchema = z.object({
  q: z.string().min(1).max(100),
  page: z.coerce.number().int().min(1).max(50).default(1),
})

export default defineEventHandler(async (event) => {
  // Never interpolate raw query values; validate and bound them first.
  const { q, page } = await getValidatedQuery(event, querySchema.parse)
  return await search(q, page)
})
nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // Personalised: must never be shared between users.
    '/account/**': { cache: false },
    // Public and identical for everyone: safe to cache, and purge it on deploy.
    '/docs/**': { swr: 600 },
  },
})
Gotcha· Upgrading without purging the cache

After a deploy, cached HTML and payloads reference chunk names from the previous build. Users on the stale HTML hit missing chunks; Nuxt's emitRouteChunkError reload papers over it, but the correct fix is to version or purge cache keys as part of the deploy. This bites hardest on swr/isr routes behind a CDN.

Verify before the interview:

The specific advisories and the exact versions that fixed them. The guide attributes a server-side remote code execution fix, an unauthorised component-instantiation fix via server-island props, a route-rule authorisation bypass, a server-component denial of service, cross-user payload disclosure on cached pages and a dev-server path disclosure to the 4.4.7 and 4.5.1 releases. Confirm the mapping on the advisories page before quoting version numbers.

docs ↗

Exercise

Exercise
  • Write the release checklist for @team/nuxt-layer-base: tests, typecheck, budget, changelog, migration note, advisory review, adoption report. Keep it under ten lines.
  • Write the lockfile adoption script and run it against two fake apps.
  • Add getValidatedQuery with a zod schema to a route your layer ships, then send it a malformed query and read the error the consumer would see.

Be able to say

Be able to say· How do you keep shared Nuxt tooling secure and maintainable for twenty apps?

"Three habits. First, upgrade on a cadence and watch the advisories: Nuxt shipped security releases during 2026, and nuxt upgrade --dedupe plus a CI matrix means upstream breakage arrives as a pull request rather than an incident. Second, remember that everything I ship runs in every app: server routes get validated input and no debug output, island props are untrusted, secrets live only in private runtime config, and personalised routes are never cached without varying on identity. Third, make maintenance cheap: reproduction-first support where every bug becomes a fixture test, conventional commits with a changelog and migration notes, and an adoption report from the apps' lockfiles so deprecations are decided with data."