Skip to main content

Danielle (Hoopes) Scantling8 pieces · 13 min

The Habits I Use Coding With Claude (A Security-First, Token-Conscious Workflow)

People keep asking me how I actually work with Claude day to day. Not the demo-video version. The ship-it-to-production version.

So here's the honest answer: my workflow is pretty boring, on purpose. I lean hard toward security and verification, I'm stingy with tokens, and I review every line of code before it ships, even when the tests are green. None of that is new. They're the same habits that made me a decent engineer before AI showed up, just pointed at a very fast new collaborator.

These are the habits I use, grouped by what they protect. And because nobody's workflow is finished, I've added a section at the end for the ones I'm going to start using.

01 / 08 · 1 min

Why I Care About Habits More Now, Not Less

When I wrote everything by hand, my speed was limited by typing and thinking. That friction was annoying, but it was also a built-in review step. I physically couldn't produce 800 lines of code I didn't understand in four minutes.

With Claude, I can. Easily. And that's the whole problem.

The bottleneck moved. It used to be "how fast can I write this?" Now it's "how fast can I verify this?" Experience matters more with AI, not less, because judgment is the part that doesn't come in the box: knowing what good code looks like, where bugs hide, when something feels off. My habits are how I keep that judgment in the loop instead of letting it get steamrolled by a firehose of plausible-looking output.

The way I think about it: Claude is an extremely fast, extremely well-read collaborator with zero memory of my last three production incidents. My habits are the part that remembers.

Keep reading → I Plan Before I Prompt

02 / 08 · 2 min

I Plan Before I Prompt

I define "done" before I start

The best predictor of a good session is whether I knew what I wanted before I opened it. So I write the spec first, even if it's five bullet points: what the change does, what it shouldn't touch, how I'll know it works.

"Add rate limiting to the reactions endpoint, 10 requests per minute per IP, return 429 with a Retry-After header, don't change the Firestore schema" beats "add rate limiting" every single time. Vague prompts don't get you vague code. They get you confident code that solves a slightly different problem.

I make Claude plan before it builds

For anything bigger than a one-file fix, I ask for a plan first, either in plan mode (Shift+Tab to cycle into it) or just "don't write code yet, give me a plan."

Then I actually review the plan. That's where I catch the "I'll refactor the auth module while I'm in here" energy before it turns into a 40-file diff. Reviewing a plan takes two minutes. Reviewing the wrong implementation takes an hour, plus the hour (and the tokens) to redo it.

I treat CLAUDE.md like living team memory

My CLAUDE.md is the onboarding doc for a teammate who shows up with amnesia every morning: the exact build, test, and lint commands, conventions that aren't obvious from the code, and things that have burned me before.

My rule: if Claude makes the same mistake twice, it gets a line in CLAUDE.md. It's the AI version of writing a postmortem instead of just fixing the bug. I keep it short and specific, though. A 2,000-line CLAUDE.md is just noise Claude has to wade through, and it's noise you pay for in tokens on every single session.

I turn repeated prompts into skills and commands

If I've typed the same prompt more than three times, it becomes a skill or a slash command. On my own site I run a /spec, /plan, /build, /test, /review, /ship flow, plus skills that kick in automatically for security and frontend work. The structure is borrowed from Addy Osmani's agent-skills.

I automate my own process the same way I'd automate a deployment. Consistency beats remembering.

Keep reading → Security Is a Habit, Not a Phase

03 / 08 · 2 min

Security Is a Habit, Not a Phase

This is where I'm the most opinionated. Security that depends on me remembering to check is security that will eventually get skipped on a Friday afternoon. So I push it into the workflow.

A security skill that runs every time

I have a Claude Code skill that enforces OWASP Top 10 rules on everything Claude writes for me: input validation, parameterized queries, no secrets in logs, auth checks, the works. Not when I ask. Every time. I wrote up the whole thing in How I Made Claude Code Enforce OWASP Rules if you want to steal it.

Secrets stay out of reach

Claude doesn't need to read my .env files to do its job, so it doesn't. I keep credentials behind deny rules and ignore files, and I scan diffs for secrets before anything gets committed. The easiest leaked key to deal with is the one that never ended up in a context window.

Least-privilege permissions

Allow-listing safe commands isn't enough on its own. I explicitly deny the dangerous ones: rm -rf, force pushes, anything that touches production. And I never run in skip-all-permissions mode on real code. The whole point of an agent is that it acts on its own, which is exactly why its blast radius needs a ceiling.

Prompt-injection hygiene

Anything Claude reads from outside my own head is untrusted input: web pages, GitHub issues, READMEs, dependency docs, even config files in a repo I just cloned. Any of those can contain text written to look like instructions. Prompt injection is #1 on OWASP's list for LLM applications for a reason.

So I treat changes to CLAUDE.md, skills, and commands like code changes that need review, I'm suspicious of any "instructions" that show up inside fetched content, and I'm extra careful about what tools and permissions are available when Claude is reading something I didn't write.

Guardrails live in tooling, not my memory

Branch protection so nothing, human or AI, pushes straight to main. Lint, format, and type checks that run automatically. Tests on every PR, no exceptions. Good guardrails are what let me move fast because the safety net is real.

Keep reading → I Review Every Line, Even When Tests Pass

04 / 08 · 2 min

I Review Every Line, Even When Tests Pass

This is the habit people push back on the most. "If the tests pass, why review?" Because tests only check what someone thought to test. Review catches everything else.

Tests are the contract

I still lean on tests hard:

  • Failing test first. The implementation gets a clear target.
  • For bugs, prove it before fixing it. Reproduce the bug in a failing test, fix it, watch it pass. No reproduction, no fix.
  • Watch what happens to the tests. If Claude "fixes" a failing test by changing the assertion, deleting it, or mocking the exact thing under test, that's not a fix. That's hiding the evidence.

A second-pass review agent goes first

Before I review, a separate review with a fresh context (a subagent or a dedicated review command) critiques the diff without having been part of writing it. The agent that wrote the code is invested in its own reasoning, and a fresh one isn't. It doesn't replace my review. It hands me a better starting point for it.

Then I review it anyway

AI code isn't worse than human code. It's more convincing. It's well-formatted, well-named, and confidently commented, which means it slides right past the "this looks sketchy" alarm that messy human code sets off. So I review it harder, not softer.

What I specifically look for:

  • Error handling that silently swallows errors
  • Made-up APIs, config options, or library functions
  • Security basics: input validation, auth checks, injection, secrets
  • Changes to files that had nothing to do with the task
  • Accessibility: labels, focus states, keyboard support, contrast

The rule underneath all of it: if my name is on the commit, I own every line. "Claude wrote it" isn't something I get to say in an incident review.

Small slices, so review is actually possible

I ask for one thin, testable slice at a time: build it, run it, review it, commit it, next. A 60-line diff I can actually read is worth more than a 900-line diff I skim and pray over. If a change is too big to review properly, it was too big to ask for in one go.

Claude explains, then I explain it back

I ask "why" constantly. Why this approach? What are the tradeoffs? My favorite prompt: "Argue against your own solution." You'd be amazed what comes out.

Then I flip it. Before I accept a non-trivial change, I explain it back in my own words. If I can't, I don't understand it well enough to maintain it, and I definitely don't understand it well enough to debug it at 2am.

Keep reading → How I Conserve Tokens

05 / 08 · 2 min

How I Conserve Tokens

I don't care about tokens because of the bill (okay, partly the bill). I care because a bloated context makes Claude worse. After an hour of back-and-forth, it's carrying every dead end we explored and starts getting confused by its own earlier attempts. Lean context is cheaper and sharper.

  • One task per session. When I switch tasks, I /clear or start fresh.
  • Compact before it's full. I run /compact at natural checkpoints, like after a slice is committed, instead of letting the context fill up and auto-compact in the middle of something delicate.
  • Summarize and restart when it gets messy. Ask Claude for a short summary of where things stand, then start clean with just that.
  • Right-size the model. A smaller, faster model handles simple work like renames, boilerplate, and quick lookups. The frontier model is for design decisions and hard debugging. Taking the biggest model to do a rename is like taking a helicopter to get groceries.
  • Point, don't paste. I reference specific files and functions instead of dumping half the codebase into the prompt.
  • Plan first. A reviewed plan is the cheapest way to avoid paying for the same feature twice.
  • Know when to take the wheel back. If Claude has tried the same fix two or three times and it still isn't working, I stop. Either the problem is underspecified, the approach is wrong, or I'm about to spend 45 minutes and a pile of tokens watching a very polite loop. I reset, rethink, or just write that part by hand.
Keep reading → How I Keep Myself Sharp

06 / 08 · 1 min

How I Keep Myself Sharp

This is the one people don't love hearing. If Claude writes 100% of my code, my debugging instincts and my ability to read unfamiliar code will quietly fade. And those are exactly the skills that make my AI output good in the first place.

So I deliberately do some things by hand: tricky debugging, core business logic, the first pass at a new design. Not out of nostalgia. It's maintenance on the judgment everything else depends on. And for anyone leading a team: your juniors are learning to code in a world where the shortcut is always right there. They watch what you do more than what you say.

Keep reading → Things I'm Going to Incorporate

07 / 08 · 2 min

Things I'm Going to Incorporate

Putting this post together made me look hard at my own gaps. These are the two I'm adding, and honestly, I think the first one is something we should all be doing.

Verify every package Claude suggests

This is the one I think most people aren't thinking about, me included until recently. When Claude suggests a dependency, it's easy to just say "yes, install it." But AI models sometimes recommend packages that don't exist, with names that sound completely plausible. Attackers have noticed. They register those hallucinated names on npm and PyPI with malicious code inside and wait for someone's agent to install them. It even has a name now: slopsquatting.

It's a supply-chain attack that doesn't need to trick a human at all. It only needs one of us to approve an install without looking. And a package isn't "just one package." Its install scripts run on your machine, and its code ships inside your build.

What I'm going to do before approving any new dependency:

  • Confirm it exists and it's the real one. Look it up on the registry directly. Watch for look-alike names, typos, and scope swaps.
  • Check the signals. Linked source repo, download counts, publish history, maintainers. A package published last week with no repo and a name that sounds exactly like what Claude needed is a red flag.
  • Ask "do I even need this?" Plenty of AI-suggested dependencies replace ten lines of code. Fewer dependencies means a smaller attack surface.
  • Pin it and audit it. Commit the lockfile and run npm audit (or your ecosystem's equivalent) as part of review.
  • Push it into tooling. A CLAUDE.md rule telling Claude to ask before adding any dependency, plus a deny rule on package installs so a new dependency always needs my approval.

Thirty seconds on a registry page is the cheapest security win in this whole post.

Threat-model the change

For anything that touches auth, user input, or data, I'm going to ask one question before I review the code: "How would an attacker abuse this?" My security skill checks the code against known patterns. Threat modeling checks the design, which is where the scarier bugs live. It's a one-prompt habit with a big payoff.

Keep reading → The Excuses I Watch For

08 / 08 · 1 min

The Excuses I Watch For

I keep a version of this table in my own skills files, because these rationalizations sound completely reasonable in the moment.

The shortcuts I catch myself (and Claude) reaching for, and why they cost more later:

The Excuse The Reality
"The tests pass, no need to review." Tests only check what someone thought to test.
"I'll add tests later." I won't, and now nothing proves the code works.
"The diff is too big to review, but it looks fine." That's exactly when bugs slip through. Split it up.
"Claude said it fixed it." Claude reports intent. Tests and running code report results.
"It's just one package." It's one package that runs code on your machine and ships in your build. Check it.
"Just one more retry." Three failed tries means rethink, not retry.

References

  1. 01Claude Code: Best practices for agentic coding (Anthropic)
  2. 02OWASP Top 10
  3. 03OWASP Top 10 for LLM Applications: LLM01 Prompt Injection
  4. 04agent-skills (Addy Osmani)