ktg-plugin-marketplace/plugins/config-audit/knowledge/configuration-best-practices.md

10 KiB

Configuration Best Practices

Concrete, actionable patterns. No generic advice.


CLAUDE.md

  1. Optimise for prompt-cache stability. Place stable content in the first 30 lines (cache-friendly prefix); volatile content (timestamps, dynamic counts, rolling activity logs) goes below that threshold or moves to an @import-ed file outside the cache prefix. On Opus 4.7 the dominant cost lever is cache reuse, not file length.1
  2. Use @import for specs/docs. @path/to/spec.md inlines the file at session start. Max 5 hops, but keep chains ≤ 2 hops — every @import boundary fragments the prompt-cache prefix. Keeps the main file scannable.
  3. Use HTML comments for maintainer notes. <!-- Updated 2026-01-01: reason --> is stripped before context injection — zero token cost.
  4. Put personal dev notes in CLAUDE.local.md, not CLAUDE.md. Add CLAUDE.local.md to .gitignore. Team members' sandbox URLs should never appear in git.
  5. Write ~/.claude/CLAUDE.md for preferences that apply everywhere. Communication style, preferred tools, output format — not project-specific config.
  6. Use clear markdown headers (## sections). Claude uses the structure to navigate; unstructured text is harder to follow selectively.
  7. Avoid contradicting project settings.json. CLAUDE.md is a user message; settings.json permissions take precedence. Don't document permissions in CLAUDE.md — put them in settings.json where they're enforced.

settings.json

  1. Add $schema to every settings.json. "$schema": "https://json.schemastore.org/claude-code-settings.json" enables autocomplete in VS Code and Cursor. Takes 2 seconds, saves every future edit.
  2. Use all three scopes: user, project, local. User (~/.claude/settings.json) for personal defaults. Project (.claude/settings.json) for team agreements. Local (.claude/settings.local.json) for personal project overrides.
  3. Put env vars in settings.json env block, not shell. {"env": {"NODE_ENV": "development"}} ensures they're always set in Claude sessions, regardless of how the shell was launched.
  4. Set defaultMode: "acceptEdits" for active development projects. Eliminates per-file permission prompts. Use "plan" for infrastructure repos where you want read-only analysis by default.
  5. Deny .env and secrets/ explicitly. {"permissions": {"deny": ["Read(./.env)", "Read(./secrets/**)"]}} — Claude cannot read these even if it reasons it should.
  6. Pre-allow repetitive safe commands. {"permissions": {"allow": ["Bash(npm run *)", "Bash(git status)", "Bash(git log *)"]}} — eliminates constant prompts for read-only git operations.
  7. Configure attribution for org identity. {"attribution": {"commit": "Generated with Claude Code [bot]", "pr": ""}} — keeps commit history clean and attributable.
  8. Set effortLevel per project, not per prompt. {"effortLevel": "high"} for complex codebases, "low" for simple scripts. Avoids forgetting to set it each session.

Hooks

  1. Add a Stop hook before anything else. Stop hook on session end is the most useful starting point — session summary, auto-commit prompt, notification. Many users have zero hooks; one Stop hook delivers immediate value.
  2. Use PostToolUse on Write/Edit for auto-formatting. {"PostToolUse": [{"matcher": "Write|Edit", "hooks": [{"type": "command", "command": "prettier --write ${CLAUDE_TOOL_OUTPUT_PATH}"}]}]} — eliminates manual format steps.
  3. Use PreToolUse on Bash for security. Validate shell commands before execution. Exit code 2 blocks the tool call with an error message shown to Claude.
  4. Use SessionStart for context injection. Inject git branch name, active Linear issue, or CI status into context at session start. Cheaper than asking Claude to fetch it.
  5. Add Notification hook for desktop alerts. When Claude needs input (permission prompt, idle), get a system notification. Without this, long sessions require constant manual checking.
  6. Match MCP tools precisely. "mcp__.*__write.*" matches all write tools from all MCP servers. "mcp__filesystem__.*" matches all filesystem tools. Use patterns, not exact names.
  7. Keep hook scripts fast (< 2s for PreToolUse). Blocking hooks run synchronously. Slow PreToolUse hooks add latency to every tool call. Use async for logging/reporting.
  8. Use ${CLAUDE_PLUGIN_ROOT} for paths in plugin hooks. Absolute paths break when plugins move. ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/check.sh is portable.

Rules (.claude/rules/)

  1. Use paths: frontmatter on every rules file. Rules without paths: load for every file. A TypeScript rules file with paths: ["**/*.ts", "**/*.tsx"] only loads for TypeScript work — zero overhead otherwise.
  2. One rules file per domain or language. typescript.md, python.md, testing.md, migrations.md — not one big coding-rules.md. Granular files = granular loading.
  3. Put project rules in .claude/rules/, user rules in ~/.claude/rules/. Project rules are team-specific and committed; user rules are personal preferences across all projects.
  4. Symlink shared rule sets. If multiple projects share rules, symlink: ln -s ../../shared/rules/security.md .claude/rules/security.md. Claude follows symlinks.
  5. Test path globs before committing. paths: ["src/**"] doesn't match ./src/file.ts — leading ./ matters. Test with the actual file paths Claude will encounter.

MCP

  1. Commit .mcp.json to git. Team-shared MCP servers belong in .mcp.json at project root, not in individual ~/.claude.json files. One commit, everyone gets the servers.
  2. Set enableAllProjectMcpServers: true in project settings.json for zero-friction team onboarding. New team members don't have to manually approve each server.
  3. Set trust levels explicitly. "trust": "workspace" for project-specific servers; "trust": "trusted" only for servers you fully control. Default is untrusted (sandboxed).
  4. Use @server:resource/path for dynamic data. @github:repos/owner/repo/issues pulls live data into context. More reliable than asking Claude to fetch and parse.
  5. Deny MCP tools you don't want Claude to invoke. {"permissions": {"deny": ["mcp__filesystem__write_file"]}} — even with a server connected, specific tools can be blocked.

Skills

  1. Add description to every skill. Without description, Claude never auto-invokes the skill. The description is the trigger — be specific about when to use it.
  2. Set disable-model-invocation: true on deploy/delete skills. Side-effect commands should only run when the user explicitly types /deploy, not when Claude decides it's appropriate.
  3. Use !git diff HEAD`` for dynamic context. Dynamic shell execution inlines current state at invocation time. Better than hardcoded file references that go stale.
  4. Use context: fork with a custom agent for isolated research. Forks run in a separate context (and optionally a separate model), keeping research overhead out of the main session.
  5. Add argument-hint to all parameterized skills. argument-hint: "[issue-number]" shows in the / menu autocomplete. Without it, users forget the expected argument format.
  6. Store large reference docs in skill subdirectory, not SKILL.md. SKILL.md describes when to load each reference file. The references themselves stay separate so they're only loaded when needed.

Agents

  1. Restrict tools to the minimum needed. A read-only research agent should have tools: ["Read", "Glob", "Grep"], not all tools. Scoped agents are safer and faster.
  2. Match model to task complexity. Haiku for file discovery and scanning; Sonnet for implementation; Opus for architecture and analysis. Don't use Opus for tasks that are primarily file reading.
  3. Set maxTurns on autonomous agents. Without a turn limit, a misconfigured agent can run indefinitely. maxTurns: 20 is a reasonable default for most tasks.
  4. Write description as a trigger condition, not a title. "Use when analyzing TypeScript files for type errors" beats "TypeScript analyzer". Claude uses the description to decide delegation.
  5. Use isolation: worktree for agents that make file changes. Agents running in their own worktree can't interfere with the main session. Changes are reviewable before merge.
  6. Enable memory: "user" for domain-expert agents. A security-reviewer agent that accumulates codebase knowledge across sessions gets better over time. Add memory: "user" to the frontmatter.

Permissions

  1. Start with defaultMode: "acceptEdits" for most projects. Then add specific deny rules for sensitive paths. More productive than prompting for every file write.
  2. Block secrets files by pattern, not by name. "deny": ["Read(./.env*)", "Read(./**/secrets/**)", "Read(./**/*.pem)"] — catch all variants, not just .env.
  3. Use additionalDirectories for cross-repo work. If Claude regularly reads ../shared-lib/, add it: {"additionalDirectories": ["../shared-lib/"]}. Otherwise Claude can't access it without prompts.
  4. Configure autoMode.environment before using auto mode. Without it, Claude's background safety classifier triggers false positives on your org's internal tool names and domains.
  5. Add Agent() deny rules for sensitive agents. {"deny": ["Agent(general-purpose)"]} prevents the most powerful agent from running without explicit permission.


  1. The "keep CLAUDE.md under 200 lines" threshold was a Sonnet-era adherence heuristic — Sonnet's attention quality dropped on longer files, so trimming raw line count was the optimisation lever. Opus 4.7 uses prompt-cache structure as the dominant cost driver: the first 30 lines must stay byte-stable across turns to keep the cache hit, and @import boundaries fragment the cached prefix. A 400-line CLAUDE.md with stable structure outperforms a 150-line file whose top contains a daily-rolling activity log. See knowledge/opus-4.7-patterns.md for detection IDs (CA-TOK-001..003). ↩︎