Tản mạn về code coverage
Có nên bắt buộc phải duy trì code coverage cao không? Tỉ lệ code coverage bao nhiêu là hợp lý cho dự án? Chi phí để duy trì code coverage có lớn không? Dự án bị dí deadline thì có nên bỏ qua để làm cho kịp không?
Đây là những câu hỏi mà một thời mình đã từng trăn trở khi quản lý một team phát triển. Bài viết này mình sẽ chia sẻ những trải nghiệm và quan điểm của mình về vấn đề này.
Code coverage cao là tốt?
Code coverage thể hiện tỉ lệ bao phủ giữa code test và code của logic chương trình bạn đang viết. Một cách tổng quát, tỉ lệ này cao đồng nghĩa với việc bạn đảm bảo những thứ bạn viết ra được kiểm tra và xác nhận theo cách bạn hiểu và mong muốn chương trình thực thi.
Một sản phẩm có code coverage thấp thể hiện việc bạn không quan tâm đến việc xác nhận logic thực thi của chương trình (vì lý do bất kì). Điều này thường dẫn đến việc nó mong manh, dễ lỗi. Kéo theo chi phí bảo trì, vận hành thường cao. Mình chưa thấy có sản phẩm nào dễ bảo trì nếu code coverage thấp tính đến thời điểm hiện tại.
Duy trì code coverage cao sẽ giúp mình tự tin hơn khi tiến hành những thay đổi trên sản phẩm. Chỉ cần run lại toàn bộ test sẽ giúp ta phát hiện những điểm nào bị ảnh hưởng về logic và dễ dàng sửa đổi, bảo trì hơn.
Tuy nhiên, sản phẩm có code coverage cao thì liệu có phải là một sản phẩm tốt và chất lượng code có thực sự tốt? Câu hỏi này thú vị hơn.
Thật sự, chất lượng code tốt được duy trì bởi những practice của team và code coverage cao chỉ là kết quả của việc duy trì tốt những practice này. Nếu ta làm điều ngược lại (kỳ vọng code coverage như một KPI cho chất lượng code tốt hơn) mà không duy trì tốt những practice của team thì nó sẽ tạo ra tác dụng phụ. Mình đã từng trải nghiệm ở một số team dự án có code coverage khá tốt (>80%), nhưng đó chỉ là kết quả của việc đối phó để đạt "KPI". Code chỉ được review hời hợt, và nhiều best practices của software vẫn bị bỏ qua để chạy kịp deadline dự án. Hệ quả là code coverage chỉ mang cái "mác" phủ bên ngoài để bán cho khách hàng và chất lượng thật sự là một vấn đề rất cần phải cải thiện.
Ngoài ra, ta không thể đánh đồng code coverage với chất lượng sản phẩm nói chung vì sẽ còn có nhiều yếu tố khác cộng hưởng. Code coverage cao chỉ thể hiện việc tính xác nhận về logic thực thi (theo cách hiểu của lập trình viên) được bảo chứng. Code coverage có thể cao, nhưng lập trình viên hiểu sai yêu cầu, hoặc trải nghiệm người dùng tệ trên sản phẩm ... lại là những vấn đề khác nhau.
Duy trì code coverage như thế nào?
Trong phần mềm hay cuộc sống, chất lượng chỉ là kết quả của việc duy trì thói quen tốt (practice). Để tạo ra code coverage cũng vậy. Tuy nhiên ở chiều ngược lại, phần mềm sẽ có chất lượng code tệ nếu code coverage thấp. Vì vậy, trong thực tế, ta cần quan tâm về code coverage và duy trì nó ở một ngưỡng tối thiểu. Hiện tại, mình quan sát thấy có 2 cách tiếp cận chung ở các dự án.
Practice 1 - Thiết lập tỉ lệ code coverage như một ngưỡng chặn từ đầu
Thường mình đặt một ngưỡng minimum là 80%. Sử dụng CI/CD để đảm bảo mọi PRs phải có code coverage > mức minimum đó. Đây là một practice chung mà mình thấy phần lớn các team dự án thường làm. Tuy nhiên, trong thực tế, sẽ có những dự án có kết quả code coverage cao, nhưng chất lượng thật sự không được như kỳ vọng (như mình đã đề cập ở trên).
Practice 2 - Test Driven Development (TDD)
Áp dụng TDD ngay từ những dòng code đầu tiên là một practice mình rất thích. Bản chất của TDD là một cách thuận theo tự nhiên nhất để duy trì sự bảo chứng giữa code và test. Sẽ không có dòng code nào được viết ra nếu không có test case bảo chứng nó. Nếu duy trì được TDD thì bản thân tỉ lệ code coverage sẽ cao một cách tự nhiên. Đồng thời, code viết ra cũng "testable" (hoàn toàn có thể test được) theo cách tự nhiên nhất.
Trong những dự án gần đây, mình áp dụng TDD ngay từ đầu và hướng tiếp cận này đem hiệu quả rất tốt. Một cách rất tự nhiên, code coverage trong dự án của mình đạt hơn 90% mà không cần phải nỗ lực một cách gượng ép.

2 practices này không loại trừ nhau và thực tế thường được kết hợp. Tuy nhiên, có một số dự án không áp dụng được TDD thì áp dụng practice đầu tiên cũng sẽ mang lại kết quả tương đối.
Chi phí để đảm bảo code coverage có lớn không?
Việc tạo thói quen để mỗi dòng code được viết ra phải được bảo chứng bởi test là một hướng tiếp cận tốt. Chi phí cho việc này chỉ là chi phí cho việc tạo thói quen từ ban đầu. Một cách tổng quát, nếu dự án được thiết lập với kiểm soát để duy trì code coverage ngay từ đầu thì một team chỉ tốn maximum 1 tuần để làm quen và đưa vào vận hành.
Đối với một dự án đã tồn tại và chưa có bất cứ dòng test nào. Chi phí để refactor và add thêm test vào sẽ phức tạp hơn. Hướng tiếp cận cũng sẽ khác hơn: chỉ áp dụng code coverage cho code mới hoặc những phần ta sẽ thay đổi để tránh rủi ro. Để cải tiến chất lượng code, cần phải thay đổi thói quen của nhóm. Trong thực tế, thay đổi thói quen là một bài toán không đơn giản cho tổ chức. Và chi phí của sự thay đổi này thường cao. Trường hợp xấu nhất phải thay thế những thành viên đang có.
Một câu hỏi ngược lại: chi phí phải bỏ ra nếu code coverage quá thấp / hoặc không có thì sẽ như thế nào? Phần lớn các trường hợp, chi phí để bảo trì, thay đổi và test sẽ lớn hơn rất nhiều. Vì vậy, quyết đoán thay đổi để tạo ra thói quen tốt cho team, cải thiện chất lượng code là điều bắt buộc phải làm nếu bạn muốn hướng đến một sản phẩm tốt.
Code coverage nhìn ở góc độ đạo đức nghề nghiệp
Là một lập trình viên, việc đảm bảo mỗi dòng code mình viết ra được xác nhận bởi test là một điều cần thiết (cho dù bạn có theo Test Driven Development hay không). Nó thể hiện tính chuyên nghiệp của bản thân mình trong công việc. Ngoài ra, lợi ích của code coverage đem lại không cần phải bàn cãi vì nó giúp bạn tự tin hơn rất nhiều khi release. Thậm chí giúp bạn release nhanh hơn.
Việc hướng đến code coverage 100% là một mục tiêu cần thiết để hướng đến chất lượng code tốt và là một điều đáng tự hào nếu bạn đạt được nó. Ở quan điểm cá nhân mình không tin rằng: trên đời tồn tại một sản phẩm tốt được viết ra từ những dòng code cẩu thả và không được bảo chứng bởi test. Đồng thời, mình cũng tin rằng không có lập trình viên giỏi nào không hướng đến việc mã nguồn mình viết ra phải được test và bảo chứng bởi test một cách tỉ mỉ, cẩn trọng.
Trân trọng!
Comments ()