Local Development Setup
Date: 2026-08-17
Getting a working environment on a new machine. The cost is paid by every new joiner, every machine change and every return to a dormant project — and it’s almost never measured, which is why it silently becomes a day.
Local development setup is everything needed between cloning a repository and having a running application you can change.
The measurement nobody takes
clone → running application
GOOD under 10 minutes,
two commands
TYPICAL half a day, several
people asked
BAD "ask Dave, he set it up"
Time this on the next new joiner. It’s the only way the cost becomes visible, and it’s a number that only ever goes up unless someone owns it.
The target
git clone …
cd project
make setup # or npm run setup
npm run devTwo commands. Everything else — dependency install, service startup, database creation, migration, seeding, environment file — happens inside them.
{
"scripts": {
"setup": "npm ci && docker compose up -d && npm run db:migrate && npm run db:seed && cp -n .env.example .env.local",
"dev": "docker compose up -d && vite"
}
}What has to be handled
RUNTIME VERSION .nvmrc, .tool-versions
→ and a check that
fails loudly on
mismatch
DEPENDENCIES npm ci from the
lockfile — Lockfiles
SERVICES docker compose
database, Redis,
search — Containers
DATABASE created, migrated,
SEEDED
— Test Data
ENVIRONMENT .env.example copied,
with working defaults
— Environment Configuration
CREDENTIALS which are needed, and
how to get them
See: Lockfiles · Containers · Test Data · Environment Configuration
Seeding is the step most often missing. An empty database means the application technically runs and nothing is usable — a new developer’s first hour goes on creating a product by hand.
A good seed script includes the edge cases: an order with 50 items, a customer with none, an out-of-stock product, a very long product name. It’s development data and test data at once.
Fail loudly on the wrong versions
{
"engines": { "node": ">=24 <25" },
"packageManager": "pnpm@9.0.0"
}A mismatched Node version produces bizarre errors that take an hour to trace back to the cause. A check that says “you’re on Node 18, this needs 20” saves that hour every time.
Documentation that stays true
README
├─ what this is 2 lines
├─ prerequisites versions
├─ setup the commands
├─ running the commands
├─ common problems 3–5 entries
└─ where to get credentials
Documentation drifts; scripts don’t. Anything that can be a script should be — the README should say npm run setup, not list twelve steps that were accurate last year.
The strongest test of a setup guide is a new person following it without asking anything, and updating it as they go. That’s the only reliable way it stays correct.
Where it degrades
- Undocumented manual steps. “You also need to create the bucket”
- Credentials with no route. A new joiner blocked on someone being online
- Platform assumptions. Instructions that work on macOS and not on Windows or Linux
- Version drift. A dependency bumping its Node requirement without the docs changing
- Nobody re-runs it. The setup path rots because everyone already has a working machine
Have CI run the setup path periodically, or on a schedule, from a clean state. It’s the only way the rot is detected before it costs somebody a day.
Devcontainers
The stronger option where the stack is genuinely complex:
// .devcontainer/devcontainer.json
{
"image": "mcr.microsoft.com/devcontainers/javascript-node:24",
"forwardPorts": [3000, 5432],
"postCreateCommand": "npm ci && npm run db:seed"
}Everything defined in the repository; the editor builds it. Genuinely good for onboarding and for a heterogeneous team.
The cost is the file-watching performance penalty on macOS and Windows, which is paid on every save — so it’s a better fit for backend work than for front-end development with hot reload — Containers.