DEV Community

Lawrence Cooke
Lawrence Cooke

Posted on

Setting up Claude Code for success

When first starting a new project using Claude Code, it is easy to jump ahead, diving straight into coding. However, if you spend a bit of time setting up Claude Code, the outcome will be a smoother and more enjoyable development experience.

Generating a CLAUDE.md file

When first starting your project, spend time talking through the requirements of the project with Claude. Take into account your tech stack, What language are you writing it in? Are you using a framework?

You should also not only discuss the tech stack, but also how you want this built. How do you want the folder structure to be laid out? How do you want to interact with the database?

Here is a general list of things you might include:

  • Programming Language (and version)
  • Framework and routing approach
  • Dependency injection patterns
  • Database access layer (raw queries, query builder, ORM)
  • Coding standard
  • Folder and namespace conventions
  • Error handling approach
  • Unit Testing
  • Third party packages you might include.

The goal of this conversation is to produce a CLAUDE.md file — a markdown document that lives in your project root, that is automatically loaded into Claude's context at the start of every session. It's the source of truth that means you never have to re-explain your stack again.

What a CLAUDE.md looks like

Here’s an example CLAUDE.md file

# Project Blueprint

## Stack
- Language: PHP 8.3
- Framework: FlightPHP (micro-framework)
- Database: Postgres 18
- Coding standard: PSR-12

## Database Access
Use Flight PHP build in PDO wrapper with prepared statements exclusively.
All queries live inside the business logic — never in controllers.

## Folder Structure
app/
  Controllers/ # Thin controllers, Only reference Logic classes and UI
  Logic/       # Business logic
  config/      # Config files — DO NOT READ OR EDIT

## Code Style
Run PSR-12 checks via: vendor/bin/phpcs --standard=PSR12 app/
Auto-fix via:          vendor/bin/phpcbf --standard=PSR12 app/

## What NOT to do
- Never use static methods on service classes
- Never put SQL in controllers
- Never read from app/config/config.php
- Never commit directly — all git operations are manual
Enter fullscreen mode Exit fullscreen mode

Once this file exists, every new Claude Code session starts with this context. Claude knows your conventions without being told.

The CLAUDE.md is a time saver when coding across multiple sessions. It also allows more frequent context clearing, which can help keep Claude Code focused on the current ask, lowering costs, and save time not having to explain design choices every session.

Items can also be added to the file as you come across issues and technical asks that may not have been in the CLAUDE.md file initially, building up a good repository of information that Claude can use to help build the application to your specifications.

We all have different ways of coding, and teaching Claude how you like to code through the CLAUDE.md file, will result in code similar to how you code yourself, which helps when reviewing the code, as it will seem familiar.

Guardrails

Claude Code is powerful, which is why it needs guardrails. Left unconstrained, an AI can read your environment files, touch your git history, run database commands, or make network requests you didn’t intend. None of that maliciously, just helpfully, and that’s the problem.

The permissions system in Claude Code lets you define exactly what it can and cannot do, locked into your project’s .claude/settings.json. The deny list is your non-negotiable safety layer.

There are three settings files:

  • managed-settings.json
  • settings.local.json
  • settings.json

These are hierarchical.

Managed-settings.json is the top tier, It lives outside the repo.

OS Path
macOS /Library/Application Support/ClaudeCode/managed-settings.json
Linux /etc/claude-code/managed-settings.json
Windows C:\ProgramData\ClaudeCode\managed-settings.json

Instructions in the managed-settings.json cannot be overridden by instructions in the repo level json files.

  • settings.json should be committed to your repository.
  • settings.local.json should not be committed to the repository.
  • settings.local.json overrides settings.json.

The difference between these is that settings.json is shared across developers in a multi developer setup, while settings.local.json is intended for individual developer instructions.

In a business setting, putting the most critical instructions in the managed-settings.json, and limiting the use of settings.json & settings.local.json, sets the system for success. While as an individual developer, just using the settings.local.json file might be sufficient.

What belongs on the deny list

Environment files & secrets
Your .env, .env.*, certificates (.pem, .key, .p12), and SSH/AWS credential folders should be completely off-limits. Claude has no reason to read them. With config files, creating a sample config with no secret keys set is useful for giving Claude access to the config structure without giving access to the keys.

Destructive git operations
Block git commit, git push, git merge, git reset, git clean, and anything else that writes to your history. You own the git history, not Claude. Every commit should be decided on and controlled by a person. This allows for code reviewing prior to committing the code.

Direct database access
No mysql, psql, pg_dump, mysqldump, or any other direct database CLI commands. Claude should generate migration files and queries in code, not run commands directly against your data.

Network and remote access
Block curl, wget, ssh, scp, rsync, and similar tools. Claude should fetch docs through approved WebFetch domains, not make arbitrary outbound calls.

System-level commands
rm, sudo, chmod, chown, kill, crontab — anything that can damage your system or escalate privileges.

The full settings file

The allow list is just as important as the deny list. It gives explicit permission for the tools you do want Claude to be able to run. Fetching framework docs, requiring Composer packages, running your linter etc.

Special Note

Within a settings file, DENY instructions override ALLOW instructions, however when there is a hierarchy of settings files, the higher priority files instructions override the instructions from lower priority files. If something is denied in managed-settings.json, adding an ALLOW in settings.json will not override the denial in managed-setting.json

{
  "permissions": {
    "allow": [
      "Bash(composer require:*)",
      "Bash(vendor/bin/phpcbf --standard=PSR12 app/)",
      "Bash(vendor/bin/phpcs --standard=PSR12 app/)"
    ],
    "deny": [
      "Read(app/config/config.php)",
      "Edit(app/config/config.php)",
      "Read(.env)",
      "Edit(.env)",
      "Read(**/*.pem)",
      "Read(**/*.key)",
      "Read(**/*.p12)",
      "Read(**/*.pfx)",
      "Read(~/.ssh/**)",
      "Read(~/.aws/**)",
      "Read(./.git/**)",
      "Bash(git commit:*)",
      "Bash(git push:*)",
      "Bash(git merge:*)",
      "Bash(git rebase:*)",
      "Bash(git reset:*)",
      "Bash(git clean:*)",
      "Bash(git branch -d:*)",
      "Bash(git branch -D:*)",
      "Bash(git tag -d:*)",
      "Bash(git stash drop:*)",
      "Bash(git stash clear:*)",
      "Bash(git remote add:*)",
      "Bash(git remote remove:*)",
      "Bash(git remote set-url:*)",
      "Bash(git config --global:*)",
      "Bash(rm:*)",
      "Bash(rmdir:*)",
      "Bash(shred:*)",
      "Bash(dd:*)",
      "Bash(mkfs:*)",
      "Bash(fdisk:*)",
      "Bash(curl:*)",
      "Bash(wget:*)",
      "Bash(nc:*)",
      "Bash(netcat:*)",
      "Bash(nmap:*)",
      "Bash(telnet:*)",
      "Bash(ftp:*)",
      "Bash(sftp:*)",
      "Bash(scp:*)",
      "Bash(rsync:*)",
      "Bash(ssh:*)",
      "Bash(mysql:*)",
      "Bash(mysqldump:*)",
      "Bash(mysqlimport:*)",
      "Bash(mariadb:*)",
      "Bash(mariadb-dump:*)",
      "Bash(psql:*)",
      "Bash(pg_dump:*)",
      "Bash(pg_restore:*)",
      "Bash(pg_dumpall:*)",
      "Bash(redis-cli:*)",
      "Bash(sudo:*)",
      "Bash(su:*)",
      "Bash(chmod:*)",
      "Bash(chown:*)",
      "Bash(passwd:*)",
      "Bash(useradd:*)",
      "Bash(usermod:*)",
      "Bash(crontab:*)",
      "Bash(kill:*)",
      "Bash(pkill:*)"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

The pattern to remember: tight allow, broad deny. Explicitly permit the specific tools Claude needs; broadly block everything that could cause harm if run without your supervision.

Custom Commands

Custom commands are saved prompts you can used during any session to run a repeatable, structured action across your codebase.

The most valuable commands aren’t about generating code, they’re about reviewing what’s been built. Running a security audit through a project catches problems before they’re buried under layers of new code.

Custom commands live in .claude/commands/ as markdown files. Each file is a detailed prompt that Claude executes when you invoke the slash command.

Claude Code itself can help you draft these command files efficiently.

Commands worth building

/security-audit
Reviews all files in the application for SQL injection risks, unvalidated input, missing authentication checks, insecure direct object references, and exposed error messages. Outputs a prioritised list of findings with file and line references.

/architecture-review
Checks that the business rules are being followed.

/update-claude
"Review what we have just discussed and update CLAUDE.md with any new architectural patterns or 'What not to do' rules we have discovered".

Example: the security audit command

Perform a security audit on the PHP codebase in app/.

Check for the following issues and report each with file
path, line number, and severity (high/medium/low):

1. SQL injection risks — any string concatenation in
   queries, any unparameterised input
2. Missing input validation — user-supplied data used
   without sanitisation or type checking
3. Authentication gaps — routes or methods that should
   require auth but don't check for a session
4. IDOR risks — fetching records by ID without verifying
   the current user owns that record
5. Verbose error exposure — raw exceptions or stack
   traces that could leak system details

Output format:
[SEVERITY] File: path/to/file.php (line N)
Issue: description
Fix: recommended action

Do not fix anything — report only. Fixes happen
in a separate pass once the full list is reviewed.
Enter fullscreen mode Exit fullscreen mode

Run audits often. The best time to run a security audit is not at the end of the project , it's during the project development. Issues caught early are easy to fix. Issues caught after three months of layered code become complex.

Claude Ignore

Similar to .gitignore, there is a .claudeignore file. Entries in the .claudeignore tells Claude that the files and folders listed are not relevant.

Folders like node_modules, vendor, logs, caches etc would be good one to add to .claudeignore.

.claudeignore is not a replacement for settings.json. Claude can still read items listed in .claudeignore, You can still ask Claude to read them to gain context.

.claudeignore saves you tokens by not having Claude read the files upfront.

Context Compacting

As a Claude Code session grows longer, it will eventually compact, compressing older parts of the conversation to free up context window space. Claude does its best to preserve what matters, but compacting loses context. Architectural decisions made three hours ago, the reasoning behind a particular pattern choice, all of that is at risk of being quietly forgotten.

This is where the CLAUDE.md file helps.

Because CLAUDE.md is loaded at the start of every session, and re-read whenever Claude needs to orient itself , your core context is never actually lost to compacting. It doesn't live in the conversation history. It lives in the file.

Compacting can chew through hours of back-and-forth . Your architecture conventions are still right there, intact, waiting to be loaded again.

In long conversations, you may want to update the CLAUDE.md file with new information that came about during the conversation.

This can change how you work. Instead of using a single long session and worrying about context drift, you can:

  • Use /clear freely and often to start fresh without losing your architectural context
  • Treat each task or feature as its own clean session, describe the scope, build it, clear, repeat

Working in small, focused sessions with frequent clears is actually a healthier pattern than one long marathon session anyway. It forces you to scope tasks tightly, keeps the context window lean, and means Claude is always working with fresh, uncluttered context.

Time to code

With CLAUDE.md setup, and your system secured, you've created a safe space for Claude Code to be genuinely excellent.

This lets you focus on working through the architecture and delivering a quality product.

Setting up Claude Code isn't about restricting the AI, it's about defining the playing field so you can stop worrying about the boundaries and start focusing on the architecture.

Top comments (0)