Best Practices for AI-Assisted Coding with Claude Code

Best Practices for AI-Assisted Coding with Claude Code

Table of Contents

Introduction: Why This Guide is Different

AI-first development workflows are still shaky to implement across the industry and I’ve found that it’s difficult to find trustworthy and knowledgeable source of information on how to do so effectively. Since we’re currently experiencing the wild west of AI dev workflows, I figured I’d add my own two cents into the conversation.

In particular, it’s been frustrating to see tons of “best practices” content that I’d consider inadequate when trying to learn how to run a team or just your own dev workflow. The majority of articles I’ve found are:

  • AI-generated (where the AI had obviously read other AI-generated content to rehash)
  • grifter-generated (dev evangelists that do not understand (or don’t care) what they are promoting as long as they get hype)
  • articles that, unfortunately, encompass only small-project usage

The Tool Landscape

Let’s address the elephant in the room: which tool should you use?

Over the past 18 months, I’ve used Gemini, Codex, Cursor, Claude Code, Co-Pilot, OpenCode, and several others. And ChatGPT directly for longer before that. The consensus—both mine and the community’s—is that Claude Code is currently the best.

The problem is that tooling is currently moving so fast that Claude code may not be the top choice forever. Worse yet, the tool itself goes through minute (and larger) updates which entirely destabilize any consistent approach to using it.

For example, I’ve found Claude to be less certain of itself in the past few weeks, and it’s been more likely to give me options rather than solve problems outright. And that’s despite the fact that it’s been using the same models for almost a year now.

Target Audience

These best practices are specifically aimed at:

  • larger projects
  • projects that require collaboration
  • enterprise-level complexity
  • languages including but beyond JavaScript

While the tooling does progress and update constantly, we can take a more abstract approach to best practices – some that concretely work across the updates and some that are more process-involved.

Core Best Practice: Write Your Own CLAUDE.md

Your CLAUDE.md (or AGENTS.md) is the first file that your claude core or any other agentic tool will look at. This is your opportunity to set the expectation with the AI tooling and help it understand your code and your dev workflows.

I find that just running /init provides a solid starting point but for larger projects than your weekend todo list project, you should absolutely write your own CLAUDE.md by hand. Use the initialized file as a starting point but from then on, make sure to directly be involved in how claude code approaches the work it’s supposed to do.

The ROI on taking a few initial hours to setup this file is immense, especially over the course of a long-running project.

Note: You may have found your own method of prompting claude and that’s great! This post is supposed to provide my own opinionated approach to prompting claude and utilizing it to its maximum potential.

Language – how to write

I’ve found that CC works very well with imperative direct language. The agent file should be as clear as possible. Short sentences, do/don’ts, etc.

For example:

- use `npm run prettier` to lint and format files
- lint and format files before committing any work

Another good way of thinking about this is to approach your CLAUDE.md like an onboarding document for a developer joining the codebase. And while you may have been promised that CC will completely replace dev-onboarding for codebase, you really do want to provide this structured onboarding for the AI. If there’s anything you’d want to tell your developer about working in a codebase, tell Claude, too.

Think of it this way: You don’t want your AI to second-guess itself and you don’t want it to infer how to contribute to your codebase.

Recommended Structure

I’m providing a general structure to your CLAUDE.md; however, structure it as close to what makes sense for your project as possible. This will be iterative work but my structure is here to provide a starting point.

As an example, I’ll be using my UI library Cottage UI

Here’s how to structure your CLAUDE.md:

1. Project Description

Start with what your project is about. Be specific.

# Project Overview

This is a tailwind-based React UI library that provides fully-styled UI components with accessibility and theming in mind as well as having robust componetn offering.

2. File Structure and Patterns

Be explicit about file organization. The more structured your explanation, the easier it is for Claude to understand what it’s looking for when working on a task.

## Component Structure

Each UI element follows this pattern:

- `Button.tsx` - Component definition and exports
- `Button.stories.tsx` - Storybook examples with all variants
- `Button.test.tsx` - Jest tests
- `Button.md` - Usage documentation

For larger applications, describe major sections:

## Validation System

Location: `/src/validation`

Entrypoint: `/src/validation/index.ts`

The validation system handles all form and API input validation.

See `/src/validation/README.md` for details on extending validators and working with them.

Tip: Make sure to reference other docs in your CLAUDE.md if you have them.

3. Libraries and Deprecated Code

Every codebase has a deprecated something. Whether that’s a pattern, library, or entire endpoints, specify what Claude should avoid using and what it should prefer using.

List preferred libraries and, importantly, deprecated ones:

## Preferred Libraries

When the task calls for it, use the following preferred libraries:

- Use `date-fns` for date manipulation
- Use `zod` for schema validation

## Deprecated Libraries (Do Not Use)

Do not, under any circumstances, use the following libraries:

- `lodash` - Deprecated. Do not use in new code. Refactor when encountered.
- `moment.js` - Replaced by date-fns

Do not use these libraries even when editing a file that utilizes these libraries.

4. Standards and expectations

Make sure to spell out your standards to the AI. There are multiple ways of doing that incredibly well:

  • create examples
  • create templates
  • create a do/don’t list

Claude and other AIs work incredibly well off explanations and examples so it’s important to mix both. It’s also important to let it know about all standards and your expectations.

There will be a more in-depth analysis of this section below.

Here’s a great example of some of these patterns:

## TypeScript Standards

- use semicolons
- add parameter and return types to all exported functions
- utilize `enum` for constants

DON'Ts:

- DON'T use `any` under any circumstances

## React standards

- define react components as constants

DOs:

- DO add types to exported react components

## Component structure

When building a new UI component, create a folder named after the component and then create the following files. We will use the `lib/Button` component in this codebase as an example:

- `Button.tsx` for the component
- `Button.stories.tsx` for the Storybook definition
- `Button.test.tsx` for all unit tests
- `Button.md` for documentation.

React components should generally look like this:

```
// imports
import type { MouseEvent, ReactNode } from "react";

// types, enums and constants
export enum BUTTON_VARIANTS {
PRIMARY = 'primary',
DEFAULT = 'default'
}

// variants
const ButtonVariantStyling: Record<BUTTON_VARIANTS, string> = {
[BUTTON_VARIANTS.PRIMARY]: 'bg-orange-700 hover:bg-orange-500 text-white',
[BUTTON_VARIANTS.DEFAULT]: 'bg-gray-700 hover:bg-gray-800 text-white'
}

// helpers
const getButtonText = (key) => storage[key];

// interface for React component params
interface ButtonProps {
children: ReactNode;
onClick?: (e: MouseEvent<HTMLButtonElement>) => void;
variant?: BUTTON_VARIANTS;
size?: BUTTON_SIZES;
}

// component goes last
const Button = ({ children, onClick, variant = BUTTON_VARIANTS.DEFAULT}: ButtonProps): ReactNode => {
// styling for main component
const className = `rounded p-2 ${ButtonVariantStyling[variant]}`

// return component
return <button onClick={onClick} className={className}>{children}</button>;
};

// export component as default
export default Button;
```

I find it important to add a detailed section like this for every major part of the codebase. If it feels like you’re writing a 200 page essay, the most helpful tip I can give you here is to write separate README.md files for each large section, co-locate it with the code, and reference it in the CLAUDE.md. eg.

## Component Structure

When building a new UI component, create a folder named after the component and then create the following files. We will use the `lib/Button` component in this codebase as an example:

- `Button.tsx` for the component
- `Button.stories.tsx` for the Storybook definition
- `Button.test.tsx` for all unit tests
- `Button.md` for documentation.

Check `lib/README.md` for full docs on building new components, registering them, and understanding how the component structure works.

Tip: Give your AI relative links to additional documentation so it can dive into a subject of its choosing when needed. When doing so, make sure to explain when and why to access this additional documentation

I’ve found that giving the AI links and describing when it should access that documentation lets the AI load that context when needed and it lets you be as expansive in your descriptions and examples as you want to be without sacrificing your context window.

5. Workflow and Testing

Explain your development workflow explicitly:

## Development Workflow

When solving a new task, ensure that:

- all work follows best practices outlined in this document and in the `README.md`
- ensure you are working in the correct part of the codebase
- attempt to implement work
- validate your work by writing tests and running them via `npm run test`
- ensure all tests pass
- run `npm run build` to ensure the project builds
- create a SUMMARY.md with the summary of the work that was done

## Branching strategy

Use standard gitflow branching strategy. For more info: https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow

Tip: Give your AI external links to valuable docs so that it can follow opensource and public documentation.

Writing Style: Clear and Explicit

Repeat yourself liberally. It’s better to repeat than have Claude assume wrongly.

Even if something seems clear, repetition reinforces the keywords and patterns. Use:

  • Bullet points for clarity
  • Diagrams (or text-based diagrams) for architecture

A great example is a flatten folder structure tree:

## Folder Structure

- `src/components` React components
- `src/utils` utilities utilized by React components and other parts of the codebase

## Codeflow

All components are exported via `index.tsx` at the root of the directory. This is where components are registered to be used once the library builds.

index.tsx -> src/components/index.tsx -> src/components/ComponentName/ComponentName.tsx

The No-Nos List

Create an explicit list of things Claude should not do:

## Do Not Do

- Do not use any deprecated libraries
- Do not make large architectural changes
- Do not change API contracts
- Do not modify `/src/legacy` - this is v1 and frozen
- Do not touch `.env` or configuration files
- Do not bootstrap the entire application in unit tests

Be literal. Don’t say “API contracts should be stable” – say ”DO NOT change the API contracts.”

Tip: Working with Claude is an iterative process. Whenever you come across a behavior you don’t want, add it to the Do Not Do list. It’ll help steer Claude down the right direction.

Further Resources

Further resources can be supplied via internal documentation and linking to them or by supplying external documentation. Ensure you explain why and when this information should be accessed.

You can specify these inline (like in some examples above) or in its own section.

## Further resources

- [Anatomy of My Ideal React Component](http://antjanus/digital-garden/the-anatomy-of-my-ideal-react-component) -- react component standards for anything not specified in documentation
- [Constants vs. Enums in TypeScript](https://antjanus.com/digital-garden/constants-enum-ts) -- when to use constants vs. enums
- [Best Practices for AI-Assisted Coding With Claude Code](https://antjanus.com/ai/claude-code-best-practices) -- best practices for Claude Code -- includes documentation on CLAUDE.md generation/updating
- [ARCHITECTURE.md](./ARCHITECTURE.md) -- high-level architecture documentation for this project

Leverage Examples and Templates

AI excels with concrete examples and templates. Provide as many as possible.

Templates

If you have a specific pattern in your codebase, create a template. It can be as comprehensive and detailed as you want it to be. I wrote an article on my ideal react component and I still use it as a template for React but it doesn’t have to be this detailed.

Example: If you use Mock Service Worker for every integration test:

## Test Template

Every integration test should follow this structure:

```typescript
describe('ComponentName', () => {
beforeEach(() => {
// Initialize MSW
})

afterEach(() => {
// Reset MSW
})

// Tests here
})
```

See `/src/utils/test-helpers` for available utilities.

Tip: You can hint to the AI when there are commonly-used utilities/libraries and when to use them.

Using Real Files as Templates

Templates don’t have to be blank scaffolding. You can use a real, trusted file as reference:

## API Endpoint Template

When creating a new API endpoint, reference `/src/api/users.ts` as the
canonical example. It demonstrates:

- Proper error handling
- Validation with zod
- TypeScript types
- Test coverage

This approach has a huge advantage: templates stay up-to-date automatically. If you reference an existing file that evolves with your codebase, you never have stale templates. The downside is that if this files goes untouched for a longer time period, it can provide Claude with a stale example. The great thing is that as soon as you recognize it, you can update CLAUDE.md immediately and fix the issue for you and your colleagues.

Iterative Improvement

With Claude, every frustration is an opportunity to improve your setup. The huge upside of LLMs is that you can actually affect your tooling instead of having to put up with unwanted behavior.

This goes both ways, if Claude surprises you with a positive pattern, you can reinforce that pattern.

Some examples:

Claude changes your .env. If it’s unwanted behavior, update your CLAUDE.md with DO NOT update .env files

Claude bootstraps your tests more efficiently. If it’s wanted behavior, update your CLAUDE.md with the directions on how to do it – or ask Claude to update its own directions.

Specify Expected Output

Beyond describing inputs (your codebase) and process (workflow), specify the output format you want. Templates and examplse fall into this category but you can do this with other aspects of your AI use.

Planning Workflow Example

I like to ask Claude to plan before executing major changes. You can specify this:

## Planning Workflow

For any work requiring more than 3 file changes:

1. Create a plan first before making changes
2. Output format: `PLAN.md` with:
   - Summary and explanation of the project
   - List of files to be modified with links
   - Step-by-step implementation plan
   - Mermaid diagram if architectural changes involved
3. Wait for approval before implementing

The plan should be detailed enough that another AI session could pick
it up and continue.

LLMs work really well when you tell them exactly what you need, including the format.

Documentation: The Foundation

I’m listing this last, but it should really be your first priority: Write documentation for your project.

Root README.md

Create a comprehensive entry point for that onboarding developer and for any developer learning about the codebase:

# Project Name

## Project description

[Clear description]

## Getting Started

[Setup instructions]

## Commands

```bash
npm test
```

## Architecture

[High-level overview]

## Key Directories

- `/src/components` - UI components
- `/src/api` - API handlers
- `/src/utils` - Shared utilities

## Deployment

[Deployment process]

Much of this might overlap with claude.md, but that’s fine. Claude works really well when you treat it like a regular developer learning a new codebase.

Folder-Level Documentation

Extend the README pattern to any confusing or complex parts of the codebase so that key parts or domains have their own documentation to get a developer working there:

/tests/README.md

# Testing Guide

## Framework

We use Jest 27 (older version) for testing.

## Utilities

- See `/tests/helpers` for test utilities
- Mock data generators in `/tests/fixtures`

## Patterns

[Testing patterns and examples]

Complex Component Documentation

For complicated subsystems, create dedicated README files.

Example: ETL Pipeline

/src/etl/README.md

# ETL Pipeline

## How It Works

[Extract, Transform, Load overview]

## Adding a New Data Provider (Extract)

1. Create adapter in `/src/etl/adapters/`
2. Use `/src/etl/adapters/sample-adapter.ts` as template
3. Register in `/src/etl/adapters/index.ts`
4. Add tests in `/src/etl/adapters/__tests__/`

## Adding a New Load Target

[Similar step-by-step guide]

## Testing

[How to test the ETL pipeline]

Next Steps

Ok, so now you have a good understanding of how to build a solid CLAUDE.md or an AGENTS.md file but it feels like a lot of work, right?

It’s always much easier to start with something than nothing and then iterate. And your OpenCode or Claude code or Gemini or Codex can do just that by reading this article and using it as its own template for its agent file. Then, you can iterate on the documentation and agent file as you work with your AI development companion.

Through this iterative process, you’ll end up with a codebase that’s:

  • Well-documented for new human developers
  • Fantastically documented for Claude and other AI tools
  • Easier to maintain and extend
  • More consistent in code quality

The key is treating AI coding tools like team members who need good onboarding, clear guidelines, and continuous feedback. The time you invest in documentation and structure pays dividends in faster, more accurate AI-assisted development.

If you want more examples, you can check out the Cottage UI repository where I’m experimenting with Claude, OpenCode, and all kinds of agents to get the best workflow down.

I’d also suggest referencing this article in your CLAUDE.md in case you want to update your CLAUDE.md or need help extending/structuring your documentation and existing CLAUDE.md.