Cập nhật lần cuối:
Hướng dẫn độ dài tên biến & hàm - Quy ước đặt tên trong lập trình
Trong lập trình, việc đặt tên là một trong những yếu tố quan trọng nhất ảnh hưởng đến khả năng đọc mã nguồn. Tên quá ngắn không truyền đạt được ý nghĩa; tên quá dài khiến mã nguồn trở nên rườm rà. Việc chọn độ dài phù hợp đòi hỏi sự đánh giá dựa trên phạm vi và độ phức tạp. Bài viết này đề cập đến hướng dẫn về độ dài đặt tên và quy ước riêng cho từng ngôn ngữ. Kiểm tra độ dài định danh của bạn với Bộ đếm ký tự.
Độ dài tên biến theo phạm vi
Độ dài phù hợp của tên biến tỷ lệ thuận với phạm vi của nó, và tỷ lệ nghịch với số lần nó được sử dụng. Nguyên tắc này được nêu trong tài liệu hướng dẫn phong cách của Go (bản hiện hành tính đến tháng 8 năm 2026) và cũng được trình bày trong cuốn "Clean Code" của Robert C. Martin. Cơ chế phía sau khá cụ thể: khi phạm vi rộng, dòng mã đang đọc thường cách xa nơi khai báo, nên một tên không tự giải thích buộc người đọc phải cuộn ngược lại chỗ khai báo để tra ý nghĩa. Ngược lại, trong vòng lặp 2–3 dòng, phần khai báo vẫn nằm trong tầm mắt nên i là đủ; đặt tên dài ở đó chỉ làm dòng mã kéo dài theo chiều ngang và đẩy phần logic quan trọng ra khỏi màn hình.
| Phạm vi | Độ dài khuyến nghị | Ví dụ | Lý do |
|---|---|---|---|
| Bộ đếm vòng lặp (1–3 dòng) | 1–2 ký tự | i, j, k | Quy ước được hiểu rộng rãi |
| Lambda / khối ngắn (≤5 dòng) | 3–8 ký tự | item, user, val | Ngữ cảnh làm rõ kiểu/vai trò |
| Biến cục bộ trong hàm | 8–15 ký tự | userName, totalPrice | Vai trò phải rõ ràng trong phạm vi hàm |
| Trường/thuộc tính của lớp | 10–20 ký tự | maxRetryCount, isAuthenticated | Được tham chiếu trong toàn bộ lớp |
| Biến toàn cục / hằng số | 15–25 ký tự | MAX_CONNECTION_TIMEOUT, DEFAULT_PAGE_SIZE | Phải rõ ràng trong toàn bộ mã nguồn |
Vì sao độ dài đặt tên thay đổi theo văn hóa của từng ngôn ngữ
Cùng một nguyên tắc "độ dài tỷ lệ thuận với phạm vi" lại cho ra kết quả rất khác nhau giữa các ngôn ngữ. Lý do không nằm ở khẩu vị của cộng đồng, mà ở chỗ mỗi ngôn ngữ có cơ chế phân định phạm vi và bổ nghĩa cho định danh khác nhau.
C không có namespace, nên tiền tố module trong tên hàm (tcp_v4_connect, ext4_read_inode) đóng vai trò thay thế namespace. Đó là lý do mã C thường có tên biến rất ngắn nhưng tên hàm lại tương đối dài: phần định danh phải tự mang theo thông tin thuộc về module nào. Đây là yêu cầu về cấu trúc, không phải sự thiếu nhất quán.
Go đi theo hướng ngược lại. Tên gói luôn xuất hiện ở nơi gọi (http.Get, json.Marshal), nên chính tên gói đã làm nhiệm vụ bổ nghĩa; lặp lại tên gói bên trong định danh chỉ tạo ra sự thừa. Vì vậy tên trong Go có xu hướng ngắn, và biến receiver thường chỉ 1–2 ký tự.
Java thì chấp nhận tên dài mang tính mô tả, kể cả với tên lớp. Chẳng hạn AbstractSingletonProxyFactoryBean trong Spring Framework là một lớp tồn tại thực tế và dài 33 ký tự. Khi so sánh giữa các ngôn ngữ, hãy đọc độ dài như hệ quả của cơ chế bổ nghĩa mà ngôn ngữ đó cung cấp, chứ không phải như một thước đo chất lượng mã nguồn.
Hướng dẫn tên hàm và lớp
| Loại định danh | Độ dài khuyến nghị | Nguyên tắc đặt tên | Ví dụ |
|---|---|---|---|
| Tên hàm | 10–25 ký tự | Định dạng động từ + đối tượng | calculateTotalPrice, sendEmailNotification |
| Tên lớp | 10–25 ký tự | Danh từ hoặc cụm danh từ | UserRepository, PaymentProcessor |
| Tên interface | 10–25 ký tự | Tính từ hoặc danh từ mô tả hành vi | Serializable, EventListener |
| Tên hằng số | 10–30 ký tự | UPPER_SNAKE_CASE với ý nghĩa cụ thể | MAX_RETRY_COUNT, DEFAULT_TIMEOUT_MS |
| Biến/hàm Boolean | 10–20 ký tự | Tiền tố is/has/can/should | isValid, hasPermission, canExecute |
Hàm nên luôn bắt đầu bằng một động từ. fetchData() rõ ràng hơn data(); validateInput() tốt hơn validation().
Quy ước riêng theo ngôn ngữ
| Ngôn ngữ | Biến/Hàm | Lớp | Hằng số | Hướng dẫn phong cách tiêu biểu |
|---|---|---|---|---|
| Java | camelCase | PascalCase | UPPER_SNAKE_CASE | Google Java Style Guide |
| Python | snake_case | PascalCase | UPPER_SNAKE_CASE | PEP 8 |
| JavaScript | camelCase | PascalCase | UPPER_SNAKE_CASE | Airbnb Style Guide |
| Go | camelCase / PascalCase | PascalCase | PascalCase | Effective Go |
| Ruby | snake_case | PascalCase | UPPER_SNAKE_CASE | Ruby Style Guide |
| C# | camelCase / PascalCase | PascalCase | PascalCase | Microsoft C# Conventions |
Cột cuối liệt kê tài liệu được tham chiếu phổ biến nhất cho từng ngôn ngữ, nhưng chúng không cùng một hạng: có tài liệu do chính dự án ngôn ngữ phát hành (PEP 8, Effective Go), có tài liệu do một công ty biên soạn cho mã nguồn nội bộ của họ (Google Java Style Guide, Microsoft C# Conventions) và có tài liệu do cộng đồng duy trì (Airbnb, Ruby Style Guide). Vì mức độ ràng buộc khác nhau, việc cần làm đầu tiên trong một dự án là thống nhất xem nhóm lấy tài liệu nào làm chuẩn.
- Java - Tên dài được chấp nhận trong văn hóa lập trình. Với tính năng tự động hoàn thành phong phú của IDE, những tên như
AbstractSingletonProxyFactoryBean(33 ký tự) được sử dụng trên thực tế. Tuy nhiên cần nhớ rằng tự động hoàn thành chỉ làm nhẹ việc cho người viết; người đọc vẫn phải đọc trọn tên đó mỗi lần nó xuất hiện. Vì thế độ dài nên được quyết định theo phía đọc, chứ không theo công sức gõ. - Python - PEP 8 ưu tiên sự ngắn gọn. snake_case làm rõ ranh giới từ, mặc dù thêm một ký tự cho mỗi từ so với camelCase:
get_user_name(13 ký tự) so vớigetUserName(11 ký tự). - JavaScript - Phát triển frontend có xu hướng dùng tên component dài. Những tên như
UserProfileEditFormlàm rõ vai trò được ưu tiên. - Go - Các tài liệu phong cách của chính dự án Go (Effective Go và Go Code Review Comments, bản hiện hành tính đến tháng 8 năm 2026) khuyến nghị tên ngắn gọn và đặt độ dài theo phạm vi sử dụng. Biến receiver sử dụng 1–2 ký tự (
scho server,ccho client) theo quy ước, nhất quán với cơ chế export chữ hoa/chữ thường của Go.
IDE Autocomplete và độ dài tên
Mối lo ngại rằng "tên dài gõ mệt" phần lớn đã được loại bỏ bởi các IDE hiện đại. So sánh tính năng tự động hoàn thành giữa các IDE chính cho thấy độ dài tên có tác động tối thiểu đến tốc độ phát triển.
| IDE / Trình soạn thảo | Kích hoạt hoàn thành | Chiến lược khớp | Hỗ trợ tên dài |
|---|---|---|---|
| IntelliJ IDEA | Tự động khi gõ | Khớp chữ cái đầu CamelCase (gUN → getUserName) | 2–3 chữ cái đầu thu hẹp ứng viên; tên dài tốn ít công sức |
| VS Code | Tự động khi gõ | Khớp mờ (usrnm → userName) | Hiển thị khớp một phần; không cần gõ chính xác |
| Vim / Neovim (LSP) | Ctrl+N hoặc tích hợp LSP | Hoàn thành dựa trên LSP nhận biết kiểu | Hoàn thành tương đương IDE qua coc.nvim hoặc nvim-cmp |
Tính năng khớp CamelCase của IntelliJ IDEA đặc biệt mạnh mẽ - chỉ cần gõ ASPFB là đủ để hoàn thành thành AbstractSingletonProxyFactoryBean (33 ký tự). Điều này có nghĩa là độ dài tên không cần bị giới hạn bởi công sức gõ; bạn có thể chọn độ dài tối ưu hoàn toàn vì mục đích dễ đọc.
Thông tin thú vị về đặt tên
Tài liệu coding-style của Linux kernel yêu cầu tên biến phải "ngắn gọn và đi thẳng vào vấn đề", và nêu thẳng rằng loop_counter là cách đặt tên không hiệu quả so với i (nội dung của bản hiện hành tính đến tháng 8 năm 2026). Đây là quy định trong tài liệu của dự án; cần phân biệt với những phát biểu cá nhân thường được dẫn lại trên mạng mà không kèm nguồn xác minh được. Ở phía đối lập, Google Java Style Guide §5.2.6 chỉ khuyến nghị tránh tham số một ký tự cho các phương thức public, và không đặt ra giới hạn nào đối với biến cục bộ. Sự khác biệt này phản ánh bối cảnh sử dụng: mã kernel được một nhóm nhỏ chuyên gia đọc trực tiếp, còn API public của một dịch vụ lớn lại được rất nhiều lập trình viên bên ngoài gọi đến mà không đọc phần thân hàm.
Vấn đề với tên quá dài và quá ngắn
Lỗi đặt tên rơi vào hai thái cực: quá ngắn để truyền đạt ý nghĩa, và quá dài để đọc thoải mái. Những tên ngắn như d, tmp, hoặc val ngoài bộ đếm vòng lặp trở nên khó hiểu chỉ trong vài ngày - ngay cả với chính tác giả.
Tên quá dài cũng gây vấn đề tương tự. Một biến như numberOfItemsInTheShoppingCartBeforeDiscount không vừa trên một dòng và thực sự làm giảm khả năng đọc. Điểm cân bằng lý tưởng là một tên truyền đạt vai trò của nó trong nháy mắt mà không làm gián đoạn luồng mã xung quanh.
Có thể hình dung quan hệ giữa độ dài định danh và khả năng đọc như một đường cong hình chữ U, nhưng nên hiểu nó qua cơ chế chứ không qua một con số tối ưu cố định. Tên quá ngắn buộc người đọc phải khôi phục ý nghĩa từ ngữ cảnh, tức là cuộn ngược lại chỗ khai báo mỗi lần không chắc. Tên quá dài lại làm dòng mã kéo dài theo chiều ngang, khiến cấu trúc của câu lệnh (điều kiện, phép gán, lời gọi hàm) bị đẩy ra ngoài tầm mắt hoặc phải ngắt dòng nhiều lần. Vì vậy điểm cân bằng phụ thuộc vào phạm vi và mật độ của mã xung quanh, không phải một khoảng ký tự áp dụng chung cho mọi trường hợp.
Tên biến Unicode và Multibyte
Nhiều ngôn ngữ lập trình hỗ trợ định danh Unicode, nhưng việc sử dụng ký tự không phải ASCII trong tên biến gây ra một số cạm bẫy trên thực tế.
| Ngôn ngữ | Biến Unicode | Ví dụ | Lưu ý |
|---|---|---|---|
| Python 3 | Hỗ trợ đầy đủ | 名前 = "Taro" | PEP 8 khuyến nghị chỉ dùng ASCII; tránh trong nhóm quốc tế |
| Ruby | Hỗ trợ đầy đủ | 数値 = 42 | Cần magic comment # encoding: utf-8 (trước Ruby 2.0) |
| JavaScript | Hỗ trợ (viết trực tiếp hoặc bằng ký pháp escape) | let café = true | Chuẩn hóa NFC/NFD có thể khiến tên trông giống nhau nhưng khác nhau |
| Java | Hỗ trợ đầy đủ | int 金額 = 1000; | Biên dịch được, nhưng hầu hết hướng dẫn phong cách không khuyến khích |
| Go | Danh mục Unicode Letter | 名前 := "Taro" | Chỉ cho phép ký tự thuộc danh mục Letter của Unicode |
| C / C++ | Hạn chế | int données = 0; | Phạm vi ký tự cho phép khác nhau theo phiên bản chuẩn (từ C11/C++11) và theo từng trình biên dịch |
Vấn đề chuẩn hóa Unicode của JavaScript đáng được chú ý đặc biệt. Tên biến café có thể được biểu diễn theo hai cách: dạng NFC với é là một ký tự đơn (U+00E9), hoặc dạng NFD với e + dấu kết hợp (U+0065 U+0301). Chúng trông giống hệt nhau nhưng được xử lý như các định danh khác nhau. Trên macOS, HFS+ chuẩn hóa tên tệp về một dạng gần với NFD, còn APFS thì không chuẩn hóa mà giữ đúng chuỗi ký tự đã được ghi. Nghĩa là cùng một cái tên có thể tồn tại ở hai dạng khác nhau tùy hệ thống tệp và tùy nơi tạo ra tệp, và lỗi khó truy vết sẽ xuất hiện khi bạn sinh tên biến từ tên tệp.
Xung đột từ khóa dành riêng là một trường hợp biên thường bị bỏ qua. Trong Python, class, import, và return là từ khóa dành riêng, nhưng các cách giải quyết như cls (viết tắt của class) và klass đã trở thành quy ước phổ biến. JavaScript cũng vậy: class đã nằm trong danh sách từ khóa dành riêng của chuẩn ECMAScript từ trước, chứ không phải bị ES6 chiếm lấy (ES6 chỉ bổ sung cú pháp khai báo class). Nên vấn đề ở đây không phải chuyện di chuyển mã, mà đơn giản là bạn không thể dùng từ khóa làm định danh và phải chọn một cách viết thay thế như cls hoặc klass.
Các lỗi thường gặp
- Viết tắt quá mức - Những tên như
usrAccMgrhoặccntDwnTmrkhó hiểu với bất kỳ ai ngoài tác giả. Hạn chế viết tắt chỉ với những từ được công nhận rộng rãi (URL, HTTP, ID). - Lạm dụng ký pháp Hungary - Thêm tiền tố thông tin kiểu như
strNamehoặcintAgelà thừa trong các IDE hiện đại với suy luận kiểu và tooltip khi di chuột. Các hướng dẫn thiết kế của .NET cũng nêu rõ là không dùng ký pháp Hungary cho tên định danh. - Đặt tên không nhất quán trong dự án - Trộn lẫn
user_name,userName, vàUserNamecho cùng một khái niệm phá hủy khả năng tìm kiếm. Thiết lập hướng dẫn phong cách ngay từ đầu dự án và thực thi bằng linter.
Kỹ thuật đặt tên chuyên nghiệp
- Xem xét đặt tên trong code review - Kiểm tra không chỉ tính đúng đắn của logic mà còn xem tên có truyền đạt ý định hay không. Cải thiện đặt tên là một trong những đầu tư có ROI cao nhất cho chất lượng mã dài hạn.
- Sử dụng tính năng đổi tên của IDE - Khi bạn nghĩ ra tên tốt hơn, hãy dùng tính năng đổi tên của IDE (IntelliJ Shift+F6, VS Code F2) để cập nhật an toàn tất cả tham chiếu.
- Duy trì bảng thuật ngữ nhóm - Quyết định xem "user," "account," hay "member" đại diện cho một khái niệm nhất định và ghi lại. Điều này phù hợp với nguyên tắc "ngôn ngữ chung" của Domain-Driven Design.
Thực thi quy ước đặt tên bằng Linters
Để thống nhất quy ước đặt tên trong nhóm, kiểm tra tự động bằng linter là thiết yếu. Dưới đây là các linter chính và quy tắc liên quan đến đặt tên cho từng ngôn ngữ.
| Ngôn ngữ | Linter | Quy tắc đặt tên | Ví dụ cấu hình |
|---|---|---|---|
| JavaScript / TypeScript | ESLint | @typescript-eslint/naming-convention | Bắt buộc camelCase cho biến, UPPER_CASE cho hằng số, PascalCase cho kiểu |
| Python | pylint / Ruff | C0103 (invalid-name) | Bắt buộc snake_case; mẫu regex cho tên hợp lệ có thể cấu hình riêng theo từng loại định danh |
| Java | Checkstyle | MemberName, MethodName | Định nghĩa quy tắc đặt tên qua mẫu regex |
| Go | golangci-lint | revive's var-naming | Bắt buộc MixedCaps; thống nhất viết hoa từ viết tắt (ID, URL) |
| Ruby | RuboCop | Naming/VariableName | Chọn snake_case hay camelCase qua tùy chọn EnforcedStyle |
Quy tắc @typescript-eslint/naming-convention của ESLint đặc biệt linh hoạt, cho phép quy tắc đặt tên khác nhau cho từng loại định danh (biến, hàm, lớp, interface, v.v.). Bạn thậm chí có thể bắt buộc các ràng buộc ngữ nghĩa như "biến Boolean phải bắt đầu bằng is, has, hoặc should." Tích hợp linter vào pipeline CI/CD giúp tự động phát hiện vi phạm đặt tên trước khi merge.
Kết luận
Độ dài tên phù hợp tỷ lệ thuận với phạm vi: 1–2 ký tự cho bộ đếm vòng lặp, 8–15 cho biến cục bộ, 15–25 cho hằng số toàn cục. Cách đọc nguyên tắc này rất đơn giản: phạm vi càng rộng thì khoảng cách giữa nơi khai báo và nơi sử dụng càng xa, nên tên phải tự mang đủ thông tin để người đọc không cần cuộn ngược lại. Độ dài thực tế còn thay đổi theo cơ chế bổ nghĩa của từng ngôn ngữ - tiền tố module trong C thay cho namespace, tên gói làm nhiệm vụ bổ nghĩa trong Go - nên đừng so sánh số ký tự giữa các ngôn ngữ như một thước đo chất lượng. Mặc dù hỗ trợ tên biến Unicode đang mở rộng, các vấn đề chuẩn hóa và khả năng đọc trong nhóm quốc tế khiến đặt tên chỉ dùng ASCII là lựa chọn thực tế. Tránh viết tắt quá mức và ký pháp Hungary, đồng thời thực thi quy ước đặt tên thông qua linter tích hợp vào pipeline CI/CD. Sử dụng Bộ đếm ký tự để kiểm tra độ dài định danh của bạn.