Editing Existing Code Safely
Most of your time with Claude Code isn't spent creating new files—it's spent improving, fixing, and refactoring existing code. This lesson teaches you how to edit code safely, control what Claude can modify, and recover gracefully when changes don't go as planned.
Learning Objectives
- Understand the three permission modes: Normal, acceptEdits, and Plan Mode
- Learn how Claude's Edit tool performs exact string replacements
- Constrain changes to specific files or directories using permission rules
- Master recovery techniques: Esc, /rewind, and "Undo that"
- Use checkpoints to time-travel through your code and conversation history
- Apply the "Write Once, Read Many" principle for cleaner codebases
Permission Modes: Controlling Edit Access
Claude Code has three distinct modes that control how it interacts with your files. Understanding when to use each mode is key to productive, safe collaboration.
Normal Mode (Default)
In Normal Mode, Claude asks for permission before every file edit. You'll see a confirmation prompt showing the exact changes:
Claude wants to edit src/components/Header.tsx
- const menuOpen = false;
+ const [menuOpen, setMenuOpen] = useState(false);
[Accept] [Reject] [View Full Diff]
This is the safest mode for exploratory work, first-time tasks, or when you're uncertain about the changes. The downside? Interruptions. If Claude needs to make 12 edits across 8 files, you'll click "Accept" 12 times.
When to use Normal Mode:
- Learning a new codebase
- Working on critical production code
- Unsure about Claude's approach
- First time automating a specific task
acceptEdits Mode (Shift+Tab)
Press Shift+Tab once to enter acceptEdits mode. Claude now auto-accepts all file edits for the current session without asking. You'll see a banner at the top of the chat:
🟢 acceptEdits Mode: All file edits will be auto-accepted
This mode is perfect for trusted, repetitive tasks where you've already reviewed Claude's approach. Need to refactor 15 components to use a new API pattern? acceptEdits mode eliminates the friction.
When to use acceptEdits Mode:
- Batch refactoring with a proven pattern
- You've reviewed Claude's first few edits and trust the direction
- Time-sensitive work where interruptions slow you down
- Repetitive tasks like renaming variables or updating imports
Trust, but Verify
Even in acceptEdits mode, review the git diff afterward. Claude makes fewer mistakes than humans, but mistakes still happen. A quick git diff catches issues before they reach production.
Plan Mode (Shift+Tab Again)
Press Shift+Tab a second time to enter Plan Mode. Claude can now read files, analyze code, and answer questions—but it cannot modify anything. This is perfect for code reviews, debugging, or exploratory analysis.
📋 Plan Mode: Read-only. Claude can analyze but not modify files.
When to use Plan Mode:
- Code reviews: "What security issues exist in this authentication flow?"
- Debugging: "Why is this component re-rendering infinitely?"
- Learning: "Explain how this Redux store is structured"
- Planning: "What would we need to change to support multi-tenancy?"
Press Shift+Tab a third time to cycle back to Normal Mode.
Normal Mode → Claude asks before each edit
Shift+Tab → acceptEdits Mode (auto-accept edits)
Shift+Tab → Plan Mode (read-only)
Shift+Tab → Back to Normal Mode
How the Edit Tool Works
Under the hood, Claude uses the Edit tool to modify files. It performs exact string replacements using an old_string → new_string pattern. The old_string must exist exactly in the file and must be unique.
Here's what an Edit tool call looks like internally:
Edit({
file_path: "/src/utils/formatDate.ts",
old_string: "export function formatDate(date: Date) {\n return date.toLocaleDateString();\n}",
new_string: "export function formatDate(date: Date, locale = 'en-US') {\n return date.toLocaleDateString(locale);\n}"
})Uniqueness and Context
If the old_string appears multiple times in the file, Claude must provide more surrounding context to make it unique. For example, if a file has three functions that all start with return, Claude will include the function signature above to disambiguate.
The replace_all Flag
For changes that appear many times—like renaming a variable—Claude can use replace_all: true:
Edit({
file_path: "/src/components/UserProfile.tsx",
old_string: "isUserActive",
new_string: "isActive",
replace_all: true
})This replaces every instance of isUserActive with isActive in one operation. It's efficient but requires careful review—variable renames can have unintended side effects.
Constraining Changes: What Claude Can Touch
One of the most powerful safety mechanisms is constraining which files Claude can modify. You have several tools to enforce these boundaries.
Verbal Constraints
The simplest approach: tell Claude explicitly in your prompt.
Good examples:
- "Only modify
src/auth/login.ts, don't touch anything else" - "Refactor the pricing page, but don't change the database schema"
- "Update the component styles without modifying the API routes"
Claude respects these instructions, but verbal constraints require clear, unambiguous language. Vague requests like "be careful with the database" leave room for interpretation.
Permission Rules
For stricter enforcement, use permission rules in your .claudeignore or settings:
# Allow edits only in src/ directory
Edit(src/**)
# Deny edits to database migrations
!Edit(prisma/migrations/**)
Permission rules are evaluated before Claude takes action. If a rule blocks an edit, Claude will tell you instead of attempting the change.
dontAsk Mode
For extreme safety, use dontAsk mode. This auto-denies all tool use unless you've pre-approved specific tools. It's rare to need this level of restriction, but it exists for scenarios like:
- Reviewing code from an untrusted source
- Running Claude in a production environment where accidental changes are catastrophic
- Teaching scenarios where you want to demonstrate read-only analysis
Start Narrow, Expand Gradually
When working on a complex codebase for the first time, start with strict constraints: "Only modify the header component." As you build trust in Claude's understanding, expand the scope: "Now update the footer too."
Recovering from Bad Edits
Even with careful prompts and permission modes, sometimes Claude makes the wrong change. The key is recovering quickly without losing progress.
Esc: Stop Mid-Action
Press Esc once to interrupt Claude while it's working. The conversation context is preserved—Claude remembers what you asked and what it was trying to do. Use this to:
- Stop Claude before it edits the wrong file
- Redirect when you realize the approach is off-track
- Pause to review changes before Claude continues
Example workflow:
- "Refactor the user authentication flow"
- Claude starts editing
login.ts - You realize you meant to refactor
register.tsinstead - Press Esc
- "Actually, I meant the registration flow, not login"
Claude adjusts course without starting from scratch.
Esc+Esc or /rewind: Time Travel
Press Esc twice or type /rewind to open the rewind menu. This lets you restore your conversation, code, or both to any previous checkpoint.
Checkpoint types:
- Automatic checkpoints: Created before every file edit or major action
- Session checkpoints: Created at the start of each conversation session
- Manual checkpoints: You can create these with
/checkpoint(if available in your version)
Rewind options:
- Restore conversation only: Undo Claude's messages but keep code changes
- Restore code only: Undo file edits but keep the conversation context
- Restore both: Full time-travel to a previous state
Press Esc+Esc or type /rewind
Select a checkpoint from the timeline
Choose what to restore: conversation, code, or both
Confirm the rewind
Checkpoints persist across sessions. You can close Claude Code, come back tomorrow, and still rewind to yesterday's checkpoint.
"Undo That": Ask Claude to Revert
Sometimes the fastest recovery is simply asking: "Undo that last change."
Claude remembers what it just modified and can reverse it:
You: "Add error handling to the API route"
Claude: [Edits src/app/api/users/route.ts]
You: "Actually, undo that. I want to handle errors at the middleware level instead."
Claude: [Reverts the changes to route.ts]
This works best for simple, recent changes. For complex multi-file edits from 20 minutes ago, /rewind is more reliable.
/clear: Start Fresh
If the conversation has gone off-track—multiple failed attempts, conflicting context, or confused direction—use /clear to reset. This clears the conversation history but leaves your code untouched.
When to use /clear:
- After 2-3 failed attempts at the same task
- When switching to a completely unrelated task
- When Claude seems "stuck" repeating the same mistake
- To reduce token usage on long conversations
The Two-Correction Rule
If Claude doesn't get it right after two corrections, stop. The problem is usually your prompt, not Claude's execution. Use /clear and rephrase the request with more context or a different approach.
Course Correction Best Practices
The best way to handle mistakes is to prevent them from compounding.
Correct Early and Often
Don't wait for Claude to finish 10 edits before giving feedback. If the first edit is wrong, say so immediately:
You: "Refactor these components to use the new theme system"
Claude: [Edits Button.tsx, starts changing colors]
You: "Wait—only update the spacing, not the colors. We're keeping the current palette."
Claude: [Adjusts approach]
Tight feedback loops produce better results and save time.
Know When to Restart
If you've corrected Claude twice and it's still not right, the context is probably muddled. Signs to /clear and start over:
- Claude is "fighting" your corrections, reverting to the same approach
- The conversation has 15+ back-and-forth messages on one task
- You've said "no, that's not what I meant" three times
- Claude is modifying files you didn't mention
Restart with a clearer, more detailed prompt. Often, 30 seconds of better instructions beats 10 minutes of corrections.
IDE Integration: Visual Diff Review
Claude Code integrates with your IDE to show visual diffs before and after edits.
VS Code Extension
The official VS Code extension shows inline diffs with syntax highlighting:
- const user = getUser(id);
+ const user = await getUser(id);Green highlights for additions, red for deletions, side-by-side for complex changes. You can accept, reject, or manually edit the diff before finalizing.
JetBrains Plugin
The JetBrains plugin (IntelliJ, WebStorm, PyCharm, etc.) opens the IDE's native diff viewer. This gives you the full power of your IDE's diff tools—merge conflicts, line-by-line staging, and more.
Workflow:
- Claude makes an edit
- Diff viewer opens automatically
- Review changes visually
- Accept, reject, or manually adjust
- Changes apply to your working file
Even in acceptEdits mode, you can review the git diff afterward: git diff in your terminal shows all changes since the last commit.
The "Write Once, Read Many" Principle
Claude Code follows a philosophy: prefer editing existing files over creating new ones. This prevents codebase bloat and maintains consistency.
Why This Matters
Many AI coding tools generate new files by default. Ask for a "login component" and you get a brand-new file, even if you already have a perfectly good authentication component that just needs two new features.
Claude Code does the opposite:
You: "Add a 'remember me' checkbox to the login form"
Claude: [Reads src/components/LoginForm.tsx, edits it to add the checkbox]
Instead of creating LoginFormWithRememberMe.tsx, Claude enhances the existing component. This keeps your file count low, your architecture clean, and your patterns consistent.
Building on Existing Patterns
Because Claude reads before writing, it learns your coding style:
- Your naming conventions
- Your component structure
- Your error handling patterns
- Your testing approaches
The more Claude edits your existing code, the better it gets at matching your style. A 100-file codebase teaches Claude your preferences faster than 100 prompts.
| Creating New Files | Editing Existing Files |
|---|---|
| Can introduce inconsistent patterns | Maintains existing patterns |
| Increases file count and complexity | Keeps codebase lean |
| Requires manual integration with existing code | Already integrated |
| Generic, template-like code | Contextual, project-specific code |
| Useful for new features/modules | Ideal for improvements and fixes |
Exercise: Safe Editing in Practice
Let's put these techniques into practice.
Editing with Safety Guardrails
intermediate20 minSetup:
Create a simple React component file at src/components/ProductCard.tsx:
export function ProductCard({ name, price }: { name: string; price: number }) {
return (
<div className="product-card">
<h3>{name}</h3>
<p>${price}</p>
<button>Add to Cart</button>
</div>
);
}Tasks:
-
Normal Mode Edit: Ask Claude to add a quantity selector. Review the change before accepting.
-
acceptEdits Mode: Press Shift+Tab to enter acceptEdits mode. Ask Claude to add product images, descriptions, and sale pricing. All edits apply automatically.
-
Recover with Undo: Ask Claude to "undo the sale pricing feature."
-
Rewind: Press Esc+Esc or type
/rewind. Restore the code to before the image and description changes. -
Plan Mode: Press Shift+Tab twice to enter Plan Mode. Ask: "What would we need to change to support variant selection (size, color)?" Claude analyzes but doesn't modify.
-
Constrained Edit: Exit Plan Mode (Shift+Tab). Ask Claude to "Add a favorites button, but only modify ProductCard.tsx—don't create any new files."
-
Git Diff Review: Run
git diffin your terminal to see all changes. Practice reading the diff output.
Expected Outcome:
You'll have hands-on experience with all three permission modes, practiced recovering from unwanted changes, and seen how Claude respects file constraints. The git diff shows you the cumulative impact of all edits.
Key Takeaway
- Permission modes (Normal, acceptEdits, Plan) let you control Claude's access level
- Edit tool uses exact string replacement;
old_stringmust be unique - Constrain changes verbally or with permission rules to protect critical files
- Esc stops mid-action; Esc+Esc or /rewind time-travels to checkpoints
- Correct early—tight feedback loops produce better results than late corrections
- "Write Once, Read Many"—Claude prefers enhancing existing files over creating new ones
- IDE integration provides visual diffs for confident change review
What's Next
You now know how to edit code safely, recover from mistakes, and constrain Claude's modifications. In the next lesson, we'll tackle Debugging with Claude Code — how to share errors effectively using stack traces and screenshots, apply the TDD debugging loop (error, fix, verify), perform root cause analysis, and break out of unproductive fix-loops.