What is code scaffolding?

1,901 words · 10 min read
Archie Cowan
By Archie Cowan
Senior Prototype Developer for AWS global and strategic customers, former ITHAKA/JSTOR Chief Architect. Generative AI builder. Aspiring to do everything.
The other 20% is up to you.
The other 20% is up to you.

What is code scaffolding?

Code scaffolding is the technique of generating a software project's baseline structure — directories, config files, build setup, and starter code — so that you can skip the boilerplate and start writing the parts that are actually unique to your application.
When you run rails new myapp, cargo new myapp, or npx create-next-app, you're scaffolding. The tool reads your choices (or its own defaults), creates a folder, and fills it with everything a project of that type usually needs: a build file, a source directory, a .gitignore, sometimes a starter "hello world" route, sometimes a test setup, often a lockfile.
Clone it, run it, build on it.

Where the metaphor comes from

Real scaffolding on a building site is temporary structure that lets workers reach parts of the building they couldn't otherwise. It supports the work. It isn't the work. Code scaffolding works the same way.
The generated files are the platform your application sits on while you build it. Files you'll never touch (.gitignore, basic Webpack config). Files you'll heavily modify (your routing layer, your data models). Files you'll delete the day you outgrow the defaults. The scaffolding's job is to get you to the point where you can start writing the interesting code without making 47 setup decisions first.

What scaffolding does for you

Mostly these 3 things:
  1. Picks the boring decisions. What's the directory layout? Which test framework? How does the build run? A scaffolder picks defaults so you don't have to.
  2. Wires things together. A blank package.json doesn't help much. A scaffolder gives you package.json plus a src/, plus a working npm run dev, plus the dependencies installed.
  3. Encodes a community's idea of "right." When you scaffold a Rails app, you're inheriting a decade of opinions about how Ruby web apps should be organized. That's a lot of free signal.
The phrase "convention over configuration," coined by Rails, is an argument for scaffolding. The convention is what the scaffolder generates.

Is "AI scaffolding" the same thing?

The term has split into two meanings.
AI-assisted code scaffolding is the original meaning applied to AI tools. You ask Cursor, Claude, or Kiro to "set up a Next.js project with TypeScript and Tailwind" and it runs the scaffolding command, or generates the equivalent files directly. Same scaffolding, with an LLM at the steering wheel.
LLM scaffolding (sometimes called "agent scaffolding") refers to the code, prompts, and tools wrapped around a language model to make it more capable. A web search tool, a code execution sandbox, memory between calls — scaffolding for the LLM. Same construction metaphor, applied to a different builder.
One kind scaffolds your project with AI. The other scaffolds AI with code.

Why scaffolding looks different in every stack

I've spent my career straddling different platforms. If you work on the web, this is pretty normal. Though, most stacks set out to focus on a particular problem that drives the idioms the creators adopt. Some are more or less opinionated about scaffolding structure.
Rails ships you the kitchen sink and a generator for every noun. Rust hands you a Cargo.toml and wishes you well. Next.js asks eight questions before writing a single line. SvelteKit just shipped a brand new CLI that renamed the entry point to sv create. Four frameworks. Four philosophies. Four six-month futures, none alike.
Aside: this is one of those things that's invisible until you do it across enough stacks that the patterns start showing through.
So I made a matrix. Same workflow as the polyglot build tools comparison: draft the structure, fill the first row by hand, hand the AI a research guide and a task list, verify every command against current docs.

Scaffolding by stack

StackScaffold commandProject variantsIncluded out of the boxPost-init generatorsConfig styleScaffolding philosophy
Ruby on Railsrails new myapp--api, --minimal, --database=postgresql, --javascript=esbuild, etc.DB, ORM, router, views, tests (Minitest), asset pipeline, dev serverMany: scaffold, model, controller, migration, mailerconfig/*.rb filesConvention over configuration; one canonical layout
Djangodjango-admin startproject myproject then python manage.py startapp myappNone at project level; apps are the unitSettings, URL conf, WSGI/ASGI entry, admin, ORM (no DB chosen yet)⚠️ A few: startapp, makemigrations, no view/model generatorssettings.py Python moduleExplicit over implicit; you wire it up
Spring Bootspring init --dependencies=web,data-jpa myapp or Spring Initializr web UIDriven by dependency selection (web, data-jpa, security, kafka, etc.); over 30 startersBuild file (Maven/Gradle), main class, application.properties, picked starters wired in❌ None bundled (IDE templates fill the gap)application.properties / application.ymlPick your dimensions up front; starters wire the rest
Phoenix (Elixir)mix phx.new myapp--no-ecto, --live, --no-html, --umbrella, --databaseRouter, contexts, LiveView, Ecto, ExUnit, asset pipeline, releasesMany: phx.gen.html, phx.gen.json, phx.gen.live, phx.gen.auth, phx.gen.contextconfig/*.exs per environmentFull-stack defaults; LiveView assumed unless opted out
ASP.NET Coredotnet new web (or webapi, mvc, blazorserver, worker)Dozens of templates; each is its own variantProgram.cs, csproj, launch settings, picked template's structure⚠️ dotnet aspnet-codegenerator (separate tool, MVC scaffolds)csproj XML + appsettings.jsonTemplate-per-shape; one CLI, many starting points
Next.jsnpx create-next-app@latestInteractive prompts: TS/JS, ESLint, Tailwind, src/, App Router, Turbopack, import aliasApp Router scaffold, page, layout, picked tooling pre-wired❌ No first-party generatorsnext.config.js + package.jsonInteractive prompts decide the stack
SvelteKitnpx sv create myappTemplate prompt + opt-in add-ons: TypeScript, ESLint, Prettier, Vitest, Playwright, Tailwind, Storybook, etc.Vite + SvelteKit core; everything else is opt-in via add-onssv add (add-ons after init)svelte.config.js + vite.config.tsMinimal core; add-ons compose what you need
Nuxtnpx nuxi@latest init myappSingle template; package manager promptNuxt app, pages dir, server routes, auto-imports⚠️ nuxi add for components, pages, middleware, layoutsnuxt.config.tsAuto-imports + module ecosystem do the heavy lifting
NestJSnpm i -g @nestjs/cli && nest new myprojectPackage manager prompt; otherwise single templateTypeScript, modules, controllers, services, Jest, decorator-driven structureMany: nest g module, nest g controller, nest g service, nest g resourcenest-cli.json + per-app tsconfigAngular-style monolith; generators enforce structure
Expo (React Native)npx create-expo-app@latest myappTemplates via --template: blank, blank-typescript, tabs, defaultExpo Router, TypeScript (in default), example screens❌ No generators (use Expo Router file conventions)app.json / app.config.tsManaged workflow first; native code stays hidden
Flutterflutter create myapp--template=app, --template=plugin, --template=package, --template=moduleDart entry, widget tree, platform folders (iOS/Android/web/desktop), tests❌ No generators bundledpubspec.yamlSingle template, multi-platform output
Rust + Axumcargo new myapp then cargo add axum tokio; or cargo loco new for a Rails-style full stackNone canonical via Cargo; Loco ships SaaS / REST / lightweight starters; cargo-generate for arbitrary templatesPlain Cargo: Cargo.toml, src/main.rs, .gitignore. Loco: routing, ORM (SeaORM), auth, jobs, mailers, tests⚠️ None in Cargo; ✅ Loco has cargo loco generate model/controller/scaffold (Rails-style)Cargo.toml + module structureCargo gives you a blank canvas; Loco picks Rails conventions for you
Go web (Gin/Echo/Chi)go mod init github.com/me/myapp then go get github.com/gin-gonic/gin; or gonew (Go-team experimental) for module templates; or go-blueprint create for an interactive web-stack scaffolderPlain Go: nothing canonical. go-blueprint: framework + DB + features (htmx, Docker, GitHub Actions, React frontend). gonew: any module published as a template (e.g. Google Cloud's templates)Plain: go.mod, go.sum. go-blueprint: framework wired in, DB driver, optional Docker / CI / frontend. gonew: whatever the source module ships❌ No generators in any of themGo files, no central configThe core team ships only templates (gonew); the project layout debate gave the community room to fill the gap
Python with uvuv init myapp--app, --lib, --package, --barepyproject.toml, virtualenv on first run, lockfile, hello.py (or library structure)❌ No app generators (uv is package-focused)pyproject.tomlVariant-driven init; minimal opinions beyond packaging
Projen (meta-scaffolder, any stack)npx projen new <template> (e.g. awscdk-app-ts, python, nextjs-ts) or define your own in .projenrc.tsBuilt-in templates for TS, Python, Java, Go, AWS CDK, Lambdas; or author your own blueprints for any stackWhatever you put in .projenrc.ts — package config, build, lint, CI, docs, monorepo wiring, all regenerated from one source✅ Regeneration is the generator: edit .projenrc.ts, run pnpm projen, files update across every package.projenrc.ts (TypeScript)Scaffolding as code — transcends individual stacks, encodes your patterns instead of a framework's

The exception in the matrix: Projen

Projen sits above the stacks. You write a .projenrc.ts file describing what your project should look like, and Projen regenerates the surface — package.json, tsconfig, ESLint config, GitHub Actions — every time you run pnpm projen.
Projen is to scaffolding what Infrastructure as Code was to provisioning servers. Before IaC, you stood up servers by clicking through a console or running ad-hoc shell scripts. After IaC, your infrastructure was a versioned file you could review, diff, and regenerate. Before Projen, your scaffolding was whatever rails new left behind plus whatever you hand-edited on top. After Projen, your scaffolding is a versioned TypeScript file you can review, diff, and regenerate. Same shift, different problem.
Two consequences:
Projen blueprints transcend stacks. A blueprint can stand up a TypeScript Lambda, a Python data pipeline, a CDK app, and a Next.js frontend in one repo, all configured consistently, all regenerable from the same source. The matrix shows 14 framework-specific scaffolders. Projen lets you build the 15th: the one that encodes your patterns.
It's the natural next step if you're scaffolding with AI tools. When the AI sees .projenrc.ts, it reads an explicit description of what the project should look like. The more your scaffolding is itself code, the more determinism you get from AI tools — which is the argument behind coding your own scaffolding first.
For the technical detail on Projen with PDK and Nx for polyglot monorepos, the Polyglot monorepo build and maintenance automation deep dive is the level-300 version of this section.

Some Patterns

Use flag-form scaffolders with AI assistants, not interactive prompts. Whether a scaffolder defaults to prompts (create-next-app, sv create) or skips them (rails new, mix phx.new), nearly all of them accept a flag form. Point your AI at it:
npx create-next-app@latest myapp --ts \
    --app --tailwind --src-dir \
    --import-alias "@/*" --use-pnpm --yes
One deterministic command. The disk now shows the assistant exactly what you wanted, which is a kind of chain-of-thought prompting — the structure on disk feeds every prompt that follows.
Rust and Go take a "kit, not framework" approach to scaffolding. The Rust core team ships cargo new. The Go team ships go mod init and gonew, an officially-published template copier under golang.org/x/tools. Both core teams stop at the project skeleton and trust the community to fill in framework-shaped scaffolders on top. That's where Loco lives in Rust — Rails-style cargo loco generate model, full ORM, jobs, mailers — and where go-blueprint lives in Go, with Gin-or-Fiber-or-Chi plus DB driver plus Docker plus optional React frontend. Composition over framework-imposed structure, with opt-in scaffolders for the people who want them.

Scaffold first, then prompt

If you came for the right command, the second column is what you wanted. Bookmark it.
The reason any of this matters more than it used to is AI assistants. An assistant in an empty directory has to invent a project structure. An assistant in a scaffolded directory reads every config file, every directory name, every wired dependency off the disk. The scaffold is 80% of your prompt.
So scaffold deterministically before you let AI write features. Pass the flags. Pin the versions. When you find yourself running the same create-next-app invocation by hand for every new project, code your scaffolding first — capture it, version it, regenerate it as defaults drift.

© 2026 Archie Cowan