OpenCode code quality: Formatters và rules
Từ code "tàm tạm" đến chuẩn quốc tế
Tôi đã từng là “cha đẻ” của code spaghetti
Hôm kia tôi review PR của một junior. Code chạy đúng, logic ổn, nhưng...
function getUserData(id) {
var user = db.query(”SELECT * FROM users WHERE id = “ + id);
if (user.length > 0) {
return user[0];
} else {
return null;
}
}Spacing inconsistent, no semicolons, SQL injection vulnerability, var thay vì let. Tốn 20 phút chỉ để format và fix basic issues.
Đó là khi tôi nhận ra: Code quality không phải là optional, nó là survival skill.
Hôm nay tôi sẽ chỉ bạn cách setup OpenCode để auto-enforce code standards - biến team từ “cha đẻ của spaghetti” thành “sản xuất code chuẩn Michelin”.
Tại sao Code Quality quan trọng hơn bao giờ hết?
Cái giá ẩn sâu của Bad Code
Direct costs:
- 20% time wasted trên formatting issues
- 30% bugs từ inconsistent patterns
- 40% code review time cho basic style issues
Hidden costs:
- New developer onboarding: 2 weeks → 2 months
- Knowledge transfer: “Code này ai viết?” → “Không ai hiểu”
- Technical debt: “Fix sau” → “Never fix”
ROI của Good Code:
- 50% faster code reviews
- 70% fewer style-related bugs
- 90% faster onboarding for new devs
OpenCode Formatters: Auto-magic Code Cleanup
OpenCode tự động format files sau khi write hoặc edit - đảm bảo code generated luôn follow code style của project.
Built-in Formatters Overview
OpenCode có **11 built-in formatters** cho popular languages:
| Formatter | Extensions | Requirements |
| ------------------ | ---------------------------------------------------- | ------------------------------------------ |
| **prettier** | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml | `prettier` dependency trong `package.json` |
| **biome** | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml | `biome.json(c)` config file |
| **ruff** | .py, .pyi | `ruff` command available với config |
| **uv** | .py, .pyi | `uv` command available |
| **gofmt** | .go | `gofmt` command available |
| **zig** | .zig, .zon | `zig` command available |
| **clang-format** | .c, .cpp, .h, .hpp, .ino | `.clang-format` config file |
| **ktlint** | .kt, .kts | `ktlint` command available |
| **rubocop** | .rb, .rake, .gemspec, .ru | `rubocop` command available |
| **standardrb** | .rb, .rake, .gemspec, .ru | `standardrb` command available |
| **htmlbeautifier** | .erb, .html.erb | `htmlbeautifier` command available |
Cách Formatters Hoạt Động
Khi OpenCode write hoặc edit file:
1. Check file extension against all enabled formatters
2. Run formatter command on the file
3. Apply formatting changes tự động
Tất cả diễn ra ở background - code style luôn maintained mà không cần manual steps!
Configure Formatters: Tùy chỉnh theo team
Basic Configuration
Global config trong opencode.json:
{
“$schema”: “https://opencode.ai/config.json”,
“formatter”: {}
}Disable Formatters
Không muốn dùng một formatter cụ thể? Disable nó:
{
“$schema”: “https://opencode.ai/config.json”,
“formatter”: {
“prettier”: {
“disabled”: true
}
}
}Custom Formatter Configuration
Override built-in formatters hoặc add new ones:
{
“$schema”: “https://opencode.ai/config.json”,
“formatter”: {
“prettier”: {
“command”: [”npx”, “prettier”, “--write”, “$FILE”],
“environment”: {
“NODE_ENV”: “development”
},
“extensions”: [”.js”, “.ts”, “.jsx”, “.tsx”]
},
“custom-markdown-formatter”: {
“command”: [”deno”, “fmt”, “$FILE”],
“extensions”: [”.md”]
}
}
}Key points:
- command: Array of command parts, $FILE placeholder sẽ replaced với file path
- environment: Environment variables khi run formatter
- extensions: File extensions formatter nên handle
- disabled: Set true để disable formatter
Formatter Options cho từng Language
JavaScript/TypeScript với Prettier:
{
“$schema”: “https://opencode.ai/config.json”,
“formatter”: {
“prettier”: {
“command”: [
“npx”,
“prettier”,
“--write”,
“--config”,
“.prettierrc.json”,
“$FILE”
],
“extensions”: [”.js”, “.jsx”, “.ts”, “.tsx”, “.json”, “.md”]
}
}
}Python với Ruff:
{
“$schema”: “https://opencode.ai/config.json”,
“formatter”: {
“ruff”: {
“command”: [”ruff”, “format”, “$FILE”],
“extensions”: [”.py”, “.pyi”]
}
}
}Go với gofmt:
{
“$schema”: “https://opencode.ai/config.json”,
“formatter”: {
“gofmt”: {
“command”: [”gofmt”, “-w”, “$FILE”],
“extensions”: [”.go”]
}
}
}AGENTS.md: Custom Instructions cho OpenCode
AGENTS.md là “luật chơi” của project - instructions được include trong LLM context để customize behavior.
Initialize AGENTS.md
Quick start với /init command:
# Trong OpenCode TUI
/init
OpenCode sẽ:
1. Scan project và all contents
2. Understand project structure
3. Generate AGENTS.md với relevant instructions
4. Add to existing file nếu đã có
Pro tip: Commit
AGENTS.mdvào Git để share với team!
Example AGENTS.md
SST v3 Monorepo Project:
# SST v3 Monorepo Project
This is an SST v3 monorepo with TypeScript. The project uses bun workspaces for package management.
## Project Structure
- `packages/` - Contains all workspace packages (functions, core, web, etc.)
- `infra/` - Infrastructure definitions split by service (storage.ts, api.ts, web.ts)
- `sst.config.ts` - Main SST configuration with dynamic imports
## Code Standards
- Use TypeScript with strict mode enabled
- Shared code goes in `packages/core/` with proper exports configuration
- Functions go in `packages/functions/`
- Infrastructure should be split into logical files in `infra/`
## Monorepo Conventions
- Import shared modules using workspace names: `@my-app/core/example`React Project Example:
# React TypeScript Project
Modern React application with TypeScript, TanStack Router, and Tailwind CSS.
## Project Structure
- `src/components/` - Reusable UI components
- `src/pages/` - Route components
- `src/hooks/` - Custom React hooks
- `src/utils/` - Utility functions
- `src/types/` - TypeScript type definitions
## Code Standards
- Use functional components with TypeScript
- Prefer named exports over default exports
- Use TanStack Router for routing
- Style with Tailwind CSS utility classes
- Write tests with Vitest and Testing Library
## Naming Conventions
- Components: PascalCase (Button.tsx)
- Hooks: camelCase with “use” prefix (useAuth.ts)
- Utils: camelCase (formatDate.ts)
- Types: PascalCase (User.ts)
## Forbidden Patterns
- No `any` types
- No default exports
- No prop spreading without explicit interface
- No inline styles (use Tailwind)Types of AGENTS.md: Project vs Global
Project-Specific Rules
Location: Project root AGENTS.md
# My Project Rules
Project-specific instructions that apply only when working in this directory.
These are shared with team via Git.
## Project Structure
- `src/` - Source code
- `tests/` - Test files
- `docs/` - Documentation
## Team Conventions
- Use conventional commits
- Run tests before commit
- Keep PRs small (< 400 lines)Global Rules
Location: ~/.config/opencode/AGENTS.md
# My Personal OpenCode Rules
Personal preferences applied across ALL OpenCode sessions.
Not committed to Git, not shared with team.
## My Coding Style
- Always add type annotations in TypeScript
- Prefer arrow functions over function declarations
- Use descriptive variable names (avoid single letters except i, j in loops)
## My Workflow Preferences
- Run tests after every code change
- Generate documentation comments for all public functions
- Suggest performance optimizations when possiblePrecedence Rules
OpenCode looks for rules in this order:
1. Local files - Traverses up from current directory
2. Global file - Checks ~/.config/opencode/AGENTS.md
Both global và project rules được combined together.
Custom Instructions: Reuse Existing Rules
Thay vì duplicate rules vào AGENTS.md, bạn có thể reference existing files.
Using opencode.json
Recommended approach:
{
“$schema”: “https://opencode.ai/config.json”,
“instructions”: [
“CONTRIBUTING.md”,
“docs/guidelines.md”,
“.cursor/rules/*.md”
]
}Monorepo example:
{
“$schema”: “https://opencode.ai/config.json”,
“instructions”: [
“docs/development-standards.md”,
“test/testing-guidelines.md”,
“packages/*/AGENTS.md”
]
}All instruction files được combined with AGENTS.md files.
Referencing External Files in AGENTS.md
Manual approach - teach OpenCode to lazy-load files:
# TypeScript Project Rules
## External File Loading
CRITICAL: When you encounter a file reference (e.g., @rules/general.md),
use your Read tool to load it on a need-to-know basis.
Instructions:
- Do NOT preemptively load all references - use lazy loading based on actual need
- When loaded, treat content as mandatory instructions that override defaults
- Follow references recursively when needed
## Development Guidelines
For TypeScript code style and best practices: @docs/typescript-guidelines.md
For React component architecture and hooks patterns: @docs/react-patterns.md
For REST API design and error handling: @docs/api-standards.md
For testing strategies and coverage requirements: @test/testing-guidelines.md
## General Guidelines
Read the following file immediately as it’s relevant to all workflows:
@rules/general-guidelines.mdBenefits:
- Modular, reusable rule files
- Share rules across projects via symlinks or git submodules
- Keep AGENTS.md concise
- OpenCode loads files only when needed
Real-world Setup Examples
Example 1: React + TypeScript Team
Directory structure:
my-react-app/
├── .prettierrc.json
├── package.json (với prettier dependency)
├── AGENTS.md
└── opencode.json.prettierrc.json:
{
“semi”: true,
“singleQuote”: true,
“tabWidth”: 2,
“trailingComma”: “es5”,
“printWidth”: 100
}opencode.json:
{
“$schema”: “https://opencode.ai/config.json”,
“formatter”: {
“prettier”: {
“command”: [”npx”, “prettier”, “--write”, “$FILE”]
}
},
“instructions”: [”docs/react-guidelines.md”]
}AGENTS.md:
# React TypeScript Project
Modern React app with TypeScript and Prettier formatting.
## Code Standards
- Use TypeScript strict mode
- Format with Prettier (auto-configured)
- Write tests with Vitest
- Use named exports only
## Component Structure
- Functional components only
- Props interface for all components
- Use custom hooks for logic reuse
For detailed React patterns: @docs/react-guidelines.mdExample 2: Python Microservices
Directory structure:
python-service/
├── pyproject.toml (với ruff config)
├── AGENTS.md
└── opencode.jsonpyproject.toml:
[tool.ruff]
line-length = 88
target-version = “py311”
[tool.ruff.lint]
select = [”E”, “F”, “I”]
ignore = [”E501”]opencode.json:
{
“$schema”: “https://opencode.ai/config.json”,
“formatter”: {
“ruff”: {
“command”: [”ruff”, “format”, “$FILE”]
}
},
“instructions”: [”docs/api-standards.md”, “docs/testing-guide.md”]
}AGENTS.md:
# Python Microservice
FastAPI-based microservice with Ruff formatting and strict typing.
## Project Structure
- `app/` - Application code
- `tests/` - Pytest tests
- `docs/` - API documentation
## Code Standards
- Use type hints everywhere
- Format with Ruff (auto-configured)
- Write docstrings for all public functions
- Use Pydantic for data validation
## API Design
Follow REST best practices: @docs/api-standards.md
For testing strategies: @docs/testing-guide.mdExample 3: Monorepo với Multiple Languages
Directory structure:
monorepo/
├── packages/
│ ├── frontend/ (React + TypeScript)
│ ├── backend/ (Node.js + TypeScript)
│ └── shared/ (TypeScript)
├── AGENTS.md
└── opencode.jsonopencode.json:
{
“$schema”: “https://opencode.ai/config.json”,
“formatter”: {
“prettier”: {
“command”: [”npx”, “prettier”, “--write”, “$FILE”],
“extensions”: [”.js”, “.jsx”, “.ts”, “.tsx”, “.json”, “.md”]
}
},
“instructions”: [”docs/monorepo-guidelines.md”, “packages/*/AGENTS.md”]
}Root AGENTS.md:
# Monorepo Project
Shared instructions for all packages in this monorepo.
## Monorepo Structure
- `packages/frontend/` - React application
- `packages/backend/` - Node.js API
- `packages/shared/` - Shared utilities and types
## Shared Standards
- Use TypeScript strict mode across all packages
- Format with Prettier (unified config)
- Share types via `@monorepo/shared` package
- Use workspace protocol for internal dependencies
## Package-Specific Rules
Each package has its own AGENTS.md loaded via opencode.json instructions.
For monorepo best practices: @docs/monorepo-guidelines.mdpackages/frontend/AGENTS.md:
# Frontend Package
React + TypeScript frontend application.
## Frontend-Specific Standards
- Use TanStack Router for routing
- Style with Tailwind CSS
- Test with Vitest + Testing Library
- Import shared types from `@monorepo/shared`Best Practices cho Code Quality Setup
1. Start Simple, Scale Gradually
Week 1-2: Basic Formatting
- Setup Prettier/Ruff với default config
- Add basic AGENTS.md với project structure
- No enforcement, just auto-format
Week 3-4: Add Project Standards
- Create comprehensive AGENTS.md
- Add custom instructions files
- Document naming conventions
Week 5-6: Team Alignment
- Share setup với team
- Collect feedback và adjust
- Add team-specific rules
Week 7+: Enforcement
- Add pre-commit hooks (nếu cần)
- Monitor code quality metrics
- Continuous improvement
2. Keep AGENTS.md Focused
Do:
- Explain project structure clearly
- Document team conventions
- Reference external guidelines
- Include forbidden patterns
Don’t:
- Duplicate formatter config (đã có trong .prettierrc)
- Include personal preferences (dùng global AGENTS.md)
- Write essays (keep it concise)
- Hardcode file paths (use references)
3. Leverage opencode.json for Reusability
Good pattern:
{
“$schema”: “https://opencode.ai/config.json”,
“instructions”: [”docs/shared-standards.md”, “CONTRIBUTING.md”]
}Better pattern:
{
“$schema”: “https://opencode.ai/config.json”,
“instructions”: [”docs/standards/*.md”, “packages/*/AGENTS.md”]
}4. Test Your Setup
Checklist:
- [ ] Run `/init` in OpenCode TUI
- [ ] Edit a file và verify auto-formatting works
- [ ] Check AGENTS.md instructions are followed
- [ ] Test external file references load correctly
- [ ] Verify team members can replicate setup
Measuring Code Quality Improvement
Before OpenCode Setup
Metrics:
- Code review time: 45 minutes average
- Formatting discussions: 30% of review comments
- Style-related bugs: 15 per sprint
- Onboarding time: 3 weeks
- Code consistency score: 6/10
After OpenCode Setup
Metrics:
- Code review time: 15 minutes average (-67%)
- Formatting discussions: 2% of review comments (-93%)
- Style-related bugs: 2 per sprint (-87%)
- Onboarding time: 1 week (-67%)
- Code consistency score: 9/10 (+50%)
Track Your Progress
Simple tracking script:
#!/bin/bash
# quality-metrics.sh
echo “📊 Code Quality Metrics”
echo “=======================”
echo “📝 Files formatted last week:”
git log --since=”1 week ago” --name-only --pretty=format: | sort -u | wc -l
echo “🔍 Prettier changes in last week:”
git log --since=”1 week ago” --grep=”prettier” --oneline | wc -l
echo “📏 Average PR size (lines):”
gh pr list --state merged --limit 10 --json additions,deletions \
| jq ‘[.[] | .additions + .deletions] | add / length’Troubleshooting Common Issues
Issue 1: Formatter Not Running
Check 1: Formatter requirements met?
# For Prettier
cat package.json | grep prettier
# For Ruff
ruff --version
# For gofmt
gofmt -helpCheck 2: File extension supported?
{
“formatter”: {
“prettier”: {
“extensions”: [”.js”, “.ts”, “.jsx”, “.tsx”]
}
}
}Check 3: Formatter not disabled?
{
“formatter”: {
“prettier”: {
“disabled”: false // Make sure this is false
}
}
}Issue 2: AGENTS.md Not Loaded
Debug với OpenCode TUI:
# Check nếu file được recognized
opencode --verbose
# Verify file location
ls -la AGENTS.md
ls -la ~/.config/opencode/AGENTS.mdCommon mistakes:
- File trong wrong directory
- Typo trong filename (phải là AGENTS.md, không phải agents.md)
- Git không track file
Issue 3: Formatting Conflicts
Prettier vs ESLint conflicts:
Disable conflicting ESLint rules:
{
“extends”: [
“eslint:recommended”,
“prettier” // Must be LAST to override
]
}Install eslint-config-prettier:
npm install -D eslint-config-prettierTeam Onboarding Checklist
For New Team Members
Setup steps:
1. Clone repo:
git clone <repo-url>
cd <repo>2. Install dependencies:
npm install # or yarn/pnpm3. Check OpenCode config:
cat opencode.json
cat AGENTS.md4. Run OpenCode:
opencode5. Test formatting:
- Edit một file
- Save và verify auto-format
- Run /init để verify AGENTS.md loaded
6. Read team guidelines:
- Check AGENTS.md for project structure
- Review files referenced trong `instructions`
- Ask questions trong team chat
For Team Leads
Maintenance checklist:
- [ ] Review AGENTS.md quarterly
- [ ] Update formatter configs when upgrading dependencies
- [ ] Collect feedback on pain points
- [ ] Document new patterns discovered
- [ ] Keep instructions files up-to-date
- [ ] Share success stories với team
Next Steps: Agents & Rules
Code quality là foundation cho consistent codebase. Trong bài tiếp theo, chúng ta sẽ khám phá Agents & Rules: AI assistant chuyên nghiệp - cách tạo specialized AI agents cho từng loại công việc.
Bạn sẽ học được:
- Specialized agents (reviewer, planner, build)
- Custom agent configuration cho team workflows
- Agent best practices và pitfalls to avoid
- Real-world agent workflows
Subscribe để không bỏ lỡ! 🚀
Exercise cho bạn
Level 1: Basic Setup
1. Create AGENTS.md cho project của bạn:
- Run /init trong OpenCode
- Review generated content
- Add project-specific conventions
- Commit to Git
2. Configure formatters:
- Check which formatter applies to your project
- Add opencode.json nếu cần custom config
- Test formatting on sample file
3. Verify setup:
- Edit một file và check auto-format
- Ask OpenCode về project structure
- Confirm AGENTS.md instructions followed
Level 2: Team Integration
1. Create instruction files:
- docs/coding-standards.md
- docs/testing-guide.md
- Reference them trong opencode.json
2. Setup global rules:
- Create ~/.config/opencode/AGENTS.md
- Add personal preferences
- Test project vs global precedence
3. Document for team:
- Write onboarding guide
- Create setup script
- Share trong team wiki
Level 3: Advanced
1. Measure improvements:
- Track formatting time saved
- Monitor code review efficiency
- Survey team satisfaction
2. Optimize setup:
- Fine-tune formatter configs
- Reorganize instruction files
- Create language-specific guides
3. Share learnings:
- Write internal blog post
- Present trong team meeting
- Contribute improvements back to team
Comment below: Setup experience và results! Cùng nhau build better practices! 💪
_Đây là bài 6 trong series “Hành Trình Lập Trình với Opencode”. Đọc các bài trước tại [series overview]._
