← Back to Blog

Building a Web App with Claude Code: Plan Mode, Feature Branches, and Debugging

5 min read

Part 2 of 3 in the “Building with Claude Code” series


In Part 1, we set up Claude Code, integrated it with IntelliJ, and pushed our scaffolded project to GitHub. The foundation is in place. Now we build.

This post covers the meatiest part of the experience: using Plan Mode to architect features before writing code, working through a proper Git feature-branch workflow, and debugging issues by letting Claude read stack traces.

The Feature-Branch Workflow

Before we write a line of feature code, let’s establish a workflow. In professional teams, you never commit directly to main. Instead:

  1. Create a feature branch
  2. Build and test on that branch
  3. Push and open a pull request
  4. Review, then merge

Claude Code handles all of this:

Create and switch to a new branch called feature/data-layer

Plan Mode: Think Before You Code

This is the feature that convinced me Claude Code is more than a fancy autocomplete. Plan Mode tells Claude to analyze, plan, and wait for your approval before writing anything.

Press Shift+Tab (or type /plan) and describe what you want:

/plan
I need a Bookmark entity with the following fields:
- id (auto-generated Long)
- url (String, required)
- title (String, required)
- description (String, optional)
- tags (comma-separated String for now)
- createdAt (LocalDateTime, auto-set)
- favorite (Boolean, default false)

Also create:
- A JPA repository interface
- A service class with CRUD operations + search by tag
- A REST controller with endpoints for all operations

Plan this out before writing any code.

Claude responds with a detailed plan showing each file it will create, the endpoints, and the architecture. You can review and course-correct before any code is written.

Once you approve:

Looks good. Go ahead and implement it.

Testing the API

After Claude creates the files, ask for test commands:

Give me curl commands to test all the endpoints.
Include creating a few sample bookmarks with different tags.

Claude generates a full test script:

curl -X POST http://localhost:8080/api/bookmarks \
  -H "Content-Type: application/json" \
  -d '{"url":"https://kotlinlang.org","title":"Kotlin Docs","tags":"kotlin,docs"}'

curl http://localhost:8080/api/bookmarks

curl http://localhost:8080/api/bookmarks/search?tag=kotlin

curl -X PATCH http://localhost:8080/api/bookmarks/1/favorite

Your First Pull Request from Claude Code

Commit and push the feature branch:

Commit all changes with the message "feat: add Bookmark entity,
repository, service, and REST controller".
Push the feature/data-layer branch to origin.

Then create a pull request:

Create a GitHub pull request from feature/data-layer to main
with the title "Add data layer: Bookmark CRUD API" and a description
summarizing what was built. Use the GitHub CLI.

Check your GitHub repo — there’s a proper PR with a descriptive body. Merge it:

Merge the PR with squash, switch to main, and pull.

This entire workflow — branch, build, test, commit, PR, merge — happened inside Claude Code.

Building the Frontend

Branch again and prompt:

Create a clean, modern single-page frontend for the bookmarks manager.

It should have:
- A form at the top to add new bookmarks
- A search/filter bar that filters by tag
- A list of bookmarks displayed as cards
- Each card shows: title, description, tags as pills,
  a favorite toggle, and a delete button
- Color scheme: white background, blue accents (#2563EB)

Claude creates an index.html with embedded CSS and JS that looks surprisingly polished.

Refining with File References

Open index.html in IntelliJ, select the tag pills section, then tell Claude:

@src/main/resources/templates/index.html#L45-60
Make the tag pills clickable — clicking one should filter
the bookmark list to only show bookmarks with that tag.

The @file#lines reference gives Claude surgical precision.

Debugging with Claude

When the app crashed adding a bookmark without a description, I just pasted the stack trace:

The app crashes when I try to add a bookmark without a description.
Here's the error:

org.springframework.dao.DataIntegrityConstraintViolationException:
could not execute statement; SQL [insert into bookmark ...]

Claude immediately identified the issue — the description field was a non-nullable String instead of String?. Fix proposed, approved, done.

Adding Import/Export

For the next feature, I asked Claude to think through edge cases:

Add an import/export feature:
1. An "Export" button that downloads all bookmarks as JSON
2. An "Import" button to upload JSON and bulk-add bookmarks
3. Handle duplicates by URL — skip if already exists
4. Show a summary after import

Think through edge cases before implementing.

That instruction made a real difference. Claude considered malformed JSON, empty files, large imports, and duplicate detection performance.

Keeping the Context Clean

By this point, the conversation was getting long. The fix is simple:

/compact

This summarizes the conversation, freeing up context while keeping important decisions. Run it before starting each new feature.

Key Takeaways

  1. Plan Mode is non-negotiable for multi-file changes
  2. Feature branches with PRs aren’t just for teams — they give you a clean history and safety net
  3. Claude debugs faster than I do — paste the stack trace, get the fix
  4. “Think through edge cases” is a magic prompt
  5. Run /compact regularly

Coming Up Next

In Part 3, we’ll cover power-user tricks: piping Git diffs into Claude for automated code review, auto-generated commit messages, and building a prompt library that lives in your repo.


All code is on GitHub.