Field Notes

Stack Decisions

Record technology choices and their rationale before writing code.

Every project makes technology choices. Most teams make them implicitly — someone picks a tool, everyone else follows, and six months later nobody remembers why. Stack decisions makes these choices explicit and reversible by documenting them upfront.

Why This Matters

Implicit choices create three problems:

  1. New team members can't understand the reasoning. They see the tool but not the trade-off that led to it.
  2. Revisiting decisions is impossible. Without recorded rationale, every discussion starts from scratch.
  3. Inconsistency creeps in. Different developers make different assumptions, leading to multiple tools solving the same problem.

Recording stack decisions takes minutes. Debugging the consequences of unrecorded decisions takes days.

Decision Categories

Package Manager

OptionStrengthsBest For
pnpmFast installs, disk-efficient, strict dependency resolution, native workspace supportMonorepos, teams that want strict correctness
npmZero setup (ships with Node.js), largest ecosystem familiaritySingle-repo projects, teams new to Node.js
yarnPlug'n'Play for zero-install, good workspace supportProjects that need offline installs

Build System

OptionStrengthsBest For
TurborepoSimple configuration, fast caching, understands dependency graphpnpm monorepos with multiple apps
NxRich plugin ecosystem, computation caching, affected-based testingLarge teams, enterprise monorepos
NoneNo overheadSingle-app, single-package projects

Framework

OptionStrengthsBest For
Next.jsServer-side rendering, API routes, image optimization, mature ecosystemFull-stack web applications
Vite + ReactFast dev server, minimal config, lean outputPrototypes, SPAs, admin tools
AstroContent-first, minimal JS by default, island architectureMarketing sites, documentation

Language Configuration

OptionStrengthsBest For
TypeScript (strict)Maximum type safety, catches bugs at compile timeAny project that will be maintained
TypeScript (relaxed)Faster onboarding, fewer type gymnasticsQuick prototypes, exploration
JavaScriptNo compilation step, lower barrier to entryScripts, simple tools

Styling

OptionStrengthsBest For
CSS Custom PropertiesNative browser support, runtime theming, design token friendlyDesign system projects, multi-theme apps
Tailwind CSSRapid prototyping, consistent spacing/color scales, small outputMarketing sites, content-heavy pages
CSS ModulesScoped by default, no runtime cost, familiar syntaxComponent libraries, apps with complex layouts

Testing

OptionStrengthsBest For
VitestFast, ESM-native, compatible with Vite configVite-based projects, modern codebases
JestMature ecosystem, extensive mocking, widespread familiarityNext.js projects, teams with existing Jest tests
PlaywrightCross-browser E2E, reliable selectors, built-in test generatorUser journey testing, critical flows
CypressInteractive runner, time-travel debugging, component testingTeams that prefer visual test development

The ADR Template

Use a lightweight Architecture Decision Record for each choice:

## ADR: {Decision Title}

**Status:** Accepted | Superseded | Deprecated
**Date:** {YYYY-MM-DD}

### Context
What is the situation? What forces are at play?

### Decision
What did we choose and why?

### Consequences
What becomes easier? What becomes harder?
What are we explicitly accepting as a trade-off?

Keep ADRs in a DECISIONS.md file at the project root. Number them sequentially. When a decision is superseded, link to the replacement — don't delete the original.

Anti-Pattern: Implicit Choices

Signs that stack decisions are missing:

  • Two different testing frameworks appear in the same repo.
  • A developer asks "why do we use X?" and nobody can answer.
  • A dependency is added that duplicates functionality already present.
  • The team debates the same tool choice in multiple PRs.

The fix is always the same: write down the decision, even retroactively.

See Also

On this page