CHAPTER 02 · START
Setup & Your First Project
The goal isn't "software installed". It's a reproducible environment: your laptop, a teammate's laptop, CI, and an agent's sandbox all produce the same result from the same project.
- Install and pin a Node version with a version manager, and manage dependencies with pnpm.
- Read the six most important options in a
tsconfig.json. - Explain why env vars prefixed
VITE_/NEXT_PUBLIC_must never hold secrets. - Create your first React + TS project and write an
AGENTS.mdfor it.
Opening: "works on my machine"
Same repo. pnpm dev works for your teammate; you get SyntaxError: Unexpected token; the agent's sandbox reports Cannot find module. The code is identical — why are the results different?
Where the answer lies: "the code" is only one input to the result. The other three are the Node version, the package manager and its version, and the exact installed dependency versions (decided by the lockfile). If any of them differs, the result may differ. A reproducible environment writes all three into the repo.
Step 1: install Node through a version manager Use
Don't download the installer from the website. Sooner or later you'll maintain projects that need different Node versions, and a version manager lets each project use its own automatically. We recommend mise (one tool for Node, pnpm, Python, Go and more) or fnm (Node only, minimal).
Node's versioning rule: even-numbered releases become long-term support (LTS); odd ones are short-lived "Current" releases. Use LTS in production. As of September 2026, Node 24 is LTS and Node 26 becomes LTS in October.
Step 2: install pnpm Use
Put "packageManager": "pnpm@10.x.x" (an exact version) in package.json; many tools — including CI and deployment platforms — use it to pick the same package manager version.
The most common commands, mapped to tools you know:
| Task | pnpm | Analogy |
|---|---|---|
| Install everything per the lockfile | pnpm install | go mod download |
| Strict install in CI | pnpm install --frozen-lockfile | npm ci |
| Add a runtime dependency | pnpm add zod | go get |
| Add a dev dependency | pnpm add -D vitest | Maven test scope |
| Run a script | pnpm dev / pnpm run build | make dev |
| Run a dependency's CLI | pnpm exec tsc (or pnpm tsc) | — |
| Download and run a tool once | pnpm dlx create-vite | npx |
| Why is this package installed? | pnpm why lodash | go mod why |
Step 3: the editor — your most important learning tool Use
VS Code, Cursor, Zed or JetBrains WebStorm all work. They embed the TypeScript language service: while you type, it type-checks continuously in the background.
Hover over any variable, function or component and the editor shows its full type. When reading agent-generated code, this is the fastest way to understand the shape of the data — much faster than reading docs. Cmd/Ctrl-click jumps to the definition, including library type declarations inside node_modules.
Suggested extensions: Biome (format + lint; or ESLint + Prettier), Tailwind CSS IntelliSense (class name hints), Error Lens (shows errors inline at the end of the line).
Step 4: install a coding agent
This guide isn't tied to any one tool. Popular terminal agents include Claude Code (Anthropic), Codex CLI (OpenAI) and pi (open source, minimal, extensible in TypeScript). They all work on the same principles (Chapter 12 takes them apart). Follow each tool's official install instructions; most support npm install -g.
Once installed, do one thing first: in an empty directory, ask it to "describe the environment you're running in" and check it can run node -v and pnpm -v correctly. That confirms the agent's execution environment matches your terminal.
Step 5: your first TypeScript program (no framework)
Leave React aside for now. Use the bare minimum to feel that "type checking" and "running" are two separate things:
Running these two commands yourself teaches more than reading the definition of "type erasure" ten times. You'll see: the runner is lenient, the type checker is strict — and what you want guarding the door is the latter.
Step 6: scaffold LinkNote's frontend
While learning, start with the plain Vite + React template: fewest concepts, one runtime (the browser). You'll use it for React in Chapter 06 and switch to a full-stack framework in Chapter 07.
Other common scaffolding commands (check each official site):
Reading the project structure
| Path | What it is | Commit it? |
|---|---|---|
src/ | Your source. main.tsx is the entry, App.tsx the root component | Yes |
public/ | Static files copied as-is into the output (icons, etc.) | Yes |
index.html | The first file the browser loads; it references src/main.tsx | Yes |
package.json / pnpm-lock.yaml | Declared deps / exact versions | Yes |
tsconfig*.json | TypeScript configuration | Yes |
vite.config.ts | Build tool configuration | Yes |
node_modules/ | Installed deps (rebuildable any time) | No |
dist/ | Build output | No |
.env / .env.local | Local env vars (may contain secrets) | No (commit .env.example) |
tsconfig.json: these options are all you need to read Use
strictis the most important. Turn it off and TS degrades into "JS with hints"; whole classes of bugs (like null access) go unreported. If an agent turns off strict or adds// @ts-ignoreto silence an error, that's a signal to stop it.noUncheckedIndexedAccessis off by default but strongly recommended:items[0]might not exist, and that bug is extremely common in production.- If you plan to run
.tsdirectly with Node, also considererasableSyntaxOnly: it forbids TS syntax that generates code (such asenum), guaranteeing types can simply be stripped.
Environment variables: which ones reach the browser Deep
This is one of the easiest mistakes to make and one of the most costly, which is why even a "setup" chapter goes down to the mechanism.
In backend work, env vars exist only inside the server process — naturally private. In a frontend build, however, the bundler replaces certain env var references with literal values at build time, writing them into the JS files sent to browsers. To prevent accidental leaks, frameworks only do this for variables with a specific prefix.
A variable with a public prefix such as VITE_ / NEXT_PUBLIC_ is effectively published. API keys and database connection strings must never use those prefixes. If the browser needs to call an LLM, the correct approach is for the browser to call your own server, which calls the LLM with the key (Chapter 11).
Step 7: a handbook for your agent
Nearly every coding agent reads a project instructions file from the repo root at startup: AGENTS.md (a cross-tool convention) or CLAUDE.md (Claude Code). Think of it as onboarding docs for a new colleague. Write it as soon as the project exists, and be specific:
Chapter 13 covers how to write this file well. For now, remember one principle: the second time you find yourself telling the agent the same thing, write it into AGENTS.md.
Common errors at a glance
| Error | Usual cause | Fix |
|---|---|---|
command not found: pnpm | Not installed, or the shell didn't load the version manager | Reopen the terminal; check mise/fnm shell integration |
Cannot find module 'x' | Deps not installed, or undeclared under pnpm's strict layout | pnpm install; or pnpm add x |
ERR_REQUIRE_ESM / Cannot use import statement outside a module | Mixing ES modules and CommonJS (Chapter 03) | Check "type": "module" and file extensions |
EADDRINUSE: address already in use :::5173 | Port taken (often a dev server still running) | Kill the old process or change the port |
Unsupported engine | Node version doesn't satisfy the project | Switch per the project's mise.toml / .nvmrc |
pnpm dev fails (full output below). Don't change any code yet: first tell me the Node version, the pnpm version, and whether the lockfile matches package.json; decide whether this is an environment or a code problem; then propose a fix."Diagnose first, change second — the most effective way to stop an agent from "editing things until the error goes away".
Self-check
1. You put VITE_OPENAI_KEY=sk-... in .env. It's only used on a "settings" page that requires login. Is that safe?
No. VITE_-prefixed variables are inlined into the JS at build time. JS files are static assets that can be downloaded without logging in (login protects page data, not the bundled scripts). Anyone can open the file and search for the key. Drop the prefix and use it only on the server.
2. Why must the lockfile be committed while node_modules must not?
The lockfile records the exact version and checksum of every dependency, including transitive ones — the key to reproducibility — and it's small text. node_modules can be rebuilt from the lockfile at any time, is huge, and may contain OS-specific binaries.
3. To fix a type error, an agent set strict to false in tsconfig and then reported "type check passes". What do you make of it?
It silenced the check instead of fixing the problem. Turning off strict stops reporting many real defects (like null access). Revert that change, require the agent to fix the specific error under strict mode, and add "never change tsconfig strictness options" to AGENTS.md.