Clean Code là gì? Tại sao developer nên quan tâm đến Clean code?


Những năm qua, tôi ngày càng quan tâm đến khái niệm Clean CodeSoftware Craftsmanship. Tuy nhiên, những khái niệm này thực sự có nghĩa là gì và tại sao các nhà phát triển phần mềm nên quan tâm đến chúng, hay thực sự có nên quan tâm đến chúng?





Trong bài này, tôi sẽ thảo luận về các vấn đề khác nhau liên quan đến khái niệm Clean Code và ý nghĩa thực sự của nó, hy vọng cung cấp một số hiểu biết hữu ích dựa trên kinh nghiệm của mình trong quá trình phát triển phần mềm doanh nghiệp.





Clean Code là gì? Ảnh: cleancoders.com
Clean Code là gì? Ảnh: cleancoders.com




Hầu hết mọi thứ trong cuộc sống đều mang tính chủ quan.





Cho 3 người bất kỳ vào một phòng và hầu như bạn luôn có những ý kiến ​​khác nhau về bất kỳ chủ đề nhất định nào.





Nếu có bất cứ điều gì mà con người chúng ta làm tốt, thì đó là khả năng bất đồng của chúng ta. Dù cuối cùng chúng ta có thể đạt được một số đồng thuận, nhưng nếu hỏi bất kỳ ai sau đó và bạn sẽ nhận ra rằng sự đồng thuận không gì khác hơn là một thỏa thuận cho sự bất đồng.





Trong phần lớn sự nghiệp làm phần mềm của mình, tôi tin rằng có một số luật bất thành văn, nếu bạn hỏi bất kỳ nhà phát triển nào về chất lượng code của một nhà phát triển khác, và bất kể họ đã xem hay chưa, họ gần như sẽ nói code dở tệ!





Các nhà phát triển phần mềm (software developer) thậm chí không thể đi đến đồng thuận về ngôn ngữ lập trình nào là tốt nhất.





Tôi đã từng là nhân chứng cho một trong những tranh luận lớn này, tại một thời điểm điên rồ của ban quản lý, ai đó đã quyết định đưa một loạt các Lập trình viên C++, C# và Java vào làm việc tất cả trong một phòng trong nhiều tháng. Tôi có thể nói với bạn rằng, cuộc tranh đấu đã diễn ra trong nhiều tháng, và tôi không nghĩ rằng họ đã đạt được sự đồng thuận!





Những người trong phòng nói trên, thậm chí không thể đi đến thống nhất về cách tốt nhất để pha cà phê hoặc cách phát âm. Tôi có thể nói, Java chắc chắn không được xếp hạng là loại cà phê ưa thích!





Theo như tôi biết, các lập trình viên Java vẫn lập trình bằng trong Java, C# thì vẫn bằng C# và C++ thì vẫn đang cố gắng tìm vị trí các con trỏ và tại sao bộ đệm lại bị tràn!





Clean Code là gì?





Điểm chung giữa 3 nhóm này là họ đều tin rằng chọn lựa và ngôn ngữ lập trình ưa thích của mình đã giúp họ viết được Clean Code!





Điều này chỉ khiến ai đó giả định rằng Clean Code, chỉ là một vấn đề mang tính chủ quan cao. Nó cũng có nghĩa là một cái gì đó hoàn toàn khác, tùy thuộc vào vị trí trong chuỗi Phát triển Phần mềm mà bạn ngồi.





Qua nhiều năm, tôi đã đi tới kết luận rằng có rất nhiều định nghĩa về clean code mà các developer dường như đồng ý với nhau.





Clean code là code dễ hiểu và dễ thay đổi.





Định nghĩa phổ biến nhất của clean code là code dễ hiểu và dễ thay đổi. Nhìn bề ngoài, điều này có thể khiến người ta phải gật đầu và vuốt cằm tán dương, nhưng cuối cùng thì đó là một trong những định nghĩa nói lên điều gì đó mà không thực sự nêu rõ bất cứ điều gì.





Theo định nghĩa đó, hoàn toàn bất kỳ code nào, đều có thể được phân loại là clean code. Ngay cả những code dưới đây có thể được coi là clean code. Nó dễ hiểu và tôi chắc chắn có thể thay đổi nó một cách dễ dàng.





void FixTheCondition(object aCondition, object someCriteria)
{
if(aCondition != someCriteria)
{
doSomethingToFix(aCondition)
}
}




Tôi chắc chắn rằng code này tuân thủ một số tiêu chí khiến nó dễ hiểu





  • dễ hiểu luồng thực thi của toàn bộ ứng dụng
  • dễ hiểu cách các đối tượng khác nhau cộng tác với nhau
  • dễ hiểu vai trò và trách nhiệm của từng lớp
  • dễ hiểu những gì mỗi phương pháp làm
  • dễ hiểu mục đích của từng biểu thức và biến là gì




Code này cũng dễ dàng mở rộng và refactor cũng như dễ dàng sửa các lỗi trong cơ sở mã. Điều này có thể đạt được nếu người thực hiện thay đổi hiểu được code và cũng cảm thấy tin tưởng rằng những thay đổi được giới thiệu trong code không phá vỡ bất kỳ chức năng hiện có nào.





Ai đó có thể lập luận rằng để code dễ thay đổi:





  • Các lớp và phương thức nhỏ và chỉ có trách nhiệm duy nhất
  • Các lớp có API công khai rõ ràng và ngắn gọn
  • Các lớp và phương thức có thể dự đoán được và hoạt động như mong đợi
  • Có dễ dàng kiểm tra và có các bài kiểm thử đơn vị (hoặc dễ dàng viết các bài kiểm thử)
  • Các bài kiểm thử dễ hiểu và dễ thay đổi




Đoạn code trên, phù hợp với tất cả các tiêu chí. Chúng ta có thể coi đây là Clean code, nhưng nó có thực sự như vậy không?





Trong cả sự nghiệp của mình, tôi đã thấy nhiều cách diễn giải về cùng một đoạn code, trong hàng trăm ứng dụng. Ngay cả trong những thứ mà một số người sẽ cân nhắc và thậm chí được bán như một phần mềm doanh nghiệp có quy mô lớn. Trên thực tế, nếu bạn mở hầu hết bất kỳ ứng dụng phần mềm nào, bạn có thể sẽ tìm thấy các ví dụ tương tự.





Điều này có nghĩa là code thực sự clean code?





Ai quan tâm đến Clean code?





Một trong những vấn đề lớn nhất trong ngành phát triển phần mềm là không có nhiều người thực sự quan tâm đến clean code.





Đúng là có thể một tỷ lệ nhất định các nhà phát triển quan tâm đến clean code, nhưng theo kinh nghiệm, không phải tất cả họ đều làm trong cùng một nhóm và hầu hết có thể không ở nhóm của bạn.





Sự thật là, clean code cần thời gian, công sức, chú ý và chăm sóc, cả bốn giá trị mà phía kinh doanh không thực sự quan tâm. Lý do là Clean Code nằm trong tiêu chí Chất lượng của Tam giác chất lượng, một biến thể Tam giác quản lý dự án.





Tam giác chất lượng (Quality triangle)
Tam giác chất lượng (Quality triangle)




Tam giác dự án thể hiện các ràng buộc về thời gian, chi phí và chất lượng hoặc phạm vi phải được quản lý trong quá trình thực hiện dự án. Mỗi ràng buộc được kết nối và việc di chuyển một điểm của tam giác sẽ tác động đến hai điểm còn lại.





Vì vậy, bạn có thể cung cấp code và triển khai ứng dụng nhanh chóng để đáp ứng các mục tiêu kinh doanh hoặc bạn có thể mất nhiều thời gian hơn để đảm bảo chất lượng của code được phát hành. Vấn đề là bạn không bao giờ có thể có cả hai, một thời điểm nào đó, cái gì đó phải bị gạt bỏ. Trong phần mềm, thường xuyên nhất chính là chất lượng.





Lý do chính tại sao, code thường là khía cạnh ít được nhìn thấy nhất của phát triển phần mềm.





Chắc chắn các nhà phát triển quan tâm đến nó, bạn sẽ thường nghe các nhà phát triển nói về “code đẹp” hoặc thậm chí là “code thanh lịch”, nhưng rất hiếm khi và tôi sẽ là người đầu tiên thừa nhận rằng tôi chưa bao giờ nghe nhận xét của người dùng cuối về tính thẩm mỹ của code. Sự thật là họ không quá quan tâm.





Điều duy nhất mà người dùng thường quan tâm với các ứng dụng phần mềm là nó giải quyết được vấn đề mà họ gặp phải. Bạn không cần code sạch, đẹp hoặc thậm chí thanh lịch để giải quyết vấn đề, điều này có thể được thực hiện chỉ với code.





Viết phần mềm Máy tính là một trong những hoạt động sáng tạo thuần túy nhất trong lịch sử loài người. Các lập trình viên không bị ràng buộc bởi các giới hạn thực tế như các định luật vật lý; chúng ta có thể tạo ra những thế giới ảo thú vị với những hành vi không bao giờ có thể tồn tại trong thế giới thực. Lập trình không yêu cầu kỹ năng thể chất hoặc sự phối hợp tuyệt vời như múa Ba lê hay Bóng đá. Tất cả những gì lập trình yêu cầu là đầu óc sáng tạo và khả năng sắp xếp suy nghĩ của bạn. Nếu bạn có thể hình dung một hệ thống, bạn có thể triển khai nó trong một chương trình máy tính.

Triết lý thiết kế phần mềm – John Ousterhout




Quản lý độ phức tạp





Nhiều người nghĩ rằng nhiệm vụ chính của một nhà phát triển phần mềm là viết code nhằm tạo ra các ứng dụng, điều này không hoàn toàn chính xác, bởi vì nhiệm vụ chính của một nhà phát triển phần mềm là quản lý sự phức tạp (Complexity).





Các nhà phát triển phần mềm nên sử dụng code để giấu hoặc loại bỏ sự phức tạp khi phát triển ứng dụng.





Sự phức tạp là kẻ thù của các ứng dụng phần mềm có quy mô lớn, mạnh và đáng tin cậy và các nhà phát triển cần đảm bảo họ không viết mã phức tạp.





Sự phức tạp là bất cứ thứ gì liên quan đến cấu trúc của một hệ thống phần mềm khiến nó khó hiểu và khó sửa đổi hệ thống.





Chìa khóa để viết clean code tốt là hiểu các triệu chứng của sự phức tạp và cách tránh nó.





1. Khuếch đại thay đổi (change amplification)





Một thay đổi có vẻ đơn giản đòi hỏi phải sửa đổi code ở nhiều nơi khác nhau. Mục tiêu ở đây là giảm số lượng code bị ảnh hưởng bởi mỗi quyết định thiết kế, giảm tác động của những thay đổi trong quyết định thiết kế đòi hỏi nhiều sửa đổi code.





2. Tải nhận thức (cognitive load)





Đây là mức thông tin hoặc kiến ​​thức hệ thống bắt buộc mà nhà phát triển cần biết để hoàn thành một nhiệm vụ. Yêu cầu tải nhận thức càng cao, càng làm tăng khả năng phát sinh lỗi và nhà phát triển sẽ cần thời gian để hoàn thành nhiệm vụ.





3. Quản lý các ẩn số không xác định (unknown unknowns)





Triệu chứng thứ ba của sự phức tạp là không rõ ràng những đoạn code nào phải được sửa đổi để hoàn thành một nhiệm vụ, hoặc thông tin nào mà nhà phát triển phải có để thực hiện nhiệm vụ thành công.





Trong ba biểu hiện của sự phức tạp, những ẩn số không xác định cho đến nay là tệ nhất. Điều này là do một ẩn số không xác định thường có nghĩa là có điều gì đó mà nhà phát triển cần biết, nhưng không có cách nào để họ tìm ra hoặc thậm chí liệu có vấn đề hay không.





Trong tất cả các khả năng, họ sẽ không phát hiện ra điều đó cho đến khi lỗi xuất hiện sau khi họ thực hiện thay đổi.





Khuếch đại thay đổi và tải nhận thức gây khó chịu và chắc chắn sẽ dẫn đến tăng chi phí thay đổi và bảo trì, nhưng chúng vẫn có thể quản lý được.





Tuy nhiên, với những ẩn số không xác định, một tổ chức thực sự không biết họ nên làm gì, hoặc tác động có thể xảy ra mà một thay đổi sẽ có hoặc liệu giải pháp được đề xuất có hiệu quả hay không.





Nguyên nhân của sự phức tạp





Nguyên nhân của sự phức tạp không phải lúc nào cũng trực tiếp do code xấu, cũng giống như việc code xấu không phải lúc nào cũng là kết quả trực tiếp của các nhà phát triển kém hoặc thậm chí có kỹ năng kém.





Tôi đã chứng kiến ​​nhiều nhà phát triển có tay nghề cao tạo ra code xấu và điều này không phải lúc nào cũng xảy ra.





Nhiều người tin rằng sự phức tạp được gây ra bởi hai điều: Sự phụ thuộc và Sự mơ hồ. 





Trên thực tế, nếu bạn nói chuyện với nhiều nhà phát triển về nguyên nhân của sự phức tạp, họ có thể sẽ làm nổi bật cả hai điểm này.





Sự thật là, mặc dù chúng đều làm tăng thêm độ phức tạp ở cấp độ code, nhưng chúng thường không phải là nguyên nhân trực tiếp giải thích tại sao độ phức tạp được nhắc đến ngay từ đầu.





Hai nguyên nhân này gây ra sự phức tạp trong các cơ sở mã, không phải lúc nào cũng do các lựa chọn hoặc hành động của nhà phát triển, mà thường là kết quả của các lực lượng bên ngoài tác động và buộc các nhà phát triển phải đưa ra các quyết định tồi.





Để hiểu và xác định những lực lượng này, trước tiên chúng ta nên hiểu các nguyên tắc đằng sau Sự phụ thuộc (Dependency) và Sự mơ hồ (Obscurity).





Ở mức độ cao, sự phụ thuộc tồn tại khi một đoạn code nhất định không thể được hiểu và sửa đổi một cách riêng biệt: code liên quan theo một cách nào đó với code khác và code khác phải được xem xét và / hoặc sửa đổi nếu code đã cho bị thay đổi.





Sự phụ thuộc là một phần cơ bản của phần mềm và không bao giờ có thể được loại bỏ hoàn toàn.





Chúng tôi thường giới thiệu các phụ thuộc như một phần của quá trình thiết kế phần mềm. Mỗi khi một lớp hoặc hàm mới được tạo, nó sẽ tạo ra các phụ thuộc xung quanh API cho lớp đó.





Tuy nhiên, một trong những mục tiêu của thiết kế phần mềm là giảm số lượng phụ thuộc và làm cho các phụ thuộc trở nên đơn giản và rõ ràng nhất có thể.





Sự mơ hồ xảy ra khi thông tin quan trọng không rõ ràng, thường gắn liền với các phụ thuộc, nơi không phải lúc nào cũng rõ ràng rằng có một phụ thuộc tồn tại. Nguyên nhân hàng đầu dẫn đến sự mơ hồ là sự không nhất quán, cũng thường là kết quả của việc tài liệu không hoàn chỉnh hoặc không đầy đủ. Sự mơ hồ cũng thường là một vấn đề thiết kế.





Nhiều tổ chức và nhà phát triển lập luận rằng nếu một hệ thống có thiết kế clean và rõ ràng, thì nó sẽ không cần nhiều tài liệu. Thường người ta lầm tưởng rằng nếu cần có tài liệu mở rộng, thì đây là một cảnh báo cho thấy thiết kế không hoàn toàn đúng.





Nhiều tổ chức chống lại điều này bằng cách triển khai các phương pháp luận Agile mà công bố một cach nhầm lẫn, rằng không cần tài liệu vì giá trị 2 trong 4 giá trị của Tuyên ngôn Agile nói rằng:





Phần mềm chạy được ưu tiên hơn Tài liệu toàn diện





Điều thường được đặt trước bằng cách nêu nguyên tắc 1 cũng nói:





Cá nhân và Tương tác ưu tiên hơn Quy trình và Công cụ





Đây thường là điểm khi các yếu tố phụ thuộc và mơ hồ len lỏi vào các cơ sở mã, bởi những gì hầu như luôn bị bỏ qua trong code và không phải lúc nào cũng dễ dàng giải mã khi đọc code, đó là Ý định, Lập luậnMục tiêu.





Sự phụ thuộc và Sự mơ hồ là nguyên nhân cho ba biểu hiện của sự phức tạp. Sự phụ thuộc đưa đến sự khuếch đại thay đổi và tải trọng nhận thức cao. Sự mơ hồ tạo ra những ẩn số chưa biết, góp phần vào tải trọng nhận thức. Nếu chúng ta có thể tìm thấy các kỹ thuật thiết kế để giảm thiểu sự phụ thuộc và mơ hồ, chúng ta có thể giảm độ phức tạp của phần mềm.





Độ phức tạp gia tăng lặp đi lặp lại





Sự phức tạp không chỉ xảy ra thôi đâu, mà nó tích lũy dần dần. Bản thân một sự phụ thuộc đơn lẻ hoặc một chút mơ hồ không có ảnh hưởng đáng kể đến khả năng bảo trì của một hệ thống phần mềm.





Sự phức tạp xuất hiện do hàng trăm hoặc hàng nghìn phụ thuộc nhỏ và những mơ hồ tích tụ theo thời gian. Cuối cùng, có rất nhiều thay đổi có thể xảy ra đối với một hệ thống đều bị ảnh hưởng bởi một số thay đổi trong số đó.





Đó là bản chất phức tạp ngày càng tăng khiến việc kiểm soát khó khăn và thường rất phức tạp để xác định.





Các nhóm phát triển phần mềm dễ dàng đánh lừa bản thân rằng một chút phức tạp được tạo ra bởi một thay đổi hiện tại không phải là vấn đề lớn. Mặt hạn chế, là điều này trở nên dễ dàng cho phép về mặt văn hóa và sau đó nó tích lũy nhanh chóng!





Một khi sự phức tạp đã tích tụ, thật khó để loại bỏ vì việc sửa chữa một sự phụ thuộc hoặc sự mơ hồ duy nhất sẽ không tạo ra sự khác biệt.





Cuối cùng cả team xôn xao. Họ báo cho ban quản lý rằng họ không thể tiếp tục phát triển trong cơ sở mã đáng sợ này. Họ yêu cầu thiết kế lại.

Robert C Martin – Clean Code




Kết luận





Sự phức tạp đến từ sự tích tụ của các phụ thuộc và sự mơ hồ, dẫn đến sự khuếch trương thay đổi, tải nhận thức cao và những ẩn số không xác định! Điều này thường dẫn đến nhiều sửa đổi hơn để triển khai từng tính năng mới và để khắc phục các sự cố được tạo ra bằng cách triển khai các tính năng trước đó.





Các nhà phát triển được yêu cầu dành nhiều thời gian hơn để thu thập đủ thông tin để thực hiện các thay đổi một cách an toàn, thường gặp khó khăn vì họ không bao giờ có thể tìm thấy tất cả thông tin họ cần vì phần lớn thông tin đó không tồn tại!





Về cốt lõi, clena code và các phương pháp thực hành là nhằm loại bỏ hoặc ít nhất là cố gắng giảm bớt sự phức tạp. Mặc dù các giải pháp clean code có vẻ thanh lịch và hiệu quả, nhưng chúng không phải lúc nào cũng dễ dàng và thường là kết quả trực tiếp của việc chống lại sự phức tạp!





Xem video Clean Code in Practices từ anh Tú Phạm (Head of Engineering) tại buổi Technical Event #12 do Gamba tổ chức.











Tham khảo: Garywoodfine