Back to Experiments
Claude CodeWorkflow

Findings from a Month with Anthropic's Claude Code

How I structure rules files, select plugins, manage the context window, and use a brainstorm-plan-execute workflow to ship features.

·8 min read

Most are familiar with the buzzwordy concept of prompt engineering. The more modern understanding of this is context engineering. Claude Code is incredibly effective at producing code, but the quality of the output (and its adherence to engineering principles and best practices) is highly dependent on what tools you adopt, how you manage context, and the workflow you follow. This is what I've learned after a month of using it daily.

Setting up

I recommend adopting tools intentionally, gradually, and ramping up on Claude Code incrementally. The perfect setup is unattainable, or attainable only with diminishing returns. The landscape changes fast, and will keep changing. Every tool I adopt has a distinct purpose; I understand what it solves and how it fits into orchestration. Otherwise, it's an unnecessary risk, and could even impact my results negatively. I call this the Principle of Fewest Plugins.

Principle of fewest plugins

A humble set of tools will get you close to the bleeding edge; this is good enough to be crazy effective.

Significantly, I trust Anthropic to incorporate the most effective strategies the community can offer into their platform; plugins like Claude-Mem have been superseded by native functionality, and Claude Code's built-in subagent dispatch now covers ground that previously required third-party solutions.

Be Skeptical of Hype

Anthropic is incentivized to adopt the best solutions natively, and they do.

There are many self-promoters with anecdotal evidence on popular social media channels. Many are paid products or freemium. I default to skepticism.

Security

Claude Code has become a rich attack surface.

Bad actors

I do not install a tool unless I have personally reviewed the code. Use special caution with browser-connecting tools (e.g., Playwright-based MCPs); attackers have been hiding prompts in sites to exfiltrate information and install malicious software.

Sandbox your browser, and whatever you do, don't connect Claude to an instance that's logged into your bank (call me paranoid).

You can even ask Claude: "how do I set up Claude to protect myself from exploits?" The specifics will depend on your setup, but reviewing/reading extensions is one of the best ways to both harden your environment and broaden your understanding of the ecosystem.

Here's the TL;DR from Claude (detailed output omitted):

A layered security approach, as Claude recommends.

Tangentially, prompt injection attacks (and LLMs' vulnerability to them) are why I'm skeptical that browser-based LLM features like what Google is advertising will fully take off. LLMs are inherently susceptible to these attacks — search 'ChatGPT jailbreaks' for examples of how easily they can be tricked into malicious behavior. I would never let Gemini 'auto browse' in a browser instance that has sensitive sessions cached.

MCPs

The only MCP I consider essential at this point is context7. It is simply LLM-browsable API docs, and it's available on the Claude Code marketplace.

Intuitively, we expect language-specific MCP servers to be necessary to develop in that language. I haven't found this to be the case; Claude is perfectly capable of writing idiomatic TypeScript without one. YMMV — I'm still exploring it.

Even the GitHub MCP is of dubious value given the gh CLI is feature-rich, token light, and Claude uses it easily. Keeping security in mind, I skipped the GitHub MCP and recommend you do as well.

Sound notifications

For a big quality of life bump, set up sound notifications via hooks. Claude can run for minutes at a time; a distinct sound when it finishes, needs permission, or is waiting for input lets you context-switch without babysitting the terminal. On macOS, afplay and the built-in system sounds make this trivial:

"hooks": {
  "Notification": [
    {
      "matcher": "stop",
      "hooks": [{ "type": "command", "command": "afplay /System/Library/Sounds/Submarine.aiff" }]
    },
    {
      "matcher": "permission_prompt",
      "hooks": [{ "type": "command", "command": "afplay /System/Library/Sounds/Glass.aiff" }]
    }
  ]
}

I've seen some setups where people have their smart lights automated to change and flash with hooks. I might try it one day...

Managing context

Run /statusline in Claude Code to configure what Claude shows you in the CLI app. It shows your context window usage (as a percentage), cache hit rate, active model, and session cost. The key indicator is context window usage: as it climbs toward 100%, Claude loses access to earlier context. The original spec and other custom instructions begin to fall out. A high window with a dropping cache rate is your signal to wrap up: ask Claude to create a hand-off summary, then /clear and start fresh.

Defining best practices

I rely on the rules directories to codify my best practices. When starting with a new language and Claude, I aggressively source best practices from as many LLMs as I can afford (ChatGPT, Claude right now) and store them here. To minimize context clutter, I strive to keep each to a single topic, with a descriptive filename: testing.md, concurrency.md, not rules-2.md.

At minimum, I codify library/platform versions, architectural patterns (MVVM, orthogonal backends), and testing expectations at each layer. Beyond that, I add rules as I discover recurring feedback: code style, language-specific guidelines, concurrency principles.

An iOS anecdote

When I began building for iOS, Claude immediately created a Storyboard interface. I was shocked because I happened to know storyboards were replaced by SwiftUI in iOS 13.

Blame it on a lack of open source training material for iOS, but here I knew I needed a detailed set of rules to keep it aligned with modern best practices.

I aim for minimal but complete. When rules include examples, I prefer general ones over snippets from my own codebase; local code is subject to change, and stale examples cause drift and churn.

For multi-platform projects, rules can be organized into subdirectories (frontend/, backend/, ios/). Claude discovers .md files recursively.

Rules also support path-specific scoping via paths frontmatter with glob patterns. This isn't just a token optimization; it prevents cross-contamination of concerns. I don't want my iOS concurrency rules in context when Claude is editing my API layer. This is especially important in monorepos.

You can also define personal rules in ~/.claude/rules/ for coding preferences you carry across all your projects (project rules take precedence), but I recommend keeping this as light as possible. It won't scale well in a team environment.

Refining best practices

One thing that sets apart an experienced dev from a "vibecoder" is that a dev treats AI output like a colleague's work and reviews it diligently for understanding and completeness. This is often described as knowing what "good" looks like.

In this spirit, best practices are a living document. When I give PR feedback to Claude, I often ask, "how can I codify this into our rule set?" I iterate and refine, and I'm approaching a place where I'm happy with several rule sets across multiple languages.

The first lines of code in the project establish patterns. Review them especially diligently because Claude will imitate.

Keeping Claude honest

You can also re-prompt Claude to load specific context. You'll get some abashedly honest responses from asking it:

How confident are you that this matches our best practices/implements the spec?

Claude can't really be sure unless you've asked it to track tasks as part of the plan; even then, it's only checking the list it made.

Very often I find a violated best practice or two. Sometimes it says:

"it seemed acceptable not to adhere to x for reason y."

BAD CLAUDE!! Letting these things sneak through is how we get drift and LLM technical debt. LLMs follow patterns, so I don't compromise on style. I will compromise on implementation for good reasons, however, particularly YAGNI.

CLAUDE.md

Where .claude/rules/ defines coding standards, CLAUDE.md gives Claude the big picture. It's loaded into context at the start of every session. Think of it as a briefing for a senior engineer on their first day: they need to know what, not why.

Getting started

Claude Code provides an /init skill that bootstraps a top-level CLAUDE.md.

What belongs here:

  • Build, test, and lint commands, so Claude doesn't waste cycles discovering them
  • High-level architecture: the 30-second mental model of your project
  • Key conventions that aren't captured in rules files

What doesn't:

  • Anything that changes frequently (it's versioned with your code)
  • Detailed coding standards (that's what .claude/rules/ is for)

Claude reads CLAUDE.md files recursively up the directory tree and discovers them in child directories on demand. More specific instructions take precedence; the official docs detail the full hierarchy from project-level through user-level and even organization-managed policies.

Every token in CLAUDE.md eats into your context window every session, so keep it tight. If you find yourself writing paragraphs, it probably belongs in a rules file or your actual docs. And like your rules files, prompt Claude to keep it current when the project evolves.

Workflow: brainstorm, plan, execute

I use obra's superpowers. It's lean and contains my core workflow:

brainstorm → write plan → execute plan with subagents

I initiate the chain by asking, "let's brainstorm; here is what I'm building." Sometimes I have substantial spec, sometimes I need to work through the problem: Claude shines here.

The great thing about superpowers is that, once initiated, Claude follows the steps sequentially. At each stage, it prompts me to continue to the next step. It's very fluid and low-friction.

Unsurprisingly, the best results come from concrete, single-purpose increments and solid feature branching strategies.

Brainstorming

This phase is where Claude's intelligence shines. I can bring a loose set of goals/requirements to the prompt, and Claude will work through possible approaches step-by-step. It asks clarifying questions, proposes several approaches, and recommends one in the context of the end goal. This back-and-forth clarifies inputs/outputs, optimizes trade-offs, and allows Claude to act as an expert advisor. It's where the real 'piloting' of Claude takes place, and it's the human element that (so far) can't be replicated autonomously.

Asking Claude questions is critical:

Are the failure modes accommodated/what are the risks we're not considering?

What is your honest assessment of this plan/is my solution over-engineered?

Do you advise breaking out this task into multiple features?

Claude is intelligent enough to provide solid, expert-level feedback in these circumstances. The biggest mistake we can make is to assume we know all the answers — this is one of my foundational reasons for viewing software as a team sport. Assumptions cause churn, and it's very human to get into the 'what' without adequately addressing the 'how' and 'why'.

From plan to execution

Once brainstorming converges, superpowers writes a plan and executes it, spinning off lower-cost models (Haiku) to validate each step against the spec. This is where Claude does the heavy lifting. The plan matters because in a long session, Claude begins to forget things; codifying the spec upfront is the best way to keep output on track.

Hard-won lesson

Start with a feature branch or specify that you want one when beginning a new task, or you'll be untangling commits on main

Superpowers also has a worktree skill for performing parallel work in the same repo.

Wrapping up

I was afraid that I wouldn't enjoy this new-fangled approach to coding. To the contrary (and to my delight) it's been a joy to use. The hard parts remain hard: specification, prioritization, and knowing what "good" looks like.

The biggest risk isn't that Claude writes bad code... it's that developers stop noticing when it does, or they get so used to delegating that churn becomes the main workflow. Every shortcut I let slide becomes a pattern Claude repeats. Every rule I don't codify is a conversation I'll have again tomorrow. Every feature I don't brainstorm and plan properly is a feature I'll rebuild next week. The system I've outlined here has worked well for me so far.

Incidentally, marinating in LLM output all day has also contaminated my writing style. Intellectually I know that I should avoid em-dashes nowadays — instead I've grown overfond of them!

claude-codeaideveloper-tools
Back