CLAUDE
# Claude Code Guidelines
# 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
types for IDstype 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; useinterfaceonly 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.tsin 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
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 tostringmanually.
# 5 — Code Organization
- O-1 (MUST) Place code in
packages/sharedonly if used by ≥ 2 packages.
# 6 — Tooling Gates
- G-1 (MUST)
prettier --checkpasses. - G-2 (MUST)
turbo typecheck lintpasses.
# 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:
- Can you read the function and HONESTLY easily follow what it's doing? If yes, then stop here.
- 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.
- 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.
- Are there any unused parameters in the function?
- Are there any unnecessary type casts that can be moved to function arguments?
- 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?
- 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.
- 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:
- SHOULD parameterize inputs; never embed unexplained literals such as 42 or "foo" directly in the test.
- SHOULD NOT add a test unless it can fail for a real defect. Trivial asserts (e.g., expect(2).toBe(2)) are forbidden.
- SHOULD ensure the test description states exactly what the final expect verifies. If the wording and assert don’t align, rename or rewrite.
- 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.
- SHOULD follow the same lint, type-safety, and style rules as prod code (prettier, ESLint, strict types).
- SHOULD express invariants or axioms (e.g., commutativity, idempotence, round-trip) rather than single hard-coded cases whenever practical. Use
fast-checklibrary 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)
)
);
});
});
- Unit tests for a function should be grouped under
describe(functionName, () => .... - Use
expect.any(...)when testing for parameters that can be anything (e.g. variable ids). - ALWAYS use strong assertions over weaker ones e.g.
expect(x).toEqual(1)instead ofexpect(x).toBeGreaterThanOrEqual(1). - SHOULD test edge cases, realistic input, unexpected input, and value boundaries.
- SHOULD NOT test conditions that are caught by the type checker.
# Code Organization
packages/api- Fastify API serverpackages/api/src/publisher/*.ts- Specific implementations of publishing to social media platforms
packages/web- Next.js 15 app with App Routerpackages/shared- Shared types and utilitiespackages/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.
上次更新: 2025/07/10, 15:01:57