Quy ước đặt tên team: chọn camelCase, PascalCase, snake_case hay kebab-case?【2026】
Không có case “đúng tuyệt đối” — chỉ có case đúng theo tầng và ngôn ngữ. JavaScript/TypeScript: biến & hàm = camelCase, class/component = PascalCase. Python & cột DB: snake_case. URL và class CSS: kebab-case. Hằng số / biến môi trường: UPPER_SNAKE. Khi đổi tầng (API ↔ FE), chuyển một lần ở biên — đừng để hai kiểu sống chung trong cùng file. Case converter giúp đổi nhanh trên trình duyệt, không cần đăng ký.
PR review của team outsource Việt Nam hay dừng vì chuyện “nhỏ”: userId vs user_id, UserCard vs user-card, API_KEY vs apiKey. Không phải ai cũng sai — họ đang đứng ở hai tầng khác nhau mà thiếu bảng quy ước chung.
Bài này giúp chọn case theo ngữ cảnh team, không chỉ định nghĩa từng kiểu chữ. Bạn sẽ có ma trận ngôn ngữ, ví dụ lỗi thật khi JSON lệch, checklist merge, và cách dùng tool chuyển case khi phải map hàng loạt.
Bài viết này giúp bạn
- Phân biệt camel / Pascal / snake / kebab / UPPER_SNAKE bằng ví dụ ngắn
- Có bảng quyết định theo tầng để dán vào README
- Biết chỗ chuyển case (biên API) thay vì đổi lung tung trong UI
- Tránh lỗi runtime do lệch key JSON
- Dùng case converter khi rename hoặc map payload
Năm quy ước hay gặp — ví dụ cùng một khái niệm
Cùng ý “tên người dùng”:
| Quy ước | Ví dụ | Đọc nhanh |
|---|---|---|
| camelCase | userName | từ đầu thường, từ sau viết hoa |
| PascalCase | UserName | mọi từ viết hoa đầu |
| snake_case | user_name | thường + gạch dưới |
| kebab-case | user-name | thường + gạch ngang |
| UPPER_SNAKE | USER_NAME | hằng số / env |
user_name → camel: userName
user_name → Pascal: UserName
user_name → kebab: user-name
user_name → UPPER_SNAKE: USER_NAME
Trên case converter: dán user_name, chọn định dạng đích, copy. Không cần viết script một lần cho năm dòng rename.
Ma trận team: tầng nào → case nào
| Tầng | Case mặc định | Ví dụ |
|---|---|---|
| Biến / hàm JS·TS | camelCase | orderTotal, fetchUser() |
| Class / React component / type | PascalCase | OrderService, <UserCard /> |
| Python / Ruby | snake_case | order_total, fetch_user |
| Cột SQL / JSON backend nhiều framework | snake_case | created_at, user_id |
| URL slug / CSS class / một số filename | kebab-case | /user-profile, .btn-primary |
| Env / hằng số toàn cục | UPPER_SNAKE | DATABASE_URL, MAX_RETRY |
Vì sao không “thống nhất một case cho cả monorepo”?
Vì parser và hệ sinh thái đã chọn giúp bạn:
user-namekhông hợp lệ làm identifier JavaScript.- React coi
userCardkhác<UserCard />— Pascal báo hiệu component. - PostgreSQL identifier không quote thường fold về chữ thường; snake dễ đọc trong migration.
- SEO và routing quen slug kebab (
/bai-viet-moi), không phải/baiVietMoi.
Team fullstack (Next.js + Django/FastAPI) phải sống với hai thế giới. Mục tiêu là rõ biên, không phải ép Python viết camel.
Quyết định nhanh theo tình huống
1. “File React đặt tên thế nào?”
- Component export: PascalCase —
InvoiceTable.tsx,export function InvoiceTable. - Hook: camel bắt đầu bằng
use—useInvoiceList. - Utilities: camel —
formatVndAmount. - Route URL: kebab —
/hoa-don/[id](slug), trong khi component vẫn Pascal.
2. “API trả user_id mà FE đang dùng TypeScript?”
Giữ snake trên dây nếu backend đã public contract; map sang camel một lần ở lớp client:
// Biên API — chuyển một chỗ, không rải khắp UI
type UserDto = { user_id: string; full_name: string };
type User = { userId: string; fullName: string };
function mapUser(dto: UserDto): User {
return { userId: dto.user_id, fullName: dto.full_name };
}
Khi đặt tên field mới từ ticket tiếng Việt/Anh, mở case converter, convert full_name → fullName để tránh typo full_Name.
3. “CSS và Tailwind?”
- Class tự viết: kebab (
.nav-link). - Token JS object (theme): camel phổ biến (
colors.primarySoft) hoặc đúng convention của design system bạn đang dùng. - Đừng copy class HTML
user-namethành biếnuser-nametrong JS.
4. “Biến môi trường và CI?”
Luôn UPPER_SNAKE: NEXT_PUBLIC_API_BASE, POSTGRES_PASSWORD. Nhiều platform chỉ inject env dạng này. Trong code đọc process.env.POSTGRES_PASSWORD, không invent postgresPassword cho tên biến môi trường.
Case study: bug “undefined” vì lệch case
Bối cảnh: Team 4 người (2 FE Việt, 1 BE Việt, 1 BA). Backend FastAPI trả:
{ "order_id": "A-1024", "total_amount": 250000 }
FE viết:
console.log(data.orderId); // undefined
Staging “thỉnh thoảng đúng” vì có mock MSW dùng camel. Production vỡ sau khi nối API thật.
Root cause: Không có bảng quy ước + không có mapper biên. Mock và API nói hai ngôn ngữ.
Cách chốt cho team:
- README ghi: JSON API = snake_case; model UI = camelCase; map tại
src/api/mappers/. - PR template checkbox: “Payload mới đã có mapper / type DTO”.
- Khi BE thêm field
discount_rate, FE convert tên bằng tool rồi thêm vào mapper — 30 giây, ít typo hơn đổi tay.
Checklist quy ước cho README (copy được)
Dán và sửa cho stack của bạn:
## Naming conventions
| Layer | Convention | Example |
|-------|------------|---------|
| TS variables / functions | camelCase | `invoiceTotal` |
| React components / classes | PascalCase | `InvoiceTotal` |
| Python modules / functions | snake_case | `invoice_total` |
| DB columns / API JSON | snake_case | `invoice_total` |
| URLs / CSS classes | kebab-case | `invoice-total` |
| ENV / constants | UPPER_SNAKE | `INVOICE_TOTAL_CAP` |
- Do not mix camel and snake in the same TS file for the same concept.
- Convert at API boundary only.
Reviewer chỉ cần hỏi: “Field này thuộc tầng nào?” — tranh luận kết thúc nhanh.
Lỗi thường gặp khi làm việc nhóm
- Đổi case trong UI vì “trông đẹp hơn” — phá contract API và diff reviewer.
- Pascal cho mọi thứ —
Const Config = …rồi nhầm với component. - kebab trong object JS — phải ghi
obj['user-name'], dễ quên. - UPPER_SNAKE cho biến cục bộ — khó đọc; giữ UPPER cho hằng/env thật sự toàn cục.
- Tự viết script convert thiếu edge case (
XMLHttpRequest,userID) — với danh sách ngắn, converter trên trình duyệt đủ và an toàn hơn (không gửi tên nội bộ lên server lạ).
Workflow đổi tên hàng loạt với case converter
- Mở Chuyển đổi kiểu chữ.
- Dán một tên hoặc danh sách tên (ví dụ cột export từ DB).
- Chọn đích: camelCase, snake_case, kebab-case, PascalCase, UPPER_SNAKE…
- Copy kết quả vào mapper, migration note, hoặc commit rename.
Phù hợp khi:
- Nhận OpenAPI snake và cần đặt tên type camel
- Đổi folder Vue/React sang kebab theo lint mới
- Chuẩn hóa tên file trước khi mở PR lớn
Không thay ESLint/ Presetter — tool này là bước thủ công nhanh khi bạn đang quyết định hoặc map biên.
Khi nào được phép “lệch” quy ước?
- Thư viện bên thứ ba bắt buộc tên field (Stripe, Firebase…) — giữ nguyên ở biên SDK, map vào model nội bộ.
- Legacy DB đã freeze — đừng rename cột production chỉ vì thẩm mỹ; map ở repository.
- Design token từ Figma đã đặt sẵn — thống nhất một chiều (FE theo token hoặc token theo FE), ghi rõ trong handoff.
Ngoại lệ phải có chỗ ghi, không phải “tuỳ người merge”.
Tóm tắt quyết định một dòng
- Viết JS/TS hàng ngày? → camel cho giá trị, Pascal cho kiểu/component.
- Viết Python/SQL? → snake.
- Đặt URL hoặc class CSS? → kebab.
- Env/CI? → UPPER_SNAKE.
- Hai tầng nói chuyện? → một mapper + case converter khi đặt tên.