There is a category of work that every experienced engineer knows matters and almost nobody does consistently. Not because they lack the skill. Not because they do not value it. Because the deadline is already here.
Modeling state before writing a line of it. Stress-testing an architecture before committing to it. Understanding a system before modifying it. The preflight work. The thinking that separates a codebase that holds from one that becomes someone else's archaeology project six months later.
This is the work that creates The Janitor Pattern when it gets skipped. And it gets skipped constantly, not out of laziness or ignorance, but because time was the binding constraint. There was never enough of it to think before acting.
That constraint is largely gone now. Most teams have not noticed what that actually means.
What AI Actually Unlocks
The dominant narrative around AI-assisted development is about throughput. Write code faster. Generate boilerplate. Autocomplete your way through implementation. That is real, but it is the least interesting part of what changed.
The more significant unlock is cognitive. When implementation is fast, the bottleneck shifts back to where it always belonged: decisions. What are we building and why. What state actually needs to exist. Where the boundaries go. What happens when requirements change.
These are not questions that get easier with faster typing. They get easier with time and focused thinking. AI did not make senior engineers faster at coding. It gave them back the time to actually do the architectural work they were already qualified to do but never had runway for.
That is a different claim than most people are making, and it has different implications.
The Seniority Multiplier
There is a mistake a lot of organizations are making right now.
They noticed that a junior developer with AI tooling can produce output that looks closer to senior work than it used to. That part is true. The gap in raw implementation quality has narrowed.
What they missed is the more important implication: a senior engineer with the same tools can produce output closer to five or ten seniors working without them. That is not a marginal improvement. That is a structural change in what one person can accomplish.
The reason is not code generation. A junior with AI increases coding throughput. A senior with AI improves the entire decision system behind the code.
That means defining the right problem before it gets expensive. Cutting bad scope before it ships. Choosing architecture that survives contact with production. Directing AI toward useful output instead of accepting confident noise. Catching wrong assumptions before they compound into something expensive. Keeping long-term maintainability intact while still moving fast.
None of that is something AI does on its own. It requires the judgment to know what questions to ask, recognize a wrong answer when you see one, and understand the tradeoffs well enough to make real decisions rather than defer to whatever the tool suggests.
The cost of building fast in the wrong direction is higher now, not lower, because the speed is higher. A team generating confident, well-structured code aimed at the wrong problem ships a very polished failure. That failure arrives faster than it used to.
That is why the value of genuine seniority did not go down when AI tooling arrived. In most real-world contexts, it went up.
The Workflow
A common objection to structured workflows is that they slow you down. The opposite is true once the system is in place. Output speed increases because there is no time lost to wrong assumptions, scope creep, or rework caused by starting before the problem was understood. The quality bar stays high by default rather than by heroic effort. And the dividends compound: the longer you use it, the more the constraint system knows about your codebase, the less friction each new task carries. The first task is the most expensive. The fiftieth is fast and clean.
The workflow follows a strict phase sequence: preflight, plan, implement, verify, review. Each phase is a named command. None are optional. None can be skipped. The sequence is the discipline.
But the sequence is only half of it. The other half is what the AI knows before the first command runs.
The workflow in the Battleship project is built on two layers of encoded knowledge. First, CLAUDE.md, the engineering standards document that defines every architectural constraint: layer contracts, state design rules, coordinate handling, TypeScript requirements, accessibility rules, testing strategy. Second, a set of skills in .claude/skills/ that encode domain expertise for specific concerns: react-architecture for layer separation and state design, wcag-react for accessibility implementation patterns, vitest-react for testing strategy. The skills load automatically in the right context. The AI does not re-discover these rules each session. They are written down, versioned, and available every time.
This is the part most AI workflow descriptions miss. The commands are the trigger. The standards document and skills are the constraint system. Without them, the workflow is a prompting pattern. With them, it is a system.
Preflight is the first phase and the most important one. The command instructs the AI to read CLAUDE.md in full, search the existing codebase for relevant types, utilities, and logic, identify the correct layer for each piece of work, check for duplication, and state the delta: one sentence per file to be created or modified. If it cannot state the delta clearly, the scope is not clear enough. Stop and ask.
No code is generated in this phase. That constraint is deliberate.
This step works in two directions at once. When the preflight report matches your mental model, you have a verified shared baseline before a single line changes. When it surfaces something you had not fully articulated, an existing utility you had forgotten, an ambiguous layer placement, an assumption you had not examined, you learn something about your own system. Both outcomes are useful. Neither is accidental. The surface area that comes up unexpectedly is exactly where bugs live and refactors go wrong.
Plan produces a reviewable artifact before any implementation begins. Not a vague description. A concrete specification. Every new type with its name, shape, and rationale. Every new function with its signature, layer placement, purity, and test strategy. Every state change with what gets persisted, what gets derived, and why. Every component with its props interface and what it emits. Accessibility impact. Risk flags. One sentence per file to be created or modified.
The plan is a checkpoint, not a suggestion. It requires explicit approval before anything is written. If the plan is wrong, you catch it here, before it is encoded in code that needs to be undone.
Implement writes the approved code, one complete file at a time. No scope expansion. No reinventing what already exists. The approved plan is the boundary.
Verify runs before human review, not as part of it. The command runs through a checklist: architecture boundaries, TypeScript correctness, component composition rules, accessibility compliance, test coverage, CI simulation. Architecture pass, TypeScript pass, accessibility pass, tests pass: each is an explicit gate. If anything fails, it gets fixed before the handoff. The AI does not ask for review with known violations.
Review surfaces the implementation with decisions, tradeoffs, and anything requiring human judgment explicitly flagged. Things noticed during implementation but out of scope for the current change go here as deferred observations rather than silently expanding the diff.
The framing from the project documentation captures it precisely: the same discipline you would apply working with a capable but unsupervised junior engineer. No implementation before the design is agreed. No review request before verification passes. No unexplained decisions in the final output.
Most problems in software do not come from writing bad code. They come from starting before the problem was understood.
Here is what a task prompt actually looks like. This is the document used to implement the CLI runner in the Battleship project. It carries both the specification and the execution instructions. The AI workflow section tells the AI exactly which commands to run and in what order, so the full phase sequence is encoded in the prompt itself:
Task: Implement CLI runner
Context: The engine layer is pure (state, action) => state with no React dependency. A terminal runner should consume it directly -- same reducers, same services, same types -- with zero changes to existing code. This validates that the layer boundaries are real.
Pre-task decisions (do not re-open):
- No React imports anywhere in
src/cli/ - No game rules in the CLI -- only I/O, rendering, and driving the engine
-
LineReaderinterface abstracts Node's readline to avoid importing Node types under the vite/client type scope - No colors, no ANSI formatting, no AI shot delay -- terminal output is synchronous, the delay serves no purpose
- No placement phase -- both modes use
generateRandomLayout - No tests for
src/cli/-- all meaningful logic is covered by engine and service tests
Files to create (4):
src/cli/
index.ts -- entry point, mode and difficulty menus, readline lifecycle
loop.ts -- game loops for single-player and vs-computer modes
renderer.ts -- pure string rendering, board grids, shot results, game-over messages
input.ts -- coordinate parser, readline prompt loop, LineReader interface
Files to modify (2):
-
tsconfig.cli.json-- confirm CLI entry point is correctly configured -
docs/ARCHITECTURE.md-- add CLI section documenting consumer pattern and AI delay omission
Types:
-
LineReaderinterface ininput.ts:{ question(prompt: string, cb: (answer: string) => void): void; close(): void }-- abstracts readline, no Node type import required - No new domain types -- all existing types from
engine/,services/,types/used as-is
Functions:
-
renderBoard(boardState: BoardState, label: string): string-- pure string function, no side effects, unit testable -
parseCoordinateInput(input: string, boardSize: number): Coordinate | null-- pure, returns null on invalid input -
runSinglePlayerLoop(rl: LineReader, difficulty: Difficulty): Promise<void>-- async, drives engine reducer directly -
runVsComputerLoop(rl: LineReader, difficulty: Difficulty): Promise<void>-- async, same pattern
State: No new state. CLI drives engine reducers as plain functions: state = reducer(state, action).
Accessibility impact: None -- terminal only.
Required AI workflow:
-
/cmd-preflight-- read CLAUDE.md, search for existing engine reducers, service functions, and types relevant to this task. Confirmsrc/cli/does not exist yet. Runnpx tsc -p tsconfig.json --noEmit,npm test,npm run lint. Do not proceed if anything is failing. -
/cmd-plan-- produce the full implementation plan covering types, function signatures, file list, and the ARCHITECTURE.md update. Wait for explicit approval before writing any code. -
/cmd-implement-- implement in this order:input.ts,renderer.ts,loop.ts,index.ts, then updatedocs/ARCHITECTURE.md. One complete file at a time. No scope expansion. -
/cmd-verify-- confirm zero React imports insrc/cli/, no domain logic duplicated from services, TypeScript clean, all existing tests still passing. -
/cmd-review-- surface decisions, tradeoffs, and the AI delay omission explicitly. Flag anything requiring sign-off.
Risk flags:
-
crypto.randomUUIDmay not be available in all Node versions -- confirm or useMath.randomfallback - AI shot delay is intentionally omitted -- document explicitly so a future reader does not add it back
Testing plan: No CLI-specific tests. Engine, services, and renderer pure functions are covered by existing and new unit tests. renderer.ts and input.ts are testable in isolation if needed later.
Acceptance criteria:
-
npm run clistarts the game - Both single-player and vs-computer modes work end to end
- Difficulty selection works
- Shot input parses correctly, rejects invalid input with a message
- Game over is detected and reported
- Zero changes to
engine/,services/,utils/,types/, orconstants/ -
src/cli/has no React imports -
docs/ARCHITECTURE.mdupdated with CLI section
Do not:
- Do not import React anywhere in
src/cli/ - Do not duplicate any rule logic from
services/ - Do not add the AI shot delay
- Do not add a placement phase
- Do not commit -- stop after review approval
This document is what gets approved before a line of code is written. The AI implements against it. The scope is fixed. What comes out the other end is a single, reviewable, purposeful commit, not a 40-file diff with no clear narrative.
Atomic prompts produce atomic commits. The git history stays readable. Reviews stay tractable. Not as a side effect of the workflow. As a structural outcome of defining scope before touching code.
This is what speed with quality actually looks like. Not heroics, not shortcuts. A repeatable system.
What This Looks Like in Practice
The Battleship project I recently published was a direct application of this workflow. The goal was not to build a game. It was to demonstrate what disciplined frontend architecture looks like when you actually have time to think before writing code.
The state logic lives in a pure engine layer: plain TypeScript, no React imports, no framework dependency. The hook is wiring only. The CLI runner, whose full task prompt appears in the workflow section above, is the proof that the boundaries are real. The same engine, the same reducers, the same services, consumed by a Node terminal process with zero changes to domain code. A different output layer consuming the same logic without modification.
But the more telling artifact is not the code. It is docs/ARCHITECTURE.md, a document that explains every significant decision in the codebase, every alternative that was considered, and the reasoning behind each choice. It includes a "Key Discussion Points" section that anticipates the questions a technical reviewer would ask and answers them before they are asked. It exists because the workflow created space for that kind of thinking before any code was written.
That document does not get written under normal deadline pressure. Not because the engineer is less capable, but because there is never time. The constraint was never knowledge. It was time.
That is the workflow. Not "AI writes the code." AI handles the implementation details while I spend the recovered time on the decisions that actually determine whether the system will survive.
The Honest Caveat
The tools do not give you judgment. They give you time to use the judgment you already have.
For engineers who were already doing the thinking but running out of runway, that is a genuine and significant unlock. That constraint is gone.
For everyone else, it is faster output. Which is useful, but it is not the same thing, and it will not produce the same results.
The difference shows up six months later, when one codebase is still being extended cleanly and another has become someone's full-time archaeology project.
Top comments (0)