</>CodeWithKarani

Agentic Coding as an Engineering Discipline: My Daily Claude Code Workflow

Karani GeoffreyKarani Geoffrey9 min read

Autocomplete guesses the next line. An agentic coding tool reads your repository, plans a change, edits several files, runs your test suite, reads the failure, and tries again - possibly for twenty minutes while you are in a meeting. Those are different tools requiring different habits, and most of the disappointment I hear about agentic coding comes from people using the second one with the habits of the first.

This is the workflow I actually use, in a working directory, on real client code.

The one constraint everything else follows from

The context window holds the entire conversation: every message, every file read, every command output. It fills faster than you expect - one debugging session can burn tens of thousands of tokens - and model performance degrades as it fills. Instructions from twenty minutes ago start getting ignored.

Almost every practice below exists to protect that budget. Once you internalise "context is the scarce resource", the rest stops feeling like arbitrary ceremony.

Scaffolding the repo, once

CLAUDE.md

Claude Code reads ./CLAUDE.md at the start of every session. Run /init to generate a first draft from your project structure, then cut it in half. Include only what cannot be inferred from the code:

cat > CLAUDE.md <<'EOF'
# Commands
- Dev server: `pnpm dev` (never `npm`, this repo uses pnpm workspaces)
- Tests: `pnpm test -- <path>` - run single files, the full suite takes 9 minutes
- Migrations: `pnpm db:migrate`. NEVER edit files in migrations/ that are already applied.

# Conventions
- Money is stored in integer cents. Never use floats for currency.
- All API handlers return the envelope in src/lib/response.ts. No bare json().
- Timezone is Africa/Nairobi; store UTC, format at the edge.

# Gotchas
- The staging DB is a read replica. Writes fail silently with a 200.
- `src/legacy/` is frozen. Report problems there, do not fix them.
EOF

The test for each line is: would removing this cause a mistake? If not, delete it. A bloated CLAUDE.md is worse than a short one, because the important rules get lost in the noise and start getting ignored. If Claude keeps doing something you have a rule against, the file is probably too long. Check it into git so the team contributes; it compounds in value.

Permissions and hooks

By default you approve every write and every command, which is safe and exhausting. After the tenth approval you are not reviewing, you are clicking. Allowlist the boring things in .claude/settings.json, and use hooks for anything that must happen every single time:

{
  "permissions": {
    "allow": [
      "Bash(pnpm test:*)",
      "Bash(pnpm lint:*)",
      "Bash(git status)",
      "Bash(git diff:*)",
      "Read"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Bash(git push --force:*)",
      "Read(./.env)",
      "Read(./.env.*)"
    ]
  },
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{ "type": "command", "command": "pnpm prettier --write \"$CLAUDE_FILE_PATHS\"" }]
      }
    ]
  }
}

The distinction that matters: CLAUDE.md is advisory, hooks are deterministic. "Always run the formatter" belongs in a hook. "Prefer named exports" belongs in CLAUDE.md.

Subagents and skills

Subagents run in their own context window and report back a summary, which is how you buy investigation without paying for it in your main conversation. Define them in .claude/agents/:

---
name: security-reviewer
description: Reviews a diff for security vulnerabilities
tools: Read, Grep, Glob, Bash
model: opus
---
You are a senior application security engineer reviewing a diff.
Check for: injection (SQL, command, template), missing authorisation on
handlers, secrets committed to source, unsafe deserialisation, and any
model output that reaches a shell, a query or raw HTML.
Report file and line for each finding with a concrete fix.
Flag only issues affecting correctness or security. Ignore style.

Skills go in .claude/skills/<name>/SKILL.md and are loaded on demand rather than every session, which makes them the right home for domain knowledge that only matters sometimes - your deploy runbook, your API conventions, the shape of your invoice schema.

The daily loop

1. Explore, plan, implement, commit

Start in plan mode. Claude reads and answers without touching anything:

claude --permission-mode plan

Then, in that session: "read src/billing and explain how we currently handle partial payments and reconciliation against M-Pesa callbacks." Once you are satisfied it understands, ask for a plan. Read the plan properly - this is the cheapest place to catch a wrong approach. Press Ctrl+G to open it in your editor and edit it directly. Then switch out of plan mode and let it implement.

Skip planning when you could describe the diff in one sentence. Use it when the change spans files, or you are unfamiliar with the code, or you are not sure of the approach. Planning has overhead; spend it where it pays.

2. Give it something to verify against

This is the highest-leverage habit in the entire workflow. Claude stops when the work looks done. Without a check it can run, "looks done" is the only available signal and you become the verification loop. Give it a test suite, a build exit code, a linter, a script that diffs output against a fixture, or a screenshot to compare, and the loop closes on its own.

Compare these two prompts:

  • "implement a function that validates KRA PINs"
  • "write validateKraPin. Cases: A012345678Z is valid, A12345678Z is invalid (wrong length), 0012345678Z is invalid (must start with a letter). Write the tests first, run them, iterate until green."

The second one you can walk away from. Ask for evidence rather than assertion - the test output, the command it ran and what it returned. Reading evidence is faster than re-running the check yourself, and it works for sessions you were not watching.

3. Delegate investigation to subagents

When Claude researches a codebase it reads many files, and every one of them lands in your context. Push that into a subagent:

"Use subagents to investigate how token refresh works in our auth system and whether there are existing OAuth helpers I should reuse."

The subagent burns its own context and hands back a summary. Your main conversation stays clean for the implementation, which is the part where you need the model sharp.

4. Manage context aggressively

  • /clear between unrelated tasks. Not optional. The "kitchen sink session" - one task, then an unrelated question, then back to the first task - is the most common way people degrade their own results.
  • If you have corrected the same mistake twice, stop. The context is now full of failed approaches that are actively steering the model wrong. /clear and write a better opening prompt incorporating what you learned. A clean session with a good prompt beats a long session with accumulated corrections, essentially always.
  • /compact Focus on the API changes when you need to keep going but the history is bloated.
  • Double-tap Escape or /rewind to restore conversation, code, or both to an earlier checkpoint. Checkpoints only track edits made through Claude's file tools, not Bash side effects, so they complement git rather than replace it.

5. Adversarial review before you call it done

The model that wrote the code is the worst reviewer of it. Spawn a fresh context that sees only the diff:

"Use a subagent to review this diff against PLAN.md. Check every requirement is implemented, the listed edge cases have tests, and nothing outside scope changed. Report gaps, not style preferences."

One caution: a reviewer asked to find gaps will find gaps, whether or not they matter, because that is the task you gave it. Chasing all of them leads to defensive code, unnecessary abstraction and tests for conditions that cannot occur. Tell it to flag only what affects correctness or the stated requirements.

Scaling out

Non-interactive mode is where this stops being a chat tool and becomes infrastructure:

# Structured output you can pipe
claude -p "list every API endpoint and its auth requirement" --output-format json

# Scoped, budgeted, bounded - the shape for anything unattended
claude -p "migrate $FILE from moment to date-fns. Reply OK or FAIL." \
  --allowedTools "Edit,Bash(pnpm test:*)" \
  --max-turns 8 \
  --max-budget-usd 0.75

# Fan out across a migration
for f in $(cat files.txt); do
  claude -p "Migrate $f. Run its tests. Reply OK or FAIL." \
    --allowedTools "Edit,Bash(pnpm test:*)" --max-turns 8
done

# In CI
git diff origin/main --name-only | claude -p "review these files for security issues"

Refine the prompt on two or three files before you run it over two thousand. The --allowedTools, --max-turns and --max-budget-usd flags are what make an unattended run safe to leave alone.

For parallel interactive work, git worktrees keep sessions from colliding on the same files. A writer/reviewer split across two sessions works well: one implements, the other reviews with fresh context, and you carry the feedback across.

Failure patterns, and what they look like

  • The kitchen sink session. Context full of three unrelated tasks. Fix: /clear.
  • Correcting in circles. Two failed corrections means the context is poisoned. Fix: /clear and a better prompt.
  • The over-specified CLAUDE.md. Rules getting ignored because there are too many. Fix: prune ruthlessly, or convert the rule to a hook.
  • Trust without verification. Plausible code that fails on the edge case. Fix: if you cannot verify it, do not ship it.
  • Infinite exploration. "Investigate the codebase" with no scope, hundreds of files read. Fix: scope it, or send a subagent.

Notes for those of us on the wrong side of the latency map

Two practical things. First, this is a text protocol - it streams tokens, not video - so it is far kinder to a metered or intermittent connection than most cloud IDEs. Sessions persist locally, so claude --continue picks up exactly where a dropped link left you. Second, cost control is real engineering: prompt caching is applied automatically to the stable prefix, but /clear between tasks, subagents for exploration, and choosing a smaller model or a lower --effort for routine work all move the number meaningfully. Set --max-budget-usd on anything unattended.

What it is still bad at

Ambiguous requirements, where it will confidently build the wrong thing rather than ask. Large architectural decisions with tradeoffs it cannot observe from the code. Anything where the verification signal is a human judgement call. And it consistently over-engineers when unconstrained: extra abstraction layers, defensive handling for impossible states, helpers nobody needed.

Which is the correct summary of the whole discipline. The model does the typing. You still own the specification, the verification and the judgement - and the workflow above is mostly a set of habits for making sure you keep owning them.

#Claude Code#Agentic Coding#Developer Workflow#AI Engineering#Productivity
Keep reading

Related articles