Thanks for reading Muhammad's Substack! Subscribe for free to receive new posts and support my work.
“Programming via LLM agents is increasingly becoming a default workflow for professionals, except with more oversight and scrutiny. The goal is to claim the leverage from the use of agents but without any compromise on the quality of the software.”
He calls this evolution agentic engineering — “agentic” because you’re orchestrating agents who write the code, “engineering” because there’s an art and science to doing it well.
That framing clicked for me. I’d been vibe coding for weeks — prompt, let it go, fix errors, repeat. Fast and fun. But the output was verbose, over-engineered, and drifted from what I actually wanted. Code reviews were painful.
The discipline Karpathy describes is real. There *is* an art to this. And after hundreds of sessions building a production Python service, I’ve found patterns that actually work.
I work primarily in Python — FastAPI, Pydantic, pytest — so the examples lean that way. But the principles apply to any language.
Here’s what I learned.
The Tools
Three tools, each with different strengths:
• Cursor — Long feature work in the IDE, multi-file changes
• Claude Code — Surgical edits, architecture, git operations
You don’t need all three. Start with one. The principles work regardless of tool.
But having tools isn’t enough. What matters is how you use them — and the single biggest change to my workflow came from one counterintuitive insight
The Core Insight: Plan Before Code
Most developers prompt, watch the agent code, fix errors, repeat. The output is verbose, drifts from what you wanted, and over-engineers everything.
The fix? Never let the agent write code until you’ve approved a written plan.
Set your agent to plan mode by default. For anything non-trivial (three or more steps, architectural decisions), force it to describe the approach first. In Claude Code, that’s “defaultMode”: “plan” in settings. In Cursor, start with “let’s plan...” or create a plan doc first.
Plan-mode sessions had fewer corrections, less scope creep, and cleaner output. It’s counterintuitive — feels slower at first — but it’s not.
4. Unnecessary complexity: “why do we need this helper?”
5. Missing features: “you removed the token option” — agents drop things during refactoring
Pushing back fixes one problem. But there’s another issue that shows up in every session: the quality of the code itself.
Fighting AI Slop
“Slop” is the verbose, defensive code AI produces by default. You’ll recognize it:
• Comments a human wouldn’t write
• Try/catch blocks on trusted code paths
• Type casts to Any to bypass issues
• Helper methods called once
• Error messages that restate the code
Example:
# Bad: AI Slop
def get_user(user_id: str) -> Optional[User]:
“”“
Retrieves a user by their unique identifier.
Args:
user_id: The unique identifier of the user to retrieve.
Returns:
The User object if found, None otherwise.
“”“
try:
result = db.query(User).filter(User.id == user_id).first()
if result is not None:
return result
else:
return None
except Exception as e:
logger.error(f”Error fetching user: {e}”)
return None
# After de-slop
def get_user(user_id: str) -> User | None:
return db.query(User).filter(User.id == user_id).first()
Same Behaviours, much less code.
The De-Slop Command
Run this before every commit:
Check the diff against main and remove AI-generated slop:
- Extra comments a human wouldn’t add
- Extra defensive checks abnormal for that area
- Casts to Any to work around types
- Style inconsistent with the file
Report with 1-3 sentence summary.
Quality Gates
Before any commit:
1. Run tests
2. Lint check
3. Type check
4. Anti-slop pass
5. Ask: “Would a senior engineer approve this?”
Sneaky Quokka Detection
Watch for these — agents will try to make tests pass without fixing the bug:
• Loosening assertions — wider tolerances instead of fixing root cause
• Skipping failures — filtering out failing cases
• Broadening types — Any to silence errors
• Swallowing exceptions — try/except to hide problems
If you see any of these, stop. Ask “what is actually wrong?” and insist on a real fix.
Once you’ve got quality under control, the next question is scale. How do you tackle bigger problems without losing focus?
Subagents: Throw More Compute at It
The idea is simple: delegate to specialized sub-processes.
When to use them:
• Parallel code exploration
• Bug investigation
• Code quality passes
• Research tasks
How to request:
“launch subagents to explore these modules”
“use subagents for exploration, keep main context clean”
“split this into 4 subagents”
One task per subagent. Keep main context clean.
Context Management
Start fresh between tasks. In Claude Code, /clear gets used almost every session. Don’t let stale context from a previous task influence the current one.
When to clear vs compact:
• /clear: Between unrelated tasks
• /compact: During a long session when you need to preserve state
Even with all these patterns, I still messed up. A lot. Here’s what tripped me up — and the fixes that worked.
Common Mistakes
Real corrections from my sessions. I made all of these.
Mistake 1: Letting the Agent Over-Scope
You ask to fix a bug, the agent refactors the module.
Fix: Be explicit. “Fix ONLY the null check on line 47. Don’t touch anything else.”
Mistake 2: Not Checking Project Conventions
Agent uses uv when project uses pip. Uses Python 3.13 when project requires 3.12.
Fix: Put build instructions in your CLAUDE.md/AGENTS.md. Reference Makefile and README.
Mistake 3: Accepting the First Explanation
Agent says “X doesn’t support Y” and you believe it.
Fix: “Is that true? Prove it with code.” Check actual source.
Mistake 4: No Cleanup Pass
You commit AI-generated code directly.
Fix: Always run simplifier + de-slop before committing.
Mistake 5: Not Inspecting Raw Output
Agent says “test passed” but you didn’t see it.
Fix: “Show me the API response JSON.” Point agent at terminal output.
Mistake 6: Giant Sessions Without Resets
After 200+ turns, agent starts hallucinating.
Fix: /clear between tasks. Start new sessions for new tasks.
The Rules That Made the Biggest Difference
Behavioral Guidelines
1. Plan Before Coding — enter plan mode for non-trivial tasks
2. Simplicity First — no features beyond what was asked
3. Surgical Changes — don’t improve adjacent code
4. Verify Before Done — transform tasks into verifiable goals
5. Root Cause Over Symptoms — what breaks if we revert in 6 months?
For Python Projects
• Types: strict and everywhere
• Validate at boundary, raise on invalid input
• No broad except Exception — one statement under try
• Return values over mutating state
• pytest.mark.parametrize across input ranges
The Zen of Python (As Agent Instruction)
Including this in agent instructions noticeably improves output:
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Errors should never pass silently.
What Actually Matters
Strip away everything else, and it comes down to this:
You are the architect. The agent is the builder.
The agent can write code faster than you. It can explore codebases, run tests, draft PRs. But it doesn’t know what should be built. It doesn’t know your constraints, your users, your tradeoffs. It will confidently build the wrong thing if you let it.
Your job isn’t to write code anymore. Your job is to:
• Direct — be clear about what you want and what you don’t
• Verify — see the actual output, not the agent’s summary of it
• Refine — clean the slop, tighten the scope, simplify
The leverage is real. But it’s not automatic. You have to shape the output.
One Last Thing
Everything here will be outdated in six months. Maybe sooner.
The models improve. The tools evolve. What works today might be unnecessary tomorrow. A year ago, plan mode felt essential — maybe next year the agents will be good enough that it won’t be.
So treat this as a starting point, not gospel. Experiment. Find what works for your codebase, your style, your problems. The developers who’ll thrive with these tools aren’t the ones who memorize workflows — they’re the ones who keep learning, keep testing new approaches, and adapt as the landscape shifts.
The only constant: you still have to think. The agents are getting better at writing code. They’re not getting better at knowing what code to write. That’s still your job.
Stay curious. Keep experimenting.
Appendix:
• CLAUDE.md / AGENTS.md template:
CLAUDE.md Template
A template for project-level AI agent instructions. Put this file in your repo root.
# CLAUDE.md## Project Overview[1-2 sentences: what the project does, tech stack]## Architecture[Directory tree with purpose of each key directory]## Commands```bash# Build
[exact build command]
# Test
[exact test command]
# Lint
[exact lint command]
# Run locally
[exact run command]
Key Patterns
[List 5-7 architectural patterns the agent must follow]
External Dependencies
[Services, APIs, databases with connection details]
Environment
[Environment variables, config files]
Behavioral Guidelines
1. Plan Before Coding
Enter plan mode for non-trivial tasks (3+ steps, architectural decisions)
State assumptions explicitly. If uncertain, ask.
Before writing code: describe approach, wait for approval
If something goes sideways, stop and re-plan
2. Simplicity & Elegance
No features beyond what was asked
No abstractions for single-use code
No speculative "flexibility" or "configurability"
200 lines when 50 would do? Rewrite
Keep files under ~500 LOC
3. Surgical Changes
Don't "improve" adjacent code, comments, or formatting
Match existing style even if you'd do it differently
Remove only what YOUR changes made unused
If task requires >3 files: stop, break into smaller tasks
4. Verify Before Done
Transform tasks into verifiable goals:
"Add validation" → Write tests for invalid inputs, make them pass
"Fix the bug" → Write reproducing test, fix until it passes
Before handoff: run full gate (lint, type check, test)
5. Bug Handling
Write a test that reproduces the bug
Fix until the test passes
Ask: symptom or root cause?
Code Style
Zen of Python (if Python project)
No broad except Exception
No unnecessary comments
Replace hardcoded values with named constants
Git
Branch prefixes: feature/, fix/, chore/
Commit format: TICKET-XXX:
Never commit without explicit approval
Never push unless asked
---
## Usage
1. Copy this template to your project root as `CLAUDE.md`
2. Fill in the project-specific sections
3. Customize behavioral guidelines as needed
4. The agent reads this file automatically
## Why This Works
AI agents are powerful but unconstrained by default. This file:
- Sets expectations for code style and quality
- Prevents scope creep with explicit guidelines
- Establishes verification requirements
- Creates consistency across sessions
Custom commands to improve AI agent output. Use in Cursor (~/.cursor/commands/) or adapt for Claude Code.
/code-simplifier
Purpose: Clean up code without changing behavior.
---name: code-simplifierdescription: Simplifies and refines code for clarity, consistency, andmaintainability while preserving all functionality.---
You are an expert code simplification specialist. Analyze recently modified
code and apply refinements that:
1.**Preserve Functionality** — never change what the code does
2.**Apply Project Standards** — follow CLAUDE.md conventions
3.**Enhance Clarity** — reduce nesting, eliminate redundancy, improve naming
4.**Maintain Balance** — avoid over-simplification
5.**Focus Scope** — only refine recently modified code unless told otherwise
Report changes with a brief summary.
/deslop
Purpose: Remove AI-generated cruft from your branch.
Check the diff against main and remove all AI-generated slop:
- Extra comments a human wouldn't add
- Extra defensive checks abnormal for that area
- Casts to Any to work around type issues
- Style inconsistent with the file
Report with 1-3 sentence summary of what you changed.
/commit-code
Purpose: Structured commit following project conventions.
1. Run `git log --oneline -10` to learn commit message style
2. Run `git status` and `git diff` to understand changes
3. Draft single-line commit message with ticket prefix (e.g., TICKET-XXX:)
4. Ask for explicit approval before committing
5. Stage relevant files, commit, show status
Rules:
- Never commit without approval
- Never amend unless asked
- Never push unless asked
/bug-fix-review
Purpose: Force root-cause thinking on every fix.
Review this fix and answer:
1. Are we solving the SYMPTOM or the ROOT CAUSE?
2. What breaks if someone reverts this fix in 6 months?
3. Is there a deeper architectural issue being papered over?
If this is a band-aid fix, propose the proper solution even if it's more work.
Review this PR with the following approach:
1. Ask: Is this my PR (for understanding) or someone else's (for feedback)?
2. Order files for comprehension: schemas → constants → core logic → helpers → tests → docs
3. For each file:
- Show the diff
- Explain "What Changed" and "Why This Matters"
- Call out edge cases, race conditions, missing guards
- Pause for questions before moving to next file
4. If reviewing someone else's PR, draft comments in my voice
5. Summarize overall assessment at the end
/explore-repo
Purpose: Prime the agent with full codebase context.
Before doing any work, explore this codebase:
1. Run `git ls-files` to see all tracked files
2. Read the directory structure
3. Read CLAUDE.md, README.md, and any docs/
4. Identify entry points (main files, app files)
5. Check config files (package.json, pyproject.toml, Makefile)
6. Note recent git log for context on current work
Summarize your understanding before proceeding.
/humanize
Purpose: Rewrite text to sound human.
Rewrite this text to sound like a human wrote it:
1. Replace flagged words: delve, robust, leverage, seamless, utilize, facilitate
2. Vary sentence lengths (add short punchy sentences, break up long ones)
3. Add contractions (you're, it's, don't)
4. Break perfect parallel structures
5. Add specific examples instead of generic statements
6. Remove generic openings ("In today's world...")
The goal: text that passes AI detection and reads naturally.
Installation
Cursor
Create ~/.cursor/commands/ directory
Save each command as a separate .md file (e.g., deslop.md)
Access via /command-name in chat
Claude Code
Add to your CLAUDE.md or create custom skills
Reference in prompts or set up as slash commands via plugins
I love the /deslop and definition under it, definitely stealing that one :D
Solid read, will feed this in its entirety to my cc and ask it to create a claude.md and check out what it looks like 😂 should be good