| name | zai-best-practices |
| description | Code quality guidelines and best practices for writing clean, maintainable code. Covers naming, structure, error handling, testing, and code review standards. Use when writing code, reviewing, refactoring, or asking "how should I name this", "best practice for", "clean code". |
| argument-hint | [naming|structure|errors|testing|review] |
| allowed-tools | Read Glob Grep |
| disable-model-invocation | false |
Language and Coding Standards
- Communication: Always talk in Thai when interacting with users.
- Code & Technical Assets: All code, comments, documentation, and technical definitions must be in English.
Best Practices Guide
ZeaZ Platform & apps/* Monorepo Rules
When implementing tasks on the zeaz-platform repository, you MUST strictly enforce these architecture and workflow rules:
- Monorepo Architecture (apps/*): The platform is a unified monorepo. ALL applications, microservices, frontends, and AI toolings (e.g., zLinebot, zwallet, zdash) reside inside the
apps/ directory. Do not create top-level directories for apps. When refactoring or adding features, always scope your work to the specific apps/<app-name>/ folder.
- Environment Variables: Avoid scattering
.env files. Consolidate environment variables into a central .env.example inside the respective app folder. Canonical Cloudflare variables (e.g. CLOUDFLARE_API_TOKEN, CLOUDFLARE_ZONE_ID) MUST be used instead of legacy CF_ variants.
- Commit Workflow: NEVER use
git commit or git push directly. ALWAYS stage your intended files with git add and commit using make gpg-finalize COMMIT_MSG="..." from the repository root to ensure all GitOps and DevSecOps checks pass.
- Security: NEVER commit or generate real secrets. Unsafe placeholders like
test-secret-value-value-value, test-secret-value-value-value, test-secret-value-value-value are FORBIDDEN.
- Language: Code, documentation, and technical definitions MUST be in English.
Quick Reference
/zai-best-practices — Full overview
/zai-best-practices naming — Naming conventions
/zai-best-practices structure — Code organization
/zai-best-practices errors — Error handling
/zai-best-practices testing — Testing practices
/zai-best-practices review — Code review checklist
Naming Conventions
Variables & Functions
✅ Good ❌ Bad
─────────────────────────────────────────────
getUserById(id) getUser(i)
isValidEmail checkEmail
maxRetryCount max
calculateTotalPrice calc
handleSubmit submit
Rules:
- Use descriptive names that reveal intent
- Avoid abbreviations (except universally known:
id, url, api)
- Boolean variables:
is, has, can, should prefix
- Functions: verb + noun (
fetchUser, validateInput)
- Constants: SCREAMING_SNAKE_CASE
- Classes/Types: PascalCase
- Variables/functions: camelCase (JS/TS/PHP) or snake_case (Python/Rust)
Files & Directories
✅ Good ❌ Bad
─────────────────────────────────────────────
user-service.ts userService.ts (inconsistent)
UserRepository.ts user_repository.ts (mixed)
/components/Button/ /Components/button/
/services/auth/ /Services/Auth/
Rules:
- One convention per project (kebab-case or PascalCase for files)
- Directories: lowercase with hyphens
- Test files:
*.test.ts or *.spec.ts (consistent)
- Index files: only for re-exports, not logic
Code Structure
Function Design
function calculateDiscount(price: number, discountPercent: number): number {
if (discountPercent < 0 || discountPercent > 100) {
throw new Error('Discount must be between 0 and 100');
}
return price * (1 - discountPercent / 100);
}
function processOrder(order) {
validateOrder(order);
order.discount = getDiscount();
saveToDatabase(order);
sendEmail(order.user);
return order;
}
function calculateDiscount(float $price, float $discountPercent): float
{
if ($discountPercent < 0 || $discountPercent > 100) {
throw new InvalidArgumentException('Discount must be between 0 and 100');
}
return $price * (1 - $discountPercent / 100);
}
Rules:
- Single Responsibility: one function = one job
- Max 20-30 lines per function
- Max 3-4 parameters (use object for more)
- No side effects in pure functions
- Early returns for guard clauses
Module Organization
feature/
├── index.ts # Public exports only
├── types.ts # Types and interfaces
├── constants.ts # Constants
├── utils.ts # Pure utility functions
├── hooks.ts # React hooks (if applicable)
├── service.ts # Business logic
└── repository.ts # Data access
Rules:
- Group by feature, not by type
- Clear public API via index.ts
- Internal modules prefixed with
_ or in internal/
- Avoid circular dependencies
Error Handling
Do's and Don'ts
class UserNotFoundError extends Error {
constructor(userId: string) {
super(`User not found: ${userId}`);
this.name = 'UserNotFoundError';
}
}
async function getUser(id: string): Promise<User> {
const user = await db.users.find(id);
if (!user) {
throw new UserNotFoundError(id);
}
return user;
}
async function getUser(id) {
try {
return await db.users.find(id);
} catch (e) {
console.log(e);
return null;
}
}
Rules:
- Create specific error classes for domain errors
- Never swallow exceptions without logging
- Log errors with context (user ID, request ID, etc.)
- Use error boundaries at system edges
- Return Result types for expected failures (optional)
Error Messages
✅ Good: "Failed to create user: email 'test@example.com' already exists"
❌ Bad: "Error occurred"
❌ Bad: "Something went wrong"
Testing Practices
Test Structure (AAA Pattern)
describe('calculateDiscount', () => {
it('should apply percentage discount to price', () => {
const price = 100;
const discount = 20;
const result = calculateDiscount(price, discount);
expect(result).toBe(80);
});
it('should throw for invalid discount percentage', () => {
expect(() => calculateDiscount(100, -10)).toThrow();
expect(() => calculateDiscount(100, 150)).toThrow();
});
});
Rules:
- One assertion concept per test
- Descriptive test names: "should [expected behavior] when [condition]"
- Test behavior, not implementation
- Use factories/fixtures for test data
- Avoid testing private methods directly
Test Coverage Priorities
1. Critical business logic ████████████ Must have
2. Edge cases and boundaries ████████░░░░ Important
3. Integration points ██████░░░░░░ Important
4. Happy paths ████░░░░░░░░ Basic
5. UI components ██░░░░░░░░░░ Optional
Code Review Checklist
Before Requesting Review
Reviewer Checklist
Review Comments
✅ Good feedback:
"This could throw if `user` is null. Consider adding a null check
or using optional chaining: `user?.profile?.name`"
❌ Bad feedback:
"This is wrong"
"I don't like this"
"Why did you do it this way?"
Quick Rules Summary
| Area | Rule |
|---|
| Naming | Descriptive, consistent, reveals intent |
| Functions | Small, single purpose, no side effects |
| Errors | Specific types, never swallow, log context |
| Tests | AAA pattern, test behavior, descriptive names |
| Reviews | Be specific, suggest solutions, be kind |
Artifact Ownership and Config Policy
- Primary ownership: none. This skill is advisory and reference-only.
- Write policy: do not create or modify project artifacts by default.
- Config policy: config-agnostic by design. Follow repository context,
.ai-factory/ARCHITECTURE.md, and skill-context overrides instead of reading config.yaml.