Tôi nhớ rất rõ lần đầu tiên sử dụng Claude Code refactor authentication module cho dự án banking app. Mười phút sau: code sạch đẹp, error handling đầy đủ, tests được generate sẵn. Hype là thật.
Rồi thực tế ập xuống. Claude Code generate code theo naming convention của Google Android style guide. Dự án tôi đang làm lại dùng convention của team kiểu camelCase cho variables nhưng PascalCase cho ViewModels. Nó generate test files vào thư mục test/ trong khi convention của team là __tests__/ cùng level với source files. Nó thậm chí còn dùng LiveData trong khi team đã migrate hết sang StateFlow.
Tôi mất 45 phút sửa những thứ lẽ ra không nên sai. Rồi thêm 30 phút giải thích cho Claude Code trong session tiếp theo. Rồi lại mất 40 phút trong session sau đó vì nó quên hết.
Cứ thế lặp lại suốt 3 tháng cho đến khi tôi phát hiện ra CLAUDE.md.
CLAUDE.md Là Gì Và Tại Sao Nó Quan Trọng
CLAUDE.md là một file đặc biệt ở root project mà Claude Code tự động đọc vào đầu mỗi session. Không cần config gì. Không cần tell nó đọc. Nó sẽ tự động đọc và áp dụng mọi thứ bạn viết trong đó.
Hãy coi nó như tài liệu onboarding — nhưng dành cho AI assistant.
Một CLAUDE.md tốt cho Claude Code biết:
- Tech stack thực tế — frameworks, libraries, versions cụ thể
- Naming conventions — camelCase hay snake_case? ViewModelImpl hay VMImpl?
- Testing patterns — test files để đâu? Naming convention của test thế nào?
- Architectural decisions — MVVM? Clean Architecture? Repository pattern?
- Files không được sửa — build configs, CI/CD scripts, third-party code
- Secret files cần tránh — .env, credentials.json, API keys
- Team preferences — comment bằng tiếng Việt hay tiếng Anh? Function dài bao nhiêu thì cần refactor?
Nghe có vẻ nhiều? Không cần viết hết một lần. Bắt đầu với 15-20 dòng là đủ. Thêm dần theo thời gian.
Những Gì Nên Đưa Vào CLAUDE.md
Đây là breakdown chi tiết những gì tôi đưa vào CLAUDE.md sau 3 tháng thử nghiệm:
1. Project Overview (3-5 dòng)
# Project: SocialApp Android — Social Media Platform**Type:** Native Android app + Kotlin Multiplatform shared modules**Target:** Consumer app (B2C)**Maintenance:** Active development, 200k+ usersGiúp Claude Code hiểu context business. Nếu đây là enterprise app, nó sẽ ưu tiên stability hơn bleeding-edge features. Nếu là prototype, nó sẽ favor speed hơn perfect architecture.
2. Tech Stack (càng chi tiết càng tốt)
## Tech Stack- **Language:** Kotlin 1.9.x (target JVM 17)- **UI:** Jetpack Compose + Material3- **Architecture:** MVVM + Clean Architecture (3 layers: presentation, domain, data)- **Networking:** Retrofit 2 + OkHttp + Moshi- **Database:** Room + SQLDelight (KMP modules)- **DI:** Hilt (Android) + Koin (KMP shared)- **Async:** Kotlin Coroutines + Flow- **Testing:** JUnit5, MockK, Turbine for Flow testingClaude Code cần biết chính xác stack bạn dùng. Đừng nói chung chung “Kotlin”. Nói rõ “Kotlin 1.9 + Coroutines + Flow”. Đừng nói “REST API”. Nói “Retrofit 2 với Moshi serialization”.
3. Conventions (phần quan trọng nhất)
## Naming Conventions- **Files:** PascalCase (`UserViewModel.kt`, `LoginScreen.kt`)- **Classes/Objects:** PascalCase- **Functions:** camelCase- **Variables:** camelCase- **Constants:** UPPER_SNAKE_CASE- **Composables:** PascalCase như components (`UserCard`, `BookingForm`)- **ViewModels:** `[Feature]ViewModel` (e.g., `LoginViewModel`)- **Use Cases:** `[Action][Entity]UseCase` (e.g., `GetBookingsUseCase`)- **Repositories:** `[Entity]Repository` interface + `[Entity]RepositoryImpl` (e.g., `UserRepository`, `UserRepositoryImpl`)
## Folder Structureapp/ ├── presentation/ │ ├── login/ │ │ ├── LoginScreen.kt │ │ ├── LoginViewModel.kt │ │ └── LoginUiState.kt │ └── booking/ │ └── … ├── domain/ │ ├── model/ │ ├── repository/ (interfaces) │ └── usecase/ └── data/ ├── remote/ ├── local/ └── repository/ (implementations)
## Code Style- **Max line length:** 120 characters- **Function max lines:** 40 lines (refactor if longer)- **Class max lines:** 300 lines (split if longer)- **Parameters:** If >3 params, extract to data class- **Comments:** Vietnamese OK for business logic, English for technical implementationĐây chính là phần làm chất lượng output nhảy vọt. Claude Code sẽ không còn generate code sai convention nữa.
4. Critical Rules (hard constraints)
## CRITICAL RULES- ❌ **NEVER** modify `build.gradle.kts` without explicit request- ❌ **NEVER** access or read `.env`, `local.properties`, `google-services.json`- ❌ **NEVER** hardcode API keys, secrets, or URLs (use BuildConfig)- ❌ **NEVER** use deprecated APIs (`LiveData` → use `StateFlow` instead)- ❌ **NEVER** directly call repositories from ViewModels (use UseCases)- ✅ **ALWAYS** use sealed classes for UI state- ✅ **ALWAYS** handle loading/success/error states explicitly- ✅ **ALWAYS** add KDoc for public APIsViết rõ ràng, dứt khoát. Dùng emoji ❌ và ✅ cho dễ scan. Claude Code sẽ respect những rules này.
5. Patterns To Follow
## Patterns
### ViewModel Pattern```kotlin// CORRECT ✅class LoginViewModel @Inject constructor( private val loginUseCase: LoginUseCase) : ViewModel() { private val _uiState = MutableStateFlow<LoginUiState>(LoginUiState.Idle) val uiState: StateFlow<LoginUiState> = _uiState.asStateFlow()
fun login(email: String, password: String) { viewModelScope.launch { _uiState.value = LoginUiState.Loading loginUseCase(email, password) .onSuccess { _uiState.value = LoginUiState.Success(it) } .onFailure { _uiState.value = LoginUiState.Error(it.message) } } }}
sealed interface LoginUiState { object Idle : LoginUiState object Loading : LoginUiState data class Success(val user: User) : LoginUiState data class Error(val message: String?) : LoginUiState}// WRONG ❌
class LoginViewModel : ViewModel() { var isLoading = false // mutable var exposed var user: User? = null // nullable state // No sealed class, no Flow}### Testing Pattern```kotlin// Test files: Mirror source structure// Test: app/src/test/java/presentation/login/LoginViewModelTest.kt
@Testfun `when login success then emit Success state`() = runTest { // Arrange val useCase = mockk<LoginUseCase>() coEvery { useCase(any(), any()) } returns Result.success(mockUser) val viewModel = LoginViewModel(useCase)
// Act viewModel.login("test@example.com", "password")
// Assert viewModel.uiState.test { assertEquals(LoginUiState.Loading, awaitItem()) assertEquals(LoginUiState.Success(mockUser), awaitItem()) }}Đưa code examples thật. Claude Code học patterns nhanh hơn nhiều từ code so với từ ngữ.
---
## Trước Và Sau CLAUDE.md
Để thấy rõ impact, đây là so sánh before/after khi tôi thêm CLAUDE.md vào project:
### Trước khi có CLAUDE.md
**Prompt:**> "Add pull-to-refresh to BookingListScreen"
**Claude Code output:**```kotlin// BookingListScreen.kt@Composablefun BookingListScreen(viewModel: BookingViewModel = viewModel()) { var isRefreshing by remember { mutableStateOf(false) } val bookings by viewModel.bookings.collectAsState() // LiveData!
SwipeRefresh( // deprecated library state = rememberSwipeRefreshState(isRefreshing), onRefresh = { viewModel.refreshBookings() } ) { LazyColumn { items(bookings) { booking -> BookingItem(booking) // component doesn't exist } } }}Problems:
- Dùng
SwipeRefresh(deprecated) thay vìPullRefreshIndicator(Material3) - Dùng
collectAsState()với LiveData (team đã migrate sang StateFlow) - Không handle loading/error states
viewModel()function không tồn tại (dự án dùng Hilt injection)- Component
BookingItemchưa có, nhưng generate luôn không hỏi
Tôi mất 30 phút sửa.
Sau khi có CLAUDE.md
Prompt:
“Add pull-to-refresh to BookingListScreen”
Claude Code output:
@Composablefun BookingListScreen( viewModel: BookingListViewModel = hiltViewModel()) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val pullRefreshState = rememberPullRefreshState( refreshing = uiState is BookingListUiState.Loading, onRefresh = { viewModel.refreshBookings() } )
Box(Modifier.pullRefresh(pullRefreshState)) { when (val state = uiState) { is BookingListUiState.Loading -> LoadingIndicator() is BookingListUiState.Success -> { LazyColumn { items(state.bookings) { booking -> BookingCard(booking = booking) // existing component } } } is BookingListUiState.Error -> ErrorMessage(state.message) }
PullRefreshIndicator( refreshing = uiState is BookingListUiState.Loading, state = pullRefreshState, modifier = Modifier.align(Alignment.TopCenter) ) }}Perfect:
- Dùng Material3
PullRefreshIndicator✅ - Dùng
StateFlowvớicollectAsStateWithLifecycle()✅ - Handle đầy đủ loading/success/error states ✅
- Injection đúng với
hiltViewModel()✅ - Dùng component đã có (
BookingCard) ✅
Copy-paste vào là chạy. Zero modifications.
5 Section Mọi CLAUDE.md Cần Có
Sau nhiều lần thử nghiệm, tôi rút ra template 5-section này work cho mọi loại project:
# [Project Name] — [One-line description]
## 1. OVERVIEW- **Type:** [Web app / Mobile / Backend API / CLI tool]- **Stack:** [Primary languages and frameworks]- **Stage:** [Prototype / Active dev / Production / Maintenance]
## 2. TECH STACK- **Language:** [Versions]- **Framework:** [Versions]- **Database:** [Type + ORM]- **Key Libraries:** [Top 5-10 dependencies]
## 3. CONVENTIONS### Naming- Files: [Convention]- Classes: [Convention]- Functions: [Convention]- Tests: [Convention]
### Structure[Folder structure với ví dụ paths]
### Code Style- Max line length: [X]- Max function lines: [X]- Preferences: [Tabs/spaces, comment language, etc]
## 4. CRITICAL RULES- ❌ NEVER [Hard constraints]- ✅ ALWAYS [Must-follow patterns]
## 5. PATTERNS[Code examples cho common tasks với annotations ✅ CORRECT / ❌ WRONG]Template này đủ ngắn để maintain (không quá 100 dòng), đủ chi tiết để Claude Code generate quality output.
CLAUDE.md Cho Các Loại Project Khác Nhau
Mỗi loại project cần focus vào aspects khác nhau. Đây là những gì tôi recommend:
React / Next.js Project
# Project: E-commerce Dashboard
## TECH STACK- Next.js 14 + TypeScript + App Router- React Server Components + Server Actions- Prisma ORM + PostgreSQL- TailwindCSS + shadcn/ui components- Zod for validation
## CONVENTIONS### File Structureapp/ ├── (auth)/ # Route groups │ └── login/ │ └── page.tsx ├── api/ # API routes └── dashboard/ └── [id]/ # Dynamic routes lib/ ├── actions/ # Server Actions ├── db/ # Prisma client └── utils/
### Component Patterns```typescript// CORRECT ✅ — Server Component by defaultexport default async function UserDashboard({ params }: Props) { const user = await db.user.findUnique({ where: { id: params.id } }); return <UserProfile user={user} />;}
// CORRECT ✅ — Client Component when needed'use client';export function UserProfile({ user }: Props) { const [editing, setEditing] = useState(false); // ... interactive logic}
// WRONG ❌ — Unnecessary 'use client''use client'; // Don't need client for static renderexport default function About() { return <div>About page</div>;}Server Actions
// CORRECT ✅'use server';export async function updateUser(formData: FormData) { const validated = userSchema.parse({ name: formData.get('name'), email: formData.get('email'), }); await db.user.update({ where: { id: userId }, data: validated }); revalidatePath('/dashboard');}CRITICAL RULES
- ❌ NEVER use API routes for mutations (use Server Actions)
- ❌ NEVER put database access in components (use Server Actions or separate functions)
- ❌ NEVER hardcode secrets (use process.env)
- ✅ ALWAYS validate with Zod before database operations
- ✅ ALWAYS use Server Components by default
- ✅ ALWAYS revalidatePath after mutations
SECRET FILES (never access)
- .env.local — DATABASE_URL, STRIPE_SECRET_KEY
- docker-compose.yml
### Python / FastAPI Backend
```markdown# Project: Analytics API
## TECH STACK- Python 3.11 + FastAPI- SQLAlchemy 2.0 + Alembic- PostgreSQL + Redis- Pydantic V2 for validation- Celery for background tasks
## CONVENTIONS### Structureapp/ ├── api/ │ └── v1/ │ ├── endpoints/ │ └── dependencies.py ├── core/ │ ├── config.py # Settings (BaseSettings) │ └── security.py ├── models/ # SQLAlchemy models ├── schemas/ # Pydantic schemas ├── services/ # Business logic └── repositories/ # Data access
### Layering Pattern```python# CORRECT ✅ — Router → Service → Repository@router.post("/users")async def create_user( user_in: UserCreate, service: UserService = Depends(get_user_service)): return await service.create_user(user_in)
class UserService: def __init__(self, repo: UserRepository): self.repo = repo
async def create_user(self, user_in: UserCreate) -> User: # Business logic here return await self.repo.create(user_in)
# WRONG ❌ — Direct database access in router@router.post("/users")async def create_user(user_in: UserCreate, db: Session = Depends(get_db)): user = User(**user_in.dict()) # No validation, no business logic db.add(user) db.commit() return userAsync Everywhere
# CORRECT ✅async def get_user(user_id: int) -> User: return await user_repo.get(user_id)
# WRONG ❌def get_user(user_id: int) -> User: # Missing async return user_repo.get(user_id)CRITICAL RULES
- ❌ NEVER skip layers (Router must call Service, Service must call Repository)
- ❌ NEVER access os.getenv() directly (use Settings from core.config)
- ❌ NEVER use synchronous functions (everything is async/await)
- ❌ NEVER omit type hints (mypy strict mode enabled)
- ✅ ALWAYS use Pydantic schemas for validation
- ✅ ALWAYS use dependency injection
- ✅ ALWAYS handle exceptions with HTTPException
SECRET FILES
- .env — DATABASE_URL, REDIS_URL, SECRET_KEY
- docker-compose.yml
### Mobile (Kotlin Multiplatform)
```markdown# Project: Banking App (KMP)
## TECH STACK- Kotlin Multiplatform (Android + iOS)- Compose Multiplatform for UI- Ktor client for networking- SQLDelight for database- Koin for dependency injection- Kotlin Coroutines + Flow
## CONVENTIONS### Structureshared/ ├── commonMain/ │ ├── kotlin/ │ │ ├── data/ │ │ ├── domain/ │ │ └── presentation/ │ └── sqldelight/ ├── androidMain/ └── iosMain/ androidApp/ # Android UI + platform code iosApp/ # iOS UI + platform code
### expect/actual Pattern```kotlin// CORRECT ✅ — commonMainexpect class PlatformSecureStorage { suspend fun save(key: String, value: String) suspend fun get(key: String): String?}
// androidMainactual class PlatformSecureStorage { actual suspend fun save(key: String, value: String) { // Android Keystore implementation }}
// iosMainactual class PlatformSecureStorage { actual suspend fun save(key: String, value: String) { // iOS Keychain implementation }}State Management
// CORRECT ✅ — Sealed class for all statessealed interface TransactionUiState { object Loading : TransactionUiState data class Success(val transactions: List<Transaction>) : TransactionUiState data class Error(val message: String) : TransactionUiState}
class TransactionViewModel( private val repo: TransactionRepository) : ViewModel() { private val _state = MutableStateFlow<TransactionUiState>(TransactionUiState.Loading) val state: StateFlow<TransactionUiState> = _state.asStateFlow()}
// WRONG ❌ — Mutable state exposedclass TransactionViewModel : ViewModel() { var transactions: List<Transaction>? = null // nullable, mutable var isLoading = false}CRITICAL RULES
- ❌ NEVER put platform-specific code in commonMain
- ❌ NEVER expose mutable state (always use StateFlow)
- ❌ NEVER skip sealed classes for state
- ✅ ALWAYS use expect/actual for platform APIs
- ✅ ALWAYS keep business logic in commonMain
- ✅ ALWAYS inject dependencies via Koin
SECRET FILES
- Android: local.properties, google-services.json, keystore.jks
- iOS: GoogleService-Info.plist, Config.xcconfig
### Go Backend Service
```markdown# Project: Payment Gateway
## TECH STACK- Go 1.21- Gin framework- GORM + PostgreSQL- Redis for caching- gRPC for internal services
## CONVENTIONS### Structurecmd/ └── server/ └── main.go internal/ ├── handlers/ # HTTP handlers ├── services/ # Business logic ├── repositories/ # Data access ├── models/ # Domain models └── config/ # Configuration pkg/ # Reusable packages
### Handler Pattern```go// CORRECT ✅func (h *PaymentHandler) CreatePayment(c *gin.Context) { var req CreatePaymentRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return }
payment, err := h.service.CreatePayment(c.Request.Context(), req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return }
c.JSON(http.StatusCreated, payment)}
// WRONG ❌ — No error handlingfunc (h *PaymentHandler) CreatePayment(c *gin.Context) { var req CreatePaymentRequest c.BindJSON(&req) // No error check payment, _ := h.service.CreatePayment(c.Request.Context(), req) // Ignoring error c.JSON(200, payment)}Error Handling
// CORRECT ✅ — Always check errorspayment, err := repo.GetByID(ctx, id)if err != nil { if errors.Is(err, ErrNotFound) { return nil, ErrPaymentNotFound } return nil, fmt.Errorf("failed to get payment: %w", err)}
// WRONG ❌payment, _ := repo.GetByID(ctx, id) // Ignoring errorsCRITICAL RULES
- ❌ NEVER ignore errors (always check and handle)
- ❌ NEVER use panic in production code (return errors)
- ❌ NEVER expose internal errors to clients (wrap with public error types)
- ✅ ALWAYS pass context.Context as first parameter
- ✅ ALWAYS use pointer receivers for methods
- ✅ ALWAYS validate input before processing
SECRET FILES
- .env — DATABASE_URL, REDIS_URL, JWT_SECRET
- config.yaml
---
## Sai Lầm Thường Gặp Khi Viết CLAUDE.md
Sau khi review CLAUDE.md của 50+ developers, đây là những sai lầm tôi thấy nhiều nhất:
### 1. Quá chung chung
❌ **Wrong:**```markdown## Tech Stack- JavaScript- Database- Frontend framework✅ Correct:
## Tech Stack- Next.js 14.1.0 + TypeScript 5.3- PostgreSQL 15 + Prisma ORM 5.x- React 18 + TailwindCSS 3.4Chính xác > chung chung. Claude Code cần biết versions để không suggest deprecated APIs.
2. Quá dài, không có structure
❌ Wrong:
This project uses React and we prefer functional components and we use TypeScript and we use Prettier with 2 spaces and we don't like semicolons and we use Redux for state management but only for global state and local state should use useState or useReducer and...Một đoạn text dài 200 chữ không có headings, không có bullet points, không có code examples.
✅ Correct: Chia thành sections với headings. Dùng bullets. Dùng code examples.
3. Không có examples
❌ Wrong:
## Rules- Use sealed classes for state- Follow repository pattern- Add testsNghe hay nhưng vague. “Sealed classes for state” trông như thế nào?
✅ Correct:
## State Management Pattern```kotlin// ✅ CORRECTsealed interface UiState { object Loading : UiState data class Success(val data: Data) : UiState data class Error(val message: String) : UiState}Một code example thắng vạn lời.
### 4. Không update theo thời gian
Nhiều người viết CLAUDE.md một lần rồi bỏ quên. Project evolve, conventions thay đổi, nhưng CLAUDE.md không.
**Solution:** Mỗi khi Claude Code generate code sai convention, thêm correction vào CLAUDE.md ngay. Treat nó như living document.
### 5. Quên section "Secret Files"
❌ **Missing:**Không mention files nào Claude Code không được access.
✅ **Correct:**```markdown## SECRET FILES (never access)- .env, .env.local, .env.production- docker-compose.yml (has database passwords)- local.properties (Android API keys)- GoogleService-Info.plist (iOS Firebase)- credentials.json (GCP service account)Đây là critical security practice. Xem chi tiết ở Phase 2: Security.
Hiệu Ứng Tích Lũy: CLAUDE.md Theo Thời Gian
Điều nhiều người bỏ lỡ: CLAUDE.md compound theo thời gian. Nó không phải viết một lần xong. Nó là living document.
Timeline thực tế của tôi:
Week 1: 15 dòng
# SocialApp Android## Stack- Kotlin, Jetpack Compose, MVVM## Rules- Don't edit build.gradle.kts- Use StateFlow not LiveDataQuality improvement: ~30%. Claude Code vẫn sai một số convention nhưng ít hơn.
Week 2: 25 dòng (thêm naming conventions)
## Naming- ViewModels: [Feature]ViewModel- Screens: [Feature]Screen- UseCases: [Action][Entity]UseCaseQuality improvement: ~50%. Claude Code bắt đầu generate code gần như copy-paste ready.
Month 1: 40 dòng (thêm code examples cho common patterns)
## ViewModel Pattern```kotlinsealed interface UiState { ... }Quality improvement: ~70%. Tôi chỉ cần sửa minor details.
**Month 2:** 60 dòng (thêm testing patterns, error handling)```markdown## TestingTest files mirror source: `src/test/java/.../FeatureTest.kt`Always use `runTest` for coroutinesUse MockK for mocking
## Error HandlingAlways wrap repository calls with try-catchMap domain exceptions to UI error messagesQuality improvement: ~85%. Hầu như không cần sửa gì nữa.
Month 3: 80 dòng (refine dựa trên edge cases)
## Edge Cases- Empty states: Show EmptyStateMessage component- Loading during pagination: Show footer loader, not full screen- Network errors: Show retry button with specific error messageQuality improvement: 95%+. Claude Code giờ generate code tốt hơn cả một số junior developers trong team.
Nâng Cao: CLAUDE.md Cho Team
Nếu cả team dùng Claude Code, shared CLAUDE.md trở thành single source of truth cho conventions.
Team CLAUDE.md Template
# [Project Name] — Team Conventions
## OVERVIEW- **Team size:** [X developers]- **Git workflow:** [Feature branches / Trunk-based]- **Code review:** [Required reviewers, approval policy]
## CONVENTIONS[Shared conventions như bình thường]
## COMMIT MESSAGESFormat: type: description
Types:
- feat: New feature
- fix: Bug fix
- refactor: Code refactoring
- test: Add tests
- docs: Documentation
- chore: Build/tooling
Examples:
- feat(auth): add biometric login
- fix(booking): handle null dates
- refactor(payment): extract Stripe service
## CODE REVIEW CHECKLIST- [ ] Tests pass- [ ] No hardcoded secrets- [ ] Follows naming conventions- [ ] KDoc added for public APIs- [ ] No TODO comments without tracking ticket
## TEAM PREFERENCES- **Comment language:** Vietnamese for business logic, English for technical- **PR size:** Max 400 lines changed (split larger features)- **Merge strategy:** Squash and merge- **Branch naming:** `feature/JIRA-123-short-description`Tất cả trong team reference cùng một CLAUDE.md. Khi onboard member mới, họ chỉ cần đọc file này + CLAUDE.md sẽ giúp họ generate code đúng convention từ ngày đầu.
The Bottom Line
Tôi dành 3 tháng sửa code mà Claude Code generate sai convention trước khi phát hiện ra CLAUDE.md. Giờ thì tôi bắt đầu mỗi project mới với CLAUDE.md trước cả dòng code đầu tiên.
5 minutes viết CLAUDE.md = tiết kiệm hàng giờ mỗi tuần.
Đây là single highest-leverage thing bạn có thể làm cho AI-assisted development workflow. Không close second.
Bắt đầu nhỏ:
- Tạo file
CLAUDE.mdở root project - Viết 3 sections: Overview, Tech Stack, Critical Rules
- Thêm 1-2 code examples cho patterns quan trọng nhất
- Ship it
Thêm dần theo thời gian. Mỗi khi Claude Code generate code sai, thêm correction vào CLAUDE.md. Sau một tháng, bạn sẽ có AI assistant hiểu project của bạn tốt như bất kỳ team member nào.
Muốn đi sâu hơn? Khóa học Claude Code Mastery có cả module dedicated về CLAUDE.md system (Phase 4) với hands-on exercises và 20+ templates cho mọi loại project. Phase 1-3 miễn phí.
Nhận Claude Code Cheat Sheet miễn phí — 50+ commands trong single PDF — khi đăng ký newsletter.