Đôi điều về TDD

Giới thiệu

Khi nhắc đến TDD, thì có vô số những bài viết có đề cập đến. Vì nó không còn là khái niệm mới nữa, nó cũ rồi, và quen thuộc rồi. Cho những ai chưa biết, thì TDD, hiểu nôm na là viết test trước khi viết code. Cụ thể vòng lặp của nó sẽ là, viết test, chạy test fail (màu đỏ của cờ đỏ sao vàng), rồi viết code sao cho chạy test xanh. Rồi lặp lại, viết test, rồi lại chạy fail, rồi viết code. Ở đây mình sẽ không đề cập đến cụ thể, mấy khái niệm quy trình, hay phương thức, đặc điểm, nhận dạng, vân vân và mây mây. Ở bài viết này, mình sẽ nói về những điều mình cảm nhận được, thực sự khi phát triển một dự án theo hướng TDD.

Khó khăn chính khi phát triển theo hướng TDD

  • Khi phát triển theo hướng TDD, thì developer phải có một trình độ nhất định, điều này, không phải dự án nào cũng đáp ứng được. Nhiều dự án, hoặc phải nói chính xác hơn là hầu hết các dự án, đều có những dev có trình độ khác nhau, thường sẽ có một người, hoặc hai người có trình độ code cứng, kéo theo đó là một vài, hay thậm chí, dăm bảy anh trình độ fresher (không biết fresher có đúng chính tả không nữa 😃 ). Vậy tại sao, lại cần developer có trình độ nhất định, và tại sao lại có hiện tượng một hai ông code cứng gồng mình kéo cả đoàn xe dăm bảy ông fresher.

  • Lý do "cần có trình độ nhất định" là khi phát triển theo hướng TDD, là phải viết test trước, đặc điểm rõ ràng là viết test trước, vậy để viết test tốt, thì ít ra developer cần phải có hiểu biết nhất định về công việc test, về chức năng mình đảm nhiệm phát triển, và phải có tư duy được các case normal (hay gọi chính xác theo tiếng việt là các trường hợp thông thường), các trường hợp giới hạn biên (giới hạn giá trị trên, giá trị dưới), các trường hợp tiền điều kiện và hậu điều kiện. Khi phát triển theo hướng TDD, khi viết được càng nhiều test, càng nhiều case, thì code càng ít lỗi, càng ít rủi rõ, viết code test càng nhiều, thì viết code thực thi càng nhanh. Khi viết test càng sát sao, và đúng logic, thì bản chất viết thế nào, viết test ra sao, không phải là ai cũng làm được. Mà hiện tại không làm được thì sao, thì cần được chỉ dạy, cần giành thêm thời gian đọc tài liệu, cần viết thử, làm, được chỉ dạy, thì sẽ tiến bộ, sẽ hiểu bản chất, hoặc là "cứ làm đi, làm nhiều sẽ hiểu tại sao lại làm như vậy". Đó, khi đó cần những ông code cứng đứng ra đấy.

  • Tại sao không kiếm một đội code cứng hẳn đi, rồi sau đó làm, thế có phải ngon không. Thực tế thì ở các dự án, kinh phí kiếm được, sẽ là một số hạn chế, chứ không phải là nhiều, rồi thì người nhận dự án phải có hoa hồng, rồi cần phải có lãi...vân vân và mây mây, nghĩa là có hàng nghìn lý do, để kinh phí của một dự án không nhiều. Mà mức đãi ngộ cho code cứng, không hề nhỏ. Hai là, tuyển một loạt người code cứng không phải dễ như vậy. Code cứng là người đi đâu cũng được hoan nghênh, các công ty đào góc tường nhau, các HR làm việc như ngu người, hết sức lôi kéo, dụ dỗ. Nhưng kiếm đủ một team, ngay khi có dự án và khi bắt đầu dự án, điều đó không dễ dàng như vậy. Không tin thì quen một bạn HR nào đó, rồi hỏi bạn đó về tình hình công việc, nhất là khi anh em developer tán gái HR, thì cứ hỏi thôi, vừa mang tiếng là quan tâm đến công việc của em, có thêm điểm, vừa hiểu biết tình hình thị trường nhân sự. Một công dăm ba việc.

  • Kiến trúc dự án cần được thiết kế thuận lợi cho việc viết các unit test. Nghĩa là nó có khả năng test một cách độc lập, một thành phần này viết sai sẽ không ảnh hưởng gì đến thành phần module khác. Hiểu nôm na là tôi viết test cho module A, thì ngay cả khi module A chạy sai, thì test module B sẽ không vì thế mà bị chạy sai đi. Mình hay gọi đó là tính tách biệt và module hóa. Chứ thực tế từ ngữ chuyên ngành của nó là gì thì cũng không nhớ.

  • Chúng ta đã đề cập đến những khó khăn về con người, tài chính và công nghệ ảnh hưởng thực tế đến việc chọn hướng phát triển TDD cho dự án rồi. Còn yếu tố nào ảnh hưởng đến tình hình dự án nữa không? oh, còn một yếu tố khác, đó chính là Khách hàng. Khách hàng là cha mẹ, là người trả tiền cho dự án, là người nuôi sống ta, cho ta một cuộc sống giàu sang với nước tăng lực trâu húc và với những gói mì tôm thơm phưng phức. Khách hàng là yếu tố quan trọng không thể thiếu của một dự án. Và hãy thử đứng ở vai trò là khách hàng và nhìn vào dự án TDD xem.

  • Chúng ta để hiểu, phát triển theo hướng TDD, về lâu về dài, khi chúng ta đảm bảo code được coverage 100%, và viết unit test, feature test, thì chi phí phải bỏ ra về lâu về dài, nhớ là lâu và dài nhé anh em. Thì nó mới thấy các gía trị thực của nó. Có những tính toán về mặt chi phí (bao gồm chi phí phát triển và duy trì (maintaince)) thì việc phát triển theo hướng TDD, sẽ tiết kiệm hơn. Mà đứng vai trò coder, thì một hệ thống dễ maintaince thì quá tuyệt vời rồi. Chúng ta sẽ không phải vừa code vừa chửi nữa. 🤕

  • Nhưng ở mức ngắn hạn, chi phí mà khách hàng bỏ ra, hay thực chất là tiền mà khách hàng phải trả cho công sức của developer để viết test là không có giá trị. Nghĩa là khách hàng không nhìn thấy cái mà họ muốn thấy, sản phẩm của họ. Khi không có sản phẩm, nghĩa là khách hàng gặp khó khăn để kiểm tiền, lại dẫn đến chi phí dự án có thể bị cắt giảm. Họ, sẽ không thấy được làm sao họ phải trả tiền cho việc mà chẳng mang lợi ích gì cho họ. Cái test gì đó mà mấy ông phát triển nói đến là cái khỉ gì? có nhìn thấy lợi ích gì đâu. Khi nào đo đếm được bằng cách nào? Hiện tại không thể nhìn thấy, chẳng lẽ là mấy dòng code mà mấy ông ấy cho mình xem, mấy cái thứ ngôn ngữ như của người ngoài hành tinh kia, đọc ai mà hiểu được. Mà mình còn phải mất tiền cho một cái vô bổ thế chứ. Tâm lý chung thôi, chúng ta luôn muốn trả giá ở mức thấp nhất và kiếm được cái có giá trị cao nhất. Và giá trị cao ở mắt khách hàng phải là sản phẩm, phải là các tính năng, phải là hoạt động mượt mà, bắt mắt và kiếm ra tiền. Còn test gì đó á, vứt cái đó cho mấy ông khác đi.

Lợi thế khi phát triển theo hướng TDD.

  • Khi phát triển theo hướng này, dự án chạy mượt mà lắm anh em ạ. Khi code coverage 100%, nếu dòng code nào không được coverage thì có 2 lý do có thể cần nhắc, một là dòng code này không cần thiết, hai là viết test chưa đủ. Code không cần thiết thì loại bỏ đi, test chưa đủ thì cần phải bổ sung test, xem lại test case...vân vân và mây mây.
  • Khi có lỗi xảy ra, chúng ta sẽ dễ dàng phát đoán xem lỗi này thuộc module nào, dự đoán được rủi ro và nguy cơ gây ra cho dự án. Công việc maintaince sẽ ngon lành cành đào như một bữa ăn sáng. Không lo khi sửa một dòng code gây chết cả dự án, vì khi chạy test, test case nào đang chết (đỏ) thì nó sẽ báo ngay, và chúng ta sẽ hạn chế được lỗi degrade trong quá trình maintaince.
  • Hai vấn đề đau đầu nhất trong quá trình maintaince đơn giản có thể giải quyết ngon lành khi có test. Vậy viết code trước, rồi viết test sau cũng được, đâu nhất thiết phải viêt test trước theo TDD. Lý do ở đây là, khi viết code rồi viết test, dù ít, dù nhiều, viết code sẽ ảnh hưởng đến việc viết test của bạn (trừ khi ông viết code và ông viết test chẳng nhìn thấy nhau), rồi khi viết test như thế, thì có thể sẽ phải sửa code, sửa code mà chưa có test thì dễ dính lỗi degrade (lỗi sửa code ở chỗ này ảnh hưởng đến chỗ khác và gây lỗi). Ngoài ra sẽ không viết được hết các case....

Kết luận

  • Phát triển theo hướng TDD, là một cách phát triển tốt và hiện đại, nhưng thực tế có rất nhiều khó khăn khi đưa nó vào chạy thực tế. Hy vọng kết hợp được TDD với các mô hình quản lý khác, để xây dựng các mô hình quản lý dự án một cách thích hợp và ngon lành hơn.