Technical Design

In the previous section, we wrote a detailed PRD that specifies exactly what our Text Comparer should do — real-time diffing, multiple granularity modes, synced scrolling, file upload, and export. Now it's time to turn that PRD into a technical design — defining the component structure, data flow, and architecture before we write any code.

Why Understand the Design?#

You might think: if Claude Code is writing the code, why do I need to understand the technical design? Can't I just let the AI handle it?

AI coding agents are remarkably good at generating working code — but they're not perfect. When something breaks, the agent might go in circles: trying fix after fix without understanding the root cause. This is where your understanding of the design becomes critical. If you know how the diff algorithm processes two texts, how the Web Worker communicates results back to the main thread, or how synced scrolling keeps both panels aligned across insertions and deletions, you can:

  • Diagnose bugs faster — Instead of waiting for the AI to debug, you can pinpoint whether the issue is in diff computation, highlight rendering, or scroll synchronization.
  • Guide the AI effectively — A prompt like "the bug is in how padding lines are inserted to keep diff chunks aligned" is far more useful than "the scrolling is broken, fix it."
  • Fix issues yourself — Sometimes the quickest path is a manual two-line fix. You can only do that if you understand the architecture.

Think of the technical design as your map. Claude Code is a powerful driver, but when the GPS loses signal, you need to be able to read the map yourself.

Writing the Design#

Before starting, clear your conversation history — in Claude Code, use the /clear command. This matters because the previous conversation about writing the PRD leaves behind context (drafts, revisions, review feedback) that can bias the AI's design decisions. A fresh conversation forces the AI to work from the PRD file itself — the finalized version — rather than half-remembered earlier drafts.

We'll ask Claude Code to read the PRD and produce a DESIGN.md that covers the architecture, UI layout, and key implementation decisions. Having this as a separate file keeps concerns clean: the PRD says what to build, and the design says how to build it.

Ask AI to generate a technical design document based on the PRD.

Read PRD.md and write a technical design document. Save it to DESIGN.md. We're using Next.js with the App Router and TypeScript. The design should cover: - High-level architecture (what are the main modules and how do they connect) - UI design (layout, panels, interactions — including how diff highlighting renders inline) - File structure (where each module lives in the project directory) - Web Worker design (diff computation off the main thread, message protocol, debouncing) - Scroll sync strategy (how panels stay aligned across insertions/deletions) Keep it practical and get straight to the design.

Here's an example of what Claude Code might produce. The design covers the high-level architecture showing how the editor panels, diff engine, and Web Worker fit together; the UI design with layout wireframes for the side-by-side panels, toolbar, and diff highlighting; the file structure mapping every component, hook, and utility to a specific file; the Web Worker design explaining the diff computation off the main thread, the message protocol, and debouncing strategy; and the scroll sync strategy describing how panels stay aligned across insertions and deletions using padding lines.

Revising the Design#

The generated design is solid — it covers the architecture, UI layout, file structure, worker protocol, and scroll sync in a way that's ready to implement. But just like with the PRD, it's worth reviewing with a critical eye before we start coding. A design doc that's too complex or includes unnecessary abstractions will lead the AI to generate over-engineered code.

If you read through the design carefully, you'll notice several issues worth fixing:

  1. Missing src folder — The file structure puts components/, context/, workers/, lib/, and types/ at the project root. We choose to include a src/ folder to keep the root clean and separate source code from config files. The app/ directory should also move inside src/.
  2. Granularity re-computation is inconsistent — The design says Word/Char modes "share the same diff result" but Line mode requires re-computation. In reality, all three modes need different diff algorithm calls (diffLines, diffWords, diffChars), so switching between any modes triggers a re-compute. The design should be consistent about this.
  3. Padding lines approach is over-engineered — The scroll sync section describes virtual padding lines with CSS height spacers and minimal DOM elements. For the initial version, a simpler approach works: since both panels use fixed-height line rendering, compute a line-number-to-offset mapping from the alignment anchors and translate scroll positions mathematically. Virtual padding can be a future optimization if needed.
  4. Missing error states for the worker — The protocol defines a DiffError type but the design never explains when errors occur or how the UI handles them. Add: if the text exceeds 5 MB or 100k lines, the worker should post an error and the UI should show an inline warning instead of attempting the diff.

You could fix these one at a time, but here we'll batch all four fixes into a single prompt:

Ask AI to fix the UI layout, diff logic, and simplify the design.

Revise DESIGN.md to fix these issues: 1. Add a src/ folder — move app/, components/, context/, workers/, lib/, and types/ under src/. Update all references in the file structure section. 2. Fix the granularity switching logic — all three modes (Line, Word, Character) require different diff algorithm calls, so switching between any mode triggers a re-compute via the worker. Remove the claim that Word/Char share results. 3. Simplify the scroll sync: remove the virtual padding lines approach. Instead, use a line-offset mapping computed from alignment anchors. 4. Add worker error handling: if input exceeds 5 MB or 100k lines, the worker posts a DiffError and the UI shows an inline warning banner above the editors. Update the layout wireframes, file structure, and any references to match.

The design also doesn't mention testing. The pure functions in lib/ — diff computation, export format generation, scroll offset calculation, file validation — are the core logic of the app and are easy to test in isolation. Adding a test plan to the design ensures the AI generates testable code from the start.

Ask AI to add a testing section to the design.

Update DESIGN.md to add a testing section. Include unit tests for the pure functions in lib/ (diff computation, export generation, scroll sync mapping, file validation, settings persistence). Specify the test framework (Jest) and where test files should live in the file structure.

Here's the revised design with all the fixes applied. The file structure now lives under src/, the granularity logic correctly re-computes for all mode switches, the scroll sync uses a simpler mathematical offset mapping, worker error handling is defined, and a testing section covers unit tests for all lib/ functions using Jest.

Reviewing the Design#

Just as we did with the PRD, the final step is to let Claude Code review the design for any issues we might have missed — contradictions between sections, missing details that would block implementation, or unnecessary complexity that slipped through.

Ask AI to check for conflicts or gaps in the design.

Review DESIGN.md for any conflicting, unclear, or incomplete areas. Flag anything that would block or confuse a developer during implementation — contradictions between sections, missing details, ambiguous behavior, or unnecessary complexity. Also check that the file structure, component list, and worker protocol are all consistent with each other.

Here's what Claude Code flagged — contradictions, missing details, and ambiguities:

These are all valid catches. Let's ask Claude Code to resolve them:

Ask AI to fix the flagged contradictions and gaps.

Resolve the issues you found in DESIGN.md.

Here's the final design with all issues resolved:

You can repeat this review-and-resolve cycle as many times as needed until no issues remain. A design with no loose ends means fewer surprises when we start building.

With the design finalized, we're ready to move on to implementation.