Workspace and package design
Packages should expose intentional contracts, declare exact dependencies, and follow a directory structure that communicates product and platform ownership.
Make applications and reusable packages obvious
A common layout separates deployable applications from reusable packages. Names should communicate purpose rather than implementation: checkout-domain is clearer than utils-two.
Keep application-specific code close to the application until reuse is real. Premature shared packages become dependency magnets with unclear owners.
apps/
web/
admin/
docs/
services/
api/
packages/
ui/
domain/
observability/
typescript-config/Treat every internal package like a public API
Export through a deliberate package entry point. Do not let consumers import arbitrary source files, because those deep imports turn folder structure into an accidental contract.
Use package export maps and boundary lint rules. A consumer should know what a package promises without knowing how its internals are arranged.
Declare dependencies where they are used
Each workspace package should declare its runtime, peer, and development dependencies. Relying on a dependency merely because it is hoisted at the repository root creates installations that work accidentally.
Use the workspace protocol when the package manager supports it. Lockfile changes should be reviewed like source because they change the resolved software supply chain.
Enforce dependency direction
- Applications may depend on feature and platform packages
- Feature packages may depend on domain and platform contracts
- Domain packages should avoid application and UI dependencies
- Low-level utilities must not import product features
- Circular package dependencies are architectural errors, not bundler puzzles
Share configuration without hiding decisions
Central ESLint and TypeScript presets reduce drift. Allow applications to extend them where their runtime differs. A browser app, Node service, and library should not be forced into one inaccurate compiler configuration.
Pin tool versions centrally and automate updates, but document why rules exist. A shared configuration package should be understandable without tribal knowledge.
Can you explain “Workspace and package design” without reaching for the API reference?
Describe the main trade-off, one failure mode, and the decision you would make in a real project.