The ULTIMATE AI Coding Guide for Developers (Claude Code)
How I use Claude Code to implement new features in an existing complex codebase.
Here’s how I use Claude Code, the ultimate AI coding tool, to implement new features in an existing complex codebase with many users. This is not a vibe coding tutorial building throwaway MVPs.
This AI coding guide is for software developers who want to improve their daily AI coding workflows, maintain a clean production-grade codebase, and reduce bugs introduced by AI.
I walk through my CLAUDE.md file containing AI coding rules, then implement a real feature from scratch, following a structured step-by-step process.
Reality check:
In 2025, there is no AI tool that performs at a senior eng level.
You still need to be hands-on involved, push back on AI plans, stop it from going down the wrong rabbit hole, question the code, and test extensively.
AI Coding Rules - CLAUDE.md File
Here's my CLAUDE.md file, which you can ask AI to tailor to your codebase. You can also use this in Cursor Rules.
# Claude Code Guidelines by Sabrina Ramonov
## Implementation Best Practices
### 0 — Purpose
These rules ensure maintainability, safety, and developer velocity.
**MUST** rules are enforced by CI; **SHOULD** rules are strongly recommended.
---
### 1 — Before Coding
- **BP-1 (MUST)** Ask the user clarifying questions.
- **BP-2 (SHOULD)** Draft and confirm an approach for complex work.
- **BP-3 (SHOULD)** If ≥ 2 approaches exist, list clear pros and cons.
---
### 2 — While Coding
- **C-1 (MUST)** Follow TDD: scaffold stub -> write failing test -> implement.
- **C-2 (MUST)** Name functions with existing domain vocabulary for consistency.
- **C-3 (SHOULD NOT)** Introduce classes when small testable functions suffice.
- **C-4 (SHOULD)** Prefer simple, composable, testable functions.
- **C-5 (MUST)** Prefer branded `type`s for IDs
```ts
type UserId = Brand<string, 'UserId'> // ✅ Good
type UserId = string // ❌ Bad
```
- **C-6 (MUST)** Use `import type { … }` for type-only imports.
- **C-7 (SHOULD NOT)** Add comments except for critical caveats; rely on self‑explanatory code.
- **C-8 (SHOULD)** Default to `type`; use `interface` only when more readable or interface merging is required.
- **C-9 (SHOULD NOT)** Extract a new function unless it will be reused elsewhere, is the only way to unit-test otherwise untestable logic, or drastically improves readability of an opaque block.
---
### 3 — Testing
- **T-1 (MUST)** For a simple function, colocate unit tests in `*.spec.ts` in same directory as source file.
- **T-2 (MUST)** For any API change, add/extend integration tests in `packages/api/test/*.spec.ts`.
- **T-3 (MUST)** ALWAYS separate pure-logic unit tests from DB-touching integration tests.
- **T-4 (SHOULD)** Prefer integration tests over heavy mocking.
- **T-5 (SHOULD)** Unit-test complex algorithms thoroughly.
- **T-6 (SHOULD)** Test the entire structure in one assertion if possible
```ts
expect(result).toBe([value]) // Good
expect(result).toHaveLength(1); // Bad
expect(result[0]).toBe(value); // Bad
```
---
### 4 — Database
- **D-1 (MUST)** Type DB helpers as `KyselyDatabase | Transaction<Database>`, so it works for both transactions and DB instances.
- **D-2 (SHOULD)** Override incorrect generated types in `packages/shared/src/db-types.override.ts`. e.g. autogenerated types show incorrect BigInt value – so we override to `string` manually.
---
### 5 — Code Organization
- **O-1 (MUST)** Place code in `packages/shared` only if used by ≥ 2 packages.
---
### 6 — Tooling Gates
- **G-1 (MUST)** `prettier --check` passes.
- **G-2 (MUST)** `turbo typecheck lint` passes.
---
### 7 - Git
- **GH-1 (MUST**) Use Conventional Commits format when writing commit messages: https://www.conventionalcommits.org/en/v1.0.0
- **GH-2 (SHOULD NOT**) Refer to Claude or Anthropic in commit messages.
---
## Writing Functions Best Practices
When evaluating whether a function you implemented is good or not, use this checklist:
1. Can you read the function and HONESTLY easily follow what it's doing? If yes, then stop here.
2. Does the function have very high cyclomatic complexity? (number of independent paths, or, in a lot of cases, number of nesting if if-else as a proxy). If it does, then it's probably sketchy.
3. Are there any common data structures and algorithms that would make this function much easier to follow and more robust? Parsers, trees, stacks / queues, etc.
4. Are there any unused parameters in the function?
5. Are there any unnecessary type casts that can be moved to function arguments?
6. Is the function easily testable without mocking core features (e.g. sql queries, redis, etc.)? If not, can this function be tested as part of an integration test?
7. Does it have any hidden untested dependencies or any values that can be factored out into the arguments instead? Only care about non-trivial dependencies that can actually change or affect the function.
8. Brainstorm 3 better function names and see if the current name is the best, consistent with rest of codebase.
IMPORTANT: you SHOULD NOT refactor out a separate function unless there is a compelling need, such as:
- the refactored function is used in more than one place
- the refactored function is easily unit testable while the original function is not AND you can't test it any other way
- the original function is extremely hard to follow and you resort to putting comments everywhere just to explain it
## Writing Tests Best Practices
When evaluating whether a test you've implemented is good or not, use this checklist:
1. SHOULD parameterize inputs; never embed unexplained literals such as 42 or "foo" directly in the test.
2. SHOULD NOT add a test unless it can fail for a real defect. Trivial asserts (e.g., expect(2).toBe(2)) are forbidden.
3. SHOULD ensure the test description states exactly what the final expect verifies. If the wording and assert don’t align, rename or rewrite.
4. SHOULD compare results to independent, pre-computed expectations or to properties of the domain, never to the function’s output re-used as the oracle.
5. SHOULD follow the same lint, type-safety, and style rules as prod code (prettier, ESLint, strict types).
6. SHOULD express invariants or axioms (e.g., commutativity, idempotence, round-trip) rather than single hard-coded cases whenever practical. Use `fast-check` library e.g.
```
import fc from 'fast-check';
import { describe, expect, test } from 'vitest';
import { getCharacterCount } from './string';
describe('properties', () => {
test('concatenation functoriality', () => {
fc.assert(
fc.property(
fc.string(),
fc.string(),
(a, b) =>
getCharacterCount(a + b) ===
getCharacterCount(a) + getCharacterCount(b)
)
);
});
});
```
7. Unit tests for a function should be grouped under `describe(functionName, () => ...`.
8. Use `expect.any(...)` when testing for parameters that can be anything (e.g. variable ids).
9. ALWAYS use strong assertions over weaker ones e.g. `expect(x).toEqual(1)` instead of `expect(x).toBeGreaterThanOrEqual(1)`.
10. SHOULD test edge cases, realistic input, unexpected input, and value boundaries.
11. SHOULD NOT test conditions that are caught by the type checker.
## Code Organization
- `packages/api` - Fastify API server
- `packages/api/src/publisher/*.ts` - Specific implementations of publishing to social media platforms
- `packages/web` - Next.js 15 app with App Router
- `packages/shared` - Shared types and utilities
- `packages/shared/social.ts` - Character size and media validations for social media platforms
- `packages/api-schema` - API contract schemas using TypeBox
## Remember Shortcuts
Remember the following shortcuts which the user may invoke at any time.
### QNEW
When I type "qnew", this means:
```
Understand all BEST PRACTICES listed in CLAUDE.md.
Your code SHOULD ALWAYS follow these best practices.
```
### QPLAN
When I type "qplan", this means:
```
Analyze similar parts of the codebase and determine whether your plan:
- is consistent with rest of codebase
- introduces minimal changes
- reuses existing code
```
## QCODE
When I type "qcode", this means:
```
Implement your plan and make sure your new tests pass.
Always run tests to make sure you didn't break anything else.
Always run `prettier` on the newly created files to ensure standard formatting.
Always run `turbo typecheck lint` to make sure type checking and linting passes.
```
### QCHECK
When I type "qcheck", this means:
```
You are a SKEPTICAL senior software engineer.
Perform this analysis for every MAJOR code change you introduced (skip minor changes):
1. CLAUDE.md checklist Writing Functions Best Practices.
2. CLAUDE.md checklist Writing Tests Best Practices.
3. CLAUDE.md checklist Implementation Best Practices.
```
### QCHECKF
When I type "qcheckf", this means:
```
You are a SKEPTICAL senior software engineer.
Perform this analysis for every MAJOR function you added or edited (skip minor changes):
1. CLAUDE.md checklist Writing Functions Best Practices.
```
### QCHECKT
When I type "qcheckt", this means:
```
You are a SKEPTICAL senior software engineer.
Perform this analysis for every MAJOR test you added or edited (skip minor changes):
1. CLAUDE.md checklist Writing Tests Best Practices.
```
### QUX
When I type "qux", this means:
```
Imagine you are a human UX tester of the feature you implemented.
Output a comprehensive list of scenarios you would test, sorted by highest priority.
```
### QGIT
When I type "qgit", this means:
```
Add all changes to staging, create a commit, and push to remote.
Follow this checklist for writing your commit message:
- SHOULD use Conventional Commits format: https://www.conventionalcommits.org/en/v1.0.0
- SHOULD NOT refer to Claude or Anthropic in the commit message.
- SHOULD structure commit message as follows:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
- commit SHOULD contain the following structural elements to communicate intent:
fix: a commit of the type fix patches a bug in your codebase (this correlates with PATCH in Semantic Versioning).
feat: a commit of the type feat introduces a new feature to the codebase (this correlates with MINOR in Semantic Versioning).
BREAKING CHANGE: a commit that has a footer BREAKING CHANGE:, or appends a ! after the type/scope, introduces a breaking API change (correlating with MAJOR in Semantic Versioning). A BREAKING CHANGE can be part of commits of any type.
types other than fix: and feat: are allowed, for example @commitlint/config-conventional (based on the Angular convention) recommends build:, chore:, ci:, docs:, style:, refactor:, perf:, test:, and others.
footers other than BREAKING CHANGE: <description> may be provided and follow a convention similar to git trailer format.
```
AI Coding Process
My AI coding process follows the steps in my CLAUDE.md file:
open Claude Code (terminal or VSCode extension)
I typically start in “normal mode” (not Planning mode), then transition to “auto accept edits mode” when Claude starts coding
type `/clear` to clear the context & start fresh
type `qnew` to tell Claude to read my CLAUDE.md file and understand all best practices
discuss my user story with Claude and make a plan, making sure to simplify it as much as possible, remove unnecessary features or optimizations, and question anything sketchy
type `qplan` to tell Claude to
analyze similar parts of the codebase and determine whether its plan:
is consistent with rest of codebase
introduces minimal changes
reuses existing code
once I’m happy with the plan, I type `qcode` which tells Claude to:
Implement your plan and make sure your new tests pass. Always run tests to make sure you didn't break anything else. Always run `prettier` on the newly created files to ensure standard formatting. Always run `turbo typecheck lint` to make sure type checking and linting passes.
I use the shortcuts `qcheck`, `qcheckf`, and `qcheckt` pretty frequently after Claude starts writing code. They instruct Claude to review its code changes, ensuring they adhere to my best practices checklists from the CLAUDE.md file. qcheckf focuses on functions that were added or changed, while qcheckt focuses on tests. Still far from perfect, but definitely 10x better than without it!
I open the working tree and view Claude’s real-time edits to files, so I can follow along with its thought process and its proposed changes. I look for things like:
spaghetti code, i.e. code blocks that aren’t easy to follow
substantial changes to API or backend functionality
unnecessary imports, functions, comments, etc.
When I’m happy with the code, I type `qux` which tells Claude:
Imagine you are a human UX tester of the feature you implemented. Output a comprehensive list of scenarios you would test, sorted by highest priority.
This is super helpful because it outputs a UX testing list like this, which I test one-by-one:
The very last step is to commit changes and push. I type `qgit` and Claude Code writes a nice commit message following Conventional Commits format.
In the Youtube video, I walk through this entire process implementing a real feature in my codebase. I encourage you to follow along with your own small feature.
Caveats with AI Coding in 2025
While AI tools can quickly generate functioning code, the initial output often leaves much room for improvement. I rarely, if ever, accept AI’s first draft code.
Code that "works" isn’t always high-quality code, especially when you have a complex production app with many users. You still need to take a very proactive role in reviewing AI code, asking skeptical questions, and confirming each change is consistent with your codebase and best practices.
Without active oversight, it’s WAY TOO EASY to accumulate technical debt that slows down your velocity.
Another caveat is sometimes AI drifts off course. Despite appearing confident, AI tools can interpret things incorrectly or choose suboptimal solutions… all while gaslighting you, “this is the most perfect solution ever”.
I can’t repeat this enough: if you don’t watch closely, you will wasting a lot of time and accidentally introduce breaking changes.
I recommend actively reading Claude Code’s real-time thought process, checking the working tree for its edits, and stopping it early if it seems to be going down a weird rabbit hole.
Need More Help? 👋
1/ If you want to grow on social media and scale your business in coaching, consulting, speaking, selling apps, or digital products… check out Blotato
2/ Free AI courses & playbooks here
3/ Free AI prompts & AI automations
Hey Sabrina, I love your content, especially your great little snippets that you come out with. Like the little god prompts. One of the things that I saw in your recent email was your code guide for developers I loaded it and saved it to my memory in Copilot and in my chat GPT and will be saving it to my Claude as well because I'm always flipping around with my wicked bad ADD anyway I been researching some vibe coding applications and one that seems to go unnoticed and is by far the best one for full stack development is one called Emergent.sh it's really designed to handle a lot of the structure and back-end building that the other ones aren't like lovable and other ones like that it's called emergent.sh So check it out and tell me what you think. Now that I've found your A little chat domain here. I'll be a regular visitor.
https://help.emergent.sh/
Yesssss