💡 Key Takeaways
- Why Most Code Review Checklists Miss the Point
- The Error Handling Patterns That Keep Breaking Production
- The Day a Variable Name Caused a $50,000 Incident
- What the Data Actually Shows About Code Review
Danh Sách Kiểm Tra Đánh Giá Mã Mà Tôi Đã Xây Dựng Sau 2.000 Yêu Cầu Kéo
Tôi đã phân loại 2.147 bình luận PR mà tôi để lại trong 18 tháng. 34% liên quan đến xử lý lỗi. 22% liên quan đến việc đặt tên. Chỉ 8% liên quan đến hiệu suất. Điều này không phải là những gì tôi mong đợi khi bắt đầu theo dõi các đánh giá của mình. Tôi nghĩ mình sẽ phát hiện ra các vấn đề kiến trúc và lỗi phức tạp. Thay vào đó, tôi đã liên tục chỉ ra các vấn đề cơ bản giống nhau: thiếu kiểm tra null, tên biến mơ hồ, và thông báo lỗi mà không cung cấp thông tin hữu ích nào cho người dùng. Sau khi thấy những vấn đề giống nhau xuất hiện trong các sự cố sản xuất, tôi nhận ra rằng đây không phải là những lỗi nhỏ—chúng là sự khác biệt giữa các hệ thống thất bại một cách nhẹ nhàng và các hệ thống làm bạn tỉnh dậy lúc 3 giờ sáng.
💡 Những Điều Chính
- Tại Sao Hầu Hết Các Danh Sách Kiểm Tra Đánh Giá Mã Đều Bỏ Qua Điểm Quan Trọng
- Các Mẫu Xử Lý Lỗi Đang Khiến Sản Xuất Bị Vỡ
- Ngày Một Tên Biến Khiến Một Sự Cố 50.000 USD
- Dữ Liệu Thực Sự Cho Thấy Gì Về Đánh Giá Mã
Tại Sao Hầu Hết Các Danh Sách Kiểm Tra Đánh Giá Mã Đều Bỏ Qua Điểm Quan Trọng
Mọi người đều bảo bạn kiểm tra phong cách mã, độ bao phủ thử nghiệm và tài liệu. Điều này thì ổn, nhưng đó không phải là nơi sản xuất bị hỏng. Các danh sách kiểm tra mà tôi thấy được chia sẻ trên các blog kỹ thuật tập trung vào những gì dễ đo lường chứ không phải những gì thực sự quan trọng. Chúng sẽ bảo bạn xác minh rằng các hàm không quá 50 dòng, nhưng chúng sẽ không bảo bạn kiểm tra xem liệu xử lý lỗi có thật sự giúp ai đó gỡ lỗi vấn đề vào lúc 2 giờ sáng khi nhật ký là người bạn duy nhất của bạn không.
Các đánh giá mã tốt nhất không chỉ ngăn chặn lỗi—chúng ngăn chặn những loại lỗi dẫn đến sự cố. Thiếu kiểm tra null không chỉ là một sự cố có thể xảy ra; đó là một sự kiện làm hỏng dữ liệu tiềm ẩn khi sự cố đó xảy ra giữa giao dịch.
Tôi đã bắt đầu theo dõi các bình luận PR của mình vì tôi đã cảm thấy thất vọng. Tôi đã đánh giá mã, phê duyệt nó, và rồi thấy cùng một nhà phát triển mắc phải cùng một sai lầm trong PR tiếp theo. Phản hồi không có tác dụng. Vì vậy, tôi đã tạo một bảng tính. Mỗi bình luận tôi để lại đều được phân loại: xử lý lỗi, đặt tên, thử nghiệm, bảo mật, hiệu suất, kiến trúc, hoặc khác. Sau sáu tháng, tôi đã có đủ dữ liệu để thấy những mẫu. Sau mười tám tháng, những mẫu đó đã trở thành một danh sách kiểm tra thực sự hiệu quả.
Điều gây bất ngờ không chỉ là những gì đứng đầu danh sách—nó còn là những gì không có mặt. Các bình luận tối ưu hóa hiệu suất chỉ chiếm 8% trong các đánh giá của tôi. Vấn đề bảo mật là 6%. Đây là những điều mà chúng ta ám ảnh trong các cuộc thảo luận kiến trúc, nhưng trong các PR hàng ngày, chúng rất hiếm. Điều gì là phổ biến? Các nhà phát triển giả định rằng mọi thứ sẽ thuận lợi, đặt tên một cách tồi tệ, và viết thông báo lỗi cho chính họ thay vì cho người sẽ gỡ lỗi vào lúc nửa đêm.
Các Mẫu Xử Lý Lỗi Đang Khiến Sản Xuất Bị Vỡ
Dưới đây là danh sách các vấn đề xử lý lỗi của tôi theo thứ tự được xếp hạng theo mức độ thường xuyên của chúng gây ra sự cố thực sự:
- Những thất bại im lặng trong các tác vụ nền. Mã bắt một ngoại lệ, ghi lại nó và tiếp tục. Có vẻ hợp lý cho đến khi bạn nhận ra rằng tác vụ đó lẽ ra phải gửi một email xác nhận thanh toán. Giờ đây, khách hàng của bạn nghĩ rằng họ không bị tính phí, nhưng họ đã bị. Tôi thấy mẫu này trong 40% các PR tác vụ nền. Cách khắc phục rất đơn giản: nếu tác vụ là quan trọng, đừng bắt ngoại lệ—hãy để nó nổi lên và kích hoạt cảnh báo của bạn. Nếu không phải quan trọng, hãy ghi lại lý do tại sao việc thất bại một cách im lặng là chấp nhận được.
- Thông báo lỗi chung chung che giấu ngữ cảnh. "Đã xảy ra lỗi" không cho tôi biết gì. "Không thể xử lý thanh toán" thì tốt hơn nhưng vẫn vô dụng. "Không thể tính phí thẻ kết thúc bằng 4242: không đủ tiền (mã lỗi: thẻ_bị_từ_chối)" thực sự hữu ích. Tôi đánh dấu điều này trong khoảng 30% các PR. Bài kiểm tra tôi sử dụng: nếu bạn thấy lỗi này trong nhật ký sản xuất lúc 3 giờ sáng, bạn có thể chuẩn đoán nó mà không cần thêm ghi log và triển khai lại không?
- Bắt ngoại lệ thay vì các ngoại lệ cụ thể. Điều này gây tranh cãi vì một số hướng dẫn phong cách khuyến nghị điều đó, nhưng tôi đã thấy nó che giấu lỗi quá nhiều lần. Khi bạn bắt ngoại lệ, bạn cũng đang bắt NullPointerException, IllegalStateException và tất cả các ngoại lệ khác cho thấy lỗi lập trình viên thay vì điều kiện thời gian chạy. Bắt những gì bạn dự kiến sẽ xử lý. Hãy để lỗi lập trình viên xảy ra—đó là cách bạn tìm ra chúng.
- Không xác thực dữ liệu bên ngoài tại ranh giới. Phản hồi API, đầu vào của người dùng, nội dung tệp—nếu nó đến từ bên ngoài quy trình của bạn, hãy xác thực nó ngay lập tức. Tôi thấy các nhà phát triển xác thực trong lớp logic doanh nghiệp, điều này có nghĩa là dữ liệu không hợp lệ đã được truyền qua một số hàm. Đến lúc đó, bạn đang gỡ lỗi lý do tại sao một giá trị null đã vào cơ sở dữ liệu của bạn. Xác thực tại ranh giới, thất bại nhanh, trả về lỗi rõ ràng.
- Logic thử lại mà không có khoảng lùi mũ. Dịch vụ bị hỏng, vì vậy bạn thử lại ngay lập tức. Nó vẫn bị hỏng, vì vậy bạn lại thử ngay lập tức. Chúc mừng, bạn vừa biến một sự suy giảm dịch vụ thành một sự ngừng hoạt động hoàn toàn bằng cách cố gắng quá mức. Tôi thấy điều này trong 15% các PR thêm logic thử lại. Luôn luôn sử dụng khoảng lùi mũ với độ rung. Luôn luôn có số lần thử lại tối đa. Luôn luôn xem xét liệu việc thử lại có thực sự hợp lý cho tác vụ này không.
- Không xử lý các thất bại một phần trong các tác vụ hàng loạt. Bạn đang xử lý 1.000 bản ghi. Bản ghi 500 thất bại. Điều gì sẽ xảy ra với các bản ghi 501-1000? Trong hầu hết mã mà tôi xem xét, chúng sẽ không bao giờ được xử lý. Lô hàng bị thất bại, được thử lại, thất bại một lần nữa ở bản ghi 500, và bạn bị mắc kẹt. Hãy xử lý các thất bại một phần một cách rõ ràng: theo dõi những gì thành công, những gì thất bại và tại sao. Hãy làm cho các tác vụ hàng loạt của bạn có thể tiếp tục.
- Giả định rằng các giao dịch cơ sở dữ liệu sẽ luôn thành công. Bạn bắt đầu một giao dịch, thực hiện một số công việc và cam kết. Nhưng nếu cam kết đó thất bại thì sao? Nếu bạn mất kết nối với cơ sở dữ liệu giữa giao dịch thì sao? Tôi thấy mã không xử lý điều này trong 25% các PR liên quan đến mã cơ sở dữ liệu. Kết quả: trạng thái ứng dụng và trạng thái cơ sở dữ liệu của bạn không còn khớp nhau, và bạn mất hàng giờ để gỡ lỗi lý do tại sao dữ liệu không khớp với những gì nhật ký cho biết đã xảy ra.
Ngày Một Tên Biến Khiến Một Sự Cố 50.000 USD
Đó là một buổi sáng thứ Ba khi tôi nhận được tin nhắn trên Slack: "Chúng tôi vừa hoàn lại 50.000 USD cho những khách hàng sai." Tôi đã mở kênh sự cố và bắt đầu đọc. Một nhà phát triển đã đẩy một bản sửa lỗi vào đêm hôm trước cho một lỗi trong hệ thống xử lý hoàn tiền của chúng tôi. Bản sửa lỗi chỉ là một dòng. PR đã được hai kỹ sư cấp cao phê duyệt. Các bài kiểm tra đã vượt qua. Mọi thứ trông có vẻ ổn.
Lỗi nằm ở một tên biến. Mã gốc có một biến gọi là `refundAmount` đại diện cho số tiền hoàn lại tính bằng cents. Nhà phát triển đã thêm một biến mới cũng có tên là `refundAmount` đại diện cho số tiền tính bằng đô la. Họ đã quên đổi tên biến gốc. Mã biên dịch bình thường—cả hai đều là số nguyên. Các bài kiểm tra đã vượt qua vì dữ liệu thử nghiệm tình cờ sử dụng các số tiền mà ở đó cents và đô la gần giống nhau đến mức các khẳng định không phát hiện ra lỗi.
Trong sản xuất, chúng tôi đã xử lý 200 hoàn tiền vào sáng hôm đó. Một nửa trong số đó là sai số tiền. Một khoản hoàn lại 10,00 USD trở thành một khoản hoàn lại 1.000 USD. Một khoản hoàn lại 5,00 USD trở thành một khoản hoàn lại 500 USD. Khi ai đó nhận thấy, chúng tôi đã thanh toán quá 50.000 USD. Chúng tôi phải xem xét thủ công từng khoản hoàn lại, liên lạc với khách hàng và trong một số trường hợp, yêu cầu họ trả lại tiền. Việc dọn dẹp mất ba ngày.
Nguyên nhân gốc rễ không phải là sai lầm của nhà phát triển—ai cũng có thể mắc sai lầm. Đó là vì chúng tôi có hai biến với cùng một tên đại diện cho các đơn vị khác nhau trong cùng một phạm vi. Cuộc kiểm tra mã không phát hiện ra điều này vì các reviewer tập trung vào logic chứ không phải việc đặt tên. Sau sự cố đó, tôi đã thêm một quy tắc vào danh sách kiểm tra của mình: nếu một biến đại diện cho một số lượng có đơn vị, đơn vị đó phải có trong tên. Không phải `amount`, mà là `amountCents` hoặc `amountDollars`. Không phải `duration`, mà là `durationSeconds` hoặc `durationMilliseconds`.
Điều này có vẻ như là một điều tầm thường nhưng...
Written by the Cod-AI Team
Our editorial team specializes in software development and programming. We research, test, and write in-depth guides to help you work smarter with the right tools.
Related Tools