Bạn đã bao giờ mở lại bài tập viết cách đây vài ngày rồi nhìn chằm chằm vào biến x hay tmp2, không nhớ nổi nó đang giữ gì? Ở nhóm mình, chuyện này gặp khá thường xuyên. Có khi đoạn code chạy hoàn toàn đúng nhưng chính người viết lại thấy rối khi đọc lại. Viết mã dễ đọc nghĩa là đặt tên có ý nghĩa, tách việc thành các hàm nhỏ và ghi chú đúng chỗ. Nghe đơn giản, nhưng ba việc này quyết định bạn đọc lại code mất vài phút hay cả buổi chiều. Trong bài này, chúng tôi sẽ đi qua từng việc, kèm ví dụ lấy từ những bài tập mà nhóm đã review.
Vì sao đoạn code chạy đúng vẫn khiến bạn đau đầu khi quay lại
Khi viết chương trình, phần lớn chúng ta chỉ cần nó chạy được. Máy tính không quan tâm biến tên gì, hàm dài bao nhiêu hay có ghi chú hay không. Người đọc thì khác. Đó có thể là chính bạn sau một tuần, hoặc một bạn mới vào lớp cần hiểu bài để làm tiếp.
Chúng tôi hay đùa rằng đoạn code hôm nay sẽ có người đọc vào tuần sau, và người đó nhiều khi chính là bạn. Một đoạn code dễ đọc là đoạn mà người khác hiểu mục đích trong vài phút, không phải đoán từng dòng. Khi phải đoán, thời gian mất đi không chỉ vài phút. Bạn có thể vô tình sửa một chỗ đang chạy tốt, rồi lại lần ngược để tìm lỗi mới. Cảm giác đó khá mệt, và nó thường bắt nguồn từ những thứ rất nhỏ như cách đặt tên hay sắp xếp dòng lệnh.
Tiêu chí nào cho biết một đoạn code có dễ đọc hay không
Không có thước đo tuyệt đối, nhưng vẫn có vài dấu hiệu khá rõ. Cách đơn giản nhất là đọc lướt một lần rồi tự hỏi chương trình này làm gì. Nếu phải đọc kỹ từng dòng mới trả lời được, đoạn code đó đang có vấn đề. Nhóm mình thường thử cách này với bài của học viên trước khi góp ý, và nó hiệu quả hơn bạn nghĩ.
Có ba điểm mà chúng tôi thấy người mới mắc nhiều nhất khi review bài. Thứ nhất là đọc lướt một lần có hiểu mục đích chương trình không. Thứ hai là các khối lồng nhau, ví dụ if trong for trong if, đã sâu đến mức khó theo dõi chưa. Thứ ba là hàm dài bao nhiêu và nhận bao nhiêu tham số. Hàm quá dài hoặc quá nhiều tham số thường cho thấy nó đang gánh quá nhiều việc.
Khi tự kiểm tra theo ba điểm này, bạn sẽ phát hiện nhiều lỗi về tổ chức mà trước đó chưa để ý. Vì vậy chúng tôi khuyên làm ngay khi bài đã chạy đúng, đừng đợi đến lúc nộp mới xem lại.
Đặt tên sao cho người khác đọc là hiểu
Tên là thứ đầu tiên người đọc nhìn thấy. Một cái tên tốt giống biển chỉ đường, còn tên mơ hồ buộc người đọc phải dừng lại đoán. Viết tongDiem thay vì td chỉ tốn thêm vài ký tự nhưng tiết kiệm rất nhiều thời gian về sau. Có lần chúng tôi gặp bài tính học bổng dùng biến t cho tổng điểm và t2 cho điểm thưởng. Ba ngày sau, chính tác giả cũng phải đọc lại từ đầu mới nhớ ra ý mình.
Một quy ước đơn giản mà nhiều nhóm áp dụng là đặt tên biến bằng danh từ, tên hàm bằng động từ. Ví dụ diemTrungBinh cho biến và tinhDiemTrungBinh cho hàm. Quan trọng hơn cả là giữ kiểu viết nhất quán trong cùng dự án. camelCase hay snake_case đều được, miễn đừng trộn lẫn trong một file. Khi trộn lẫn, người đọc sẽ tự hỏi vì sao chỗ này viết khác chỗ kia, và câu hỏi đó chiếm mất sự chú ý vốn nên dành cho logic.
Tách hàm: khi nào nên tách và tách thế nào
Một hàm tốt nên làm đúng một việc mà bạn mô tả được bằng một câu ngắn. Nếu phải nối nhiều mệnh đề bằng chữ “và” mới diễn tả được, thường đã đến lúc tách. Đây là cách kiểm tra đơn giản nhưng hiệu quả nhất mà chúng tôi biết.
Có vài dấu hiệu báo hiệu bạn nên tách. Hàm dài hơn một màn hình là một dấu hiệu. Đoạn code giống nhau lặp lại ở nhiều chỗ cũng vậy. Ví dụ điển hình là logic tính điểm trung bình nằm lẫn trong phần nhập liệu. Khi tách phần tính toán thành hàm riêng, bạn có thể kiểm thử với vài bộ số mẫu mà không phải nhập tay mỗi lần chạy, và việc tìm lỗi nhanh hơn hẳn.
Ví dụ trước và sau khi tách hàm
Chúng tôi từng gặp bài quản lý điểm học sinh mà toàn bộ chương trình nằm trong một hàm dài hơn ba mươi dòng. Nhập dữ liệu, tính toán, xếp loại và in kết quả đều dồn vào một chỗ. Hậu quả là sửa lỗi nhỏ ở khâu xếp loại lại làm hỏng khâu in, và không ai biết vì sao cho đến khi lật lại từng dòng.
Sau khi tách thành bốn hàm nhapDiem, tinhDiemTrungBinh, xepLoai và inKetQua, mỗi phần đứng riêng. Hàm nào cũng ngắn lại rõ rệt, và có thể kiểm thử từng phần. Điều thú vị là chức năng không thay đổi, chỉ được sắp xếp lại. Đó chính là ý nghĩa của refactor: cải thiện cấu trúc trong khi hành vi bên ngoài vẫn giữ nguyên. Bạn có thể tham khảo thêm cách chia nhánh gọn gàng trong bài câu lệnh điều kiện if trong C.
Ghi chú đúng chỗ: nói vì sao, không nói làm gì
Nhiều người mới nghĩ ghi chú càng nhiều càng tốt. Thực tế, ghi chú chỉ hữu ích khi cho người đọc biết điều mà code không thể hiện được. Ví dụ bạn chọn một cách làm đặc biệt vì một lý do cụ thể, hoặc có điều kiện biên dễ gây lỗi mà người đọc cần lưu ý.
Đừng ghi chú những thứ code đã nói rõ, như “tăng i lên 1” ngay phía trên dòng i++. Đó chỉ là nhiễu. Cũng cần cẩn thận với ghi chú lỗi thời. Khi code đổi mà ghi chú vẫn giữ nguyên, người đọc bị dẫn sai hướng, đôi khi còn tệ hơn là không có ghi chú nào. Mỗi lần sửa một đoạn, hãy đọc lại những ghi chú nằm gần đó.
So sánh hai phiên bản: khó đọc và dễ đọc
Để dễ hình dung, hãy xem một đoạn tính điểm viết theo hai cách. Phiên bản đầu dùng tên x và tmp2, lồng năm tầng điều kiện và gom mọi thứ vào một hàm. Phiên bản sau dùng soBaiNop và diemTam, chia thành hai hàm nhỏ, mỗi hàm không quá hai tầng lồng. Kết quả chạy giống hệt nhau, nhưng thời gian để một người mới hiểu chênh lệch rất lớn.
Khác biệt nằm ở chỗ người đọc không phải đoán nội dung biến, không cần nhớ nhiều nhánh cùng lúc, và có thể kiểm thử từng hàm. Bạn không cần thuộc lòng quy tắc nào để nhận ra điều này, chỉ cần đọc hai phiên bản cạnh nhau một lần.
Sai lầm thường gặp khi cố viết code “ngắn” hơn
Có một cám dỗ khá lớn là viết thật ngắn cho có vẻ chuyên nghiệp. Chúng tôi từng thấy học viên gộp cả chuỗi phép toán vào một dòng. Nhìn thì gọn, nhưng khi có lỗi gần như không thể debug, vì không biết kết quả trung gian là bao nhiêu.
Đặt tên quá ngắn để tiết kiệm thao tác gõ phím cũng là bẫy. Bạn tiết kiệm được vài giây khi gõ, nhưng mất vài phút mỗi lần đọc lại. Và đừng lạm dụng ghi chú để bù cho việc đặt tên kém. Nếu phải giải thích một biến bằng ghi chú, hãy thử đổi tên biến trước. Nhiều khi chỉ cần đổi tên là ghi chú trở nên thừa.
Luyện thói quen viết mã dễ đọc mỗi ngày
Viết code dễ đọc là một thói quen, không phải kỹ năng học một lần là xong. Chúng tôi khuyên bạn thử đọc lại code của mình sau khoảng 24 giờ, và đọc như đang đọc code của người khác. Bạn sẽ nhanh chóng thấy những chỗ tối nghĩa mà lúc viết không để ý. Cách này đơn giản nhưng hiệu quả hơn nhiều so với cố nhớ quy tắc.
Mỗi tuần, hãy chọn một bài tập cũ và refactor lại. Không cần viết lại từ đầu, chỉ cần đổi tên, tách hàm và thêm ghi chú đúng chỗ. Các công cụ lint và formatter cũng giúp giữ quy ước nhất quán mà không phải nhớ thủ công. Khi đã quen, viết code dễ đọc sẽ trở thành phản xạ tự nhiên. Nếu bạn đang theo một lộ trình học lập trình cho người mới, việc refactor đều đặn sẽ giúp bạn tiến bộ rõ rệt.
Câu hỏi thường gặp
Đặt tên biến ngắn có bị coi là xấu không?
Không hẳn. Biến đếm vòng lặp như i hay j được chấp nhận vì phạm vi dùng rất nhỏ. Nhưng nếu biến xuất hiện ở nhiều nơi trong chương trình, hãy đặt tên đủ nghĩa để người đọc không phải lần ngược lên đầu file.
Hàm dài bao nhiêu dòng thì nên tách?
Nhóm mình thường lấy mốc là một màn hình. Nếu phải cuộn để xem hết hàm, hãy cân nhắc tách. Quan trọng hơn con số là hàm có đang làm nhiều việc hay không. Một hàm ngắn nhưng làm ba việc khác nhau vẫn nên tách.
Có nên ghi chú mọi dòng code không?
Không nên. Ghi chú mọi dòng khiến code rối mắt và dễ lỗi thời. Chỉ nên ghi chú những chỗ cần lý do, điều kiện đặc biệt hoặc cách làm không hiển nhiên.
Người mới nên bắt đầu học quy ước đặt tên từ đâu?
Hãy bắt đầu bằng cách chọn một kiểu viết như camelCase và giữ nhất quán trong mọi bài tập. Sau đó đọc code mẫu của các dự án mã nguồn mở để quen với cách người khác đặt tên. Để hiểu thêm về cách tổ chức dữ liệu trong chương trình Java, bạn có thể đọc Collections trong Java.
Nếu đang phân vân nên học ngôn ngữ nào trước, bạn có thể tham khảo so sánh các ngôn ngữ phổ biến cho người mới để chọn đúng nền tảng trước khi đi sâu vào thói quen viết code.