Claude Code just rewrote a 20-line service into a factory, a registry, and three interfaces — and you accepted it before you read it. Or it’s halfway down the wrong path and you hit Ctrl+C, which kills the whole session instead of the task. The difference between a frustrating session and a fast one is not the model; it’s having a recovery routine: commit before you delegate, interrupt cleanly, and revert without ceremony.
Claude Code permissions live in a permissions object in .claude/settings.json with allow, ask, and deny arrays. Entries are tool rules (Read, Bash(git diff *)), not bare command words, and Bash specifiers use space-globs — never colons.
{
"permissions": {
"allow": [
"Read",
"Bash(git status)",
"Bash(git diff *)",
"Bash(ls *)",
"Bash(grep *)"
],
"ask": [
"Edit",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(npm install *)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force *)",
"Read(./.env)"
]
}
}
Terminal window
# --dangerously-skip-permissions removes ALL approval prompts.
# Use it only in a throwaway/sandboxed environment (a fresh container,
# a scratch clone) — never on a shared dev machine or anything near prod.
claude--dangerously-skip-permissions
# For everyday work, prefer a curated allow/ask/deny policy (left tab)
# plus "defaultMode": "acceptEdits" to cut prompts without going wide open.
# Permission Policy
## Auto-Execute (Safe)
- Read operations (ls, cat, git status)
- Non-destructive queries
- Test execution
## Manual Approval Required
- File deletions
- Database modifications
- Package installations
- Git operations affecting remote
## Never Allow
- Force push to main/master
- Recursive deletions without specific paths
- Direct production database access
- Credential modifications
Security Best Practices
Never auto-allow destructive commands
Be cautious with commands that include sensitive files
Review commands that access environment variables
Use MCP servers for sensitive operations
Maintain different policies for different environments
A concrete reason to keep git add in ask rather than allow: a blanket git add . can sweep in .env files with secrets, node_modules, build artifacts, or local notes. Prefer specific paths (git add src/) or interactive staging (git add -p), and let the ask rule force a deliberate confirmation each time.
Working with AI requires a different approach to version control. The traditional habit is: make changes, debug extensively, commit when perfect. With Claude Code, invert it — commit the current state first, let Claude attempt the change, and if the result is worse than what you had, revert and re-prompt rather than debugging the AI’s output line by line.
The mechanical loop looks like this in the terminal:
Terminal window
gitadd. && gitcommit-m"Checkpoint before Claude refactoring"
# In the REPL: "Refactor this module to use dependency injection"
# If the result is overly complex:
gitreset--hardHEAD
# In the REPL: /clear, then re-prompt with tighter constraints
Reverting is usually faster than debugging an AI mistake, produces a cleaner final implementation, and saves you from anchoring on a bad first attempt. Treat commits as checkpoints in a video game: save often so retrying a different strategy costs you one line, not an afternoon.
When Claude overcomplicates, you have two moves: ask it to collapse the abstraction in place, or revert and re-prompt with a tighter spec.
Terminal window
gitreset--hardHEAD# throw away the over-built version
# In the REPL: /clear, then re-prompt with the constrained version below
Common overengineering patterns to watch for: unnecessary abstraction layers, premature optimization, over-generalization, deep inheritance hierarchies, and design patterns applied where a function would do.
How to prevent:
# Add to CLAUDE.md
## Simplicity Principles
- PREFER simple solutions over complex ones
- AVOID premature abstraction
- USE design patterns only when clearly beneficial
Master the art of stopping Claude effectively. The key distinction:
Wrong: Ctrl+C — this exits Claude Code entirely and you lose the session.
Right: Escape — this stops the current operation but keeps the conversation, so you can immediately add context and continue.
Reach for Escape the moment you notice any of these:
Claude is heading in the wrong direction
You realize you need to provide more context
You want to add an additional instruction
The operation is taking longer than the task warrants
You spot an error in your own request
The recovery is just Escape, then keep typing. For example: Claude starts implementing, you press Escape, and you type Wait, I forgot to mention we need to maintain backward compatibility with the v1 API — Claude picks up the new constraint without restarting.
Variations worth internalizing: early Escape (stop as soon as the approach looks wrong), let it finish (sometimes it’s faster to see where it lands), Escape + /clear for a complete direction change, and Escape + refine to add constraints mid-execution.
Working effectively with Claude Code is a skill, and the adjustment is real. Most developers move through a recognizable arc:
Week 1-2: Adjustment Period
Feels awkward and slow
Tendency to over-verify everything
Unsure what’s safe to delegate
Week 3-4: Finding Rhythm
You develop trust in specific areas
You learn Claude’s strengths and failure modes
You start batching related tasks
Month 2 and beyond: Flow
A natural delegate-verify-revert rhythm develops
Previously tedious tasks become routine
You measure your own usage with /cost instead of guessing at “X times faster”
Rather than chasing a productivity multiplier, track something you can verify: how often you accept Claude’s first attempt unedited, and how often you revert. Both improve as your prompting tightens.
When a session goes sideways, you want a recovery routine, not a debugging spiral. Work through these in order.
Claude went down the wrong path and is still running. Press Escape (not Ctrl+C, which kills the session). Add the missing constraint in plain language and let it continue from there.
The result is worse than what you had. Don’t debug the AI’s output. Run git reset --hard HEAD to return to your checkpoint, then /clear and re-prompt with a tighter spec. This is why you commit before delegating.
Claude over-engineered the solution. Use the de-overengineering prompt from Tip 98 to collapse the abstraction in place, or reset and re-prompt asking for “the simplest thing that passes the tests.”
Context is polluted and answers are drifting. Run /clear to drop the conversation history before starting unrelated work. A stale context is the most common cause of off-target responses.
Costs are climbing faster than expected. Check /cost in the REPL, then route routine work to Sonnet 4.6 and reserve Opus 4.8 for genuinely hard tasks. Long, unbroken sessions are usually the culprit — /clear between tasks also trims token spend.
A permission rule isn’t taking effect. Remember rules evaluate deny, then ask, then allow, and Bash specifiers are space-globs (Bash(npm run *)), not colons. A bare command word in the old autoAllow/blocked shape is a no-op — migrate to the permissions object from Tip 96.
You’ve reached the end of the 100 tips. The throughline: speed without verification is worthless, and a tight delegate-verify-revert loop beats trusting any single output. To go deeper:
Revisit Team Collaboration to turn these habits into shared, versioned team defaults.
Keep your permission policy honest with the official settings reference.