Scaffold First
Ship the boring structure before the interesting logic.
When to Use
A new feature requires a lot of boilerplate, configuration, file structure, types, or infrastructure alongside the actual business logic. Instead of one massive PR that's impossible to review (reviewer sees 40 new files and checks out mentally), split the structural setup from the logic. Reviewers can quickly verify the scaffold is correct (file naming, types, configuration), then focus their attention on the logic PR where the actual complexity lives.
Use this technique when:
- A new feature requires many new files — routes, services, types, tests, configuration — alongside the actual logic
- The boilerplate is easy to verify, but the logic is not — a reviewer can sanity-check empty function signatures in 30 seconds, but needs to think carefully about the business logic
- You want reviewers focused — separating scaffold from logic means each PR has a clear, single thing to evaluate
The Pattern
- PR 1 — Scaffold: All the structural work — new files, directory layout, type definitions, empty function signatures, configuration changes. Everything compiles and a placeholder test passes, but no meaningful logic exists yet.
- PR 2 — Core logic: Implement the happy-path business logic, slotting into the established scaffold. This is where the reviewer's attention should be.
- PR 3 — Edge cases: Input validation, error handling, and defensive code. Small, focused.
- PR 4 — Polish: Logging, metrics, rate limiting, observability. Easy to review in isolation.
Worked Example
The team is adding a new API endpoint for product reviews. This involves a new route, service, types, database operations, and tests. A single PR would touch six new files with 300 lines of logic tangled together — impossible to review meaningfully.
Instead, we build the skeleton first: route and service files with empty function bodies and placeholder return values that compile and pass a smoke test. Then we fill in the logic in focused follow-up PRs. The scaffold PR is boring by design.
PR 1: Scaffold review endpoint — types, empty service, smoke test, route
Create four new files, all intentionally empty of business logic. The type definitions are complete. The service functions have correct signatures but return dummy data. The route handlers are wired to the service. The test file has a single smoke test that passes.
The entire PR is boring by design. Four files land: src/types/review.ts, src/services/reviews.ts, src/services/reviews.test.ts, and app/api/reviews/route.ts. The diff here shows the service file — correct signatures, dummy return values, nothing to reason about yet. A reviewer can skim all four in two minutes and verify the shape is right. That's the point.
| line number | line content |
|---|
PR 2: Implement review CRUD with real database queries
Fill in getReviews() and createReview() with real Drizzle ORM queries. The scaffold is already in place — the function signatures, return types, and the route's service imports are all unchanged. This PR adds ~19 lines of actual logic into an already-understood structure.
Notice how small this PR feels despite adding meaningful functionality: the reviewer already knows the shape from PR 1. They can focus entirely on whether the SQL is correct — do we paginate right? do we return the right fields? — without also having to parse file structure and type definitions at the same time.
| line number | line content |
|---|
PR 3: Add Zod validation and proper HTTP error responses
Add a Zod schema for review creation, validate the request body in POST, and return correct HTTP status codes: 400 for missing productId or invalid input, 409 for a duplicate review, 500 for unexpected errors.
This is a focused diff in a single file. The service layer is not touched — validation is a separate concern from persistence, and this PR makes that separation explicit. A reviewer can verify the validation rules are complete and the error responses match the API spec without reading anything about database logic.
| line number | line content |
|---|
PR 4: Add rate limiting and structured request logging
Add a rate limit check before each handler (60 reads/min per IP, 10 writes/min per IP) and structured log entries on successful operations. This is a pure observability addition — no business logic changes, no type changes, no test changes.
The diff shows two hunks in the same file. Each handler gains a four-line rate limit block at the top and one log call inside the happy path. Easy to verify in isolation: the reviewer can check the rate limits are sane and the log fields are useful without context from any other PR.
| line number | line content |
|---|
Common Mistakes
Making the scaffold PR "too empty." Empty files with just // TODO comments are not a scaffold. A scaffold PR should compile cleanly, have correct types, and include at least one test that runs. The smoke test passing is proof the structure is sound. An export {} file tells reviewers nothing useful.
Sneaking logic into the scaffold PR because "it was right there." The entire value of this technique is that the scaffold PR is boring and takes five minutes to review. The moment you add interesting logic, you've defeated the purpose. Keep the scaffold pure. If you find yourself implementing something meaningful, save it for PR 2.
Over-scaffolding: creating abstractions you don't need yet. The scaffold should match the exact shape of the feature, not a generalized framework. Don't create a generic BaseService class when the feature only needs a reviews service. Don't create an abstraction layer "for when we add more endpoints later." Build the minimum structure the current feature requires — no more.