- Độ phức tạp là yếu tố nguy hiểm nhất trong phát triển phần mềm
- Hiệu quả thực sự đến từ cách tiếp cận thực dụng để tránh độ phức tạp, như “giải pháp 80/20”
- Điều quan trọng là giữ thái độ cân bằng và linh hoạt đối với kiểm thử và tái cấu trúc
- Nhấn mạnh việc tận dụng công cụ và hình thành thói quen viết mã dễ đọc, dễ bảo trì
- Cảnh giác với trừu tượng hóa quá mức và các trào lưu, đồng thời khuyến nghị theo đuổi sự đơn giản
Giới thiệu
- Bài viết này là tập hợp những suy nghĩ của một nhà phát triển có não Grug, đúc kết từ những điều học được qua kinh nghiệm sau thời gian dài làm phần mềm
- Nhà phát triển có não Grug tự cho rằng mình không thông minh, nhưng đã học được rất nhiều qua nhiều năm lập trình
- Với mong muốn người khác có thể học từ sai lầm, tác giả chia sẻ những nhận ra của mình theo cách dễ hiểu và hài hước
- Chính độ phức tạp mới là kẻ thù lớn nhất của đời lập trình
- Độ phức tạp lén lút xâm nhập vào codebase, khiến cả những đoạn mã ban đầu dễ hiểu cũng dần trở nên không thể chỉnh sửa
Đối phó với con quỷ của độ phức tạp
- Độ phức tạp len vào một cách im lặng như một linh hồn vô hình, và quản lý dự án hay các nhà phát triển không phải Grug thường khó nhận ra điều đó
- Cách tốt nhất để ngăn độ phức tạp là nói “không”
- “Tôi sẽ không làm tính năng này”
- “Tôi sẽ không đưa lớp trừu tượng này vào”
- Dĩ nhiên, xét về sự nghiệp thì việc hô “có” có thể có lợi hơn, nhưng nhà phát triển có não Grug coi trọng việc thành thật với chính mình
- Tùy tình huống vẫn cần thỏa hiệp (“ok”), và trong những trường hợp đó tác giả thích giải quyết vấn đề đơn giản bằng giải pháp 80/20 (áp dụng nguyên lý Pareto)
- Không cần nói hết mọi thứ với quản lý dự án mà thực tế vẫn làm theo kiểu 80/20 cũng là một chiến lược khôn ngoan
Cấu trúc mã và trừu tượng hóa
- Đơn vị chia tách phù hợp của mã (cutpoint) sẽ tự nhiên lộ ra theo thời gian, vì vậy tốt hơn là tránh trừu tượng hóa sớm
- Một cutpoint tốt lý tưởng là có giao diện hẹp với phần còn lại của hệ thống
- Nỗ lực trừu tượng hóa quá sớm rất dễ thất bại, và các lập trình viên giàu kinh nghiệm thường chỉ từ từ cấu trúc hóa khi hình dạng của mã đã phần nào ổn định
- Các nhà phát triển ít kinh nghiệm hoặc kiểu “não to” thường cố trừu tượng hóa quá mức ở giai đoạn đầu của dự án, để lại gánh nặng bảo trì
Chiến lược kiểm thử
- Sự cân bằng trong mức độ ám ảnh với kiểm thử là rất quan trọng
- Tác giả thích viết test sau khi đã tạo nguyên mẫu và khi mã đã tương đối ổn định
- Unit test có được dùng ở giai đoạn đầu, nhưng trên thực tế tầng trung gian (kiểm thử tích hợp) mới mang lại hiệu quả lớn nhất
- Kiểm thử end-to-end cũng cần thiết, nhưng nếu quá nhiều sẽ thành không thể bảo trì, nên chỉ giữ lại một số ít luồng thật sự cần thiết
- Khi có báo cáo lỗi thì phải thêm test tái hiện lỗi trước rồi mới sửa lỗi
Quy trình, agile, tái cấu trúc
- Agile không tệ với nhà phát triển Grug, cũng không phải thứ tệ nhất, nhưng đặt kỳ vọng quá mức vào “thầy pháp agile” thì rất nguy hiểm
- Tạo nguyên mẫu, công cụ và đồng đội tốt mới thực sự là các yếu tố thành công quan trọng hơn
- Tái cấu trúc cũng là một thói quen tốt, nhưng những đợt tái cấu trúc lớn và gượng ép thì rất rủi ro
- Việc cố ép đưa vào các lớp trừu tượng phức tạp ngược lại còn dẫn tới thất bại dự án
Bảo trì, chủ nghĩa hoàn hảo và sự khiêm tốn
- Việc xé ra làm lại hệ thống hiện có mà không có lý do là rất nguy hiểm, và tùy tiện loại bỏ những cấu trúc “không biết vì sao nó tồn tại” không phải là thói quen tốt
- Chủ nghĩa lý tưởng mơ về mã hoàn hảo trên thực tế phần lớn gây ra vấn đề
- Càng có kinh nghiệm, người ta càng cảm nhận rõ rằng cần tôn trọng đoạn mã đang chạy được
Công cụ và năng suất
- Công cụ phát triển tốt (IDE autocomplete, debugger, v.v.) có thể nâng năng suất lên rất nhiều, và điều quan trọng là phải hiểu sâu cách dùng chúng
- Tác giả nhấn mạnh rằng giá trị thực tế của hệ thống kiểu nằm ở “tự động hoàn thành” và ngăn lỗi, còn trừu tượng hóa và generic quá mức thì ngược lại lại nguy hiểm
Phong cách mã và sự lặp lại
- Khuyến nghị phong cách như tách điều kiện ra nhiều dòng để mã dễ đọc và dễ debug hơn
- Tôn trọng nguyên tắc DRY (Don’t Repeat Yourself), nhưng nhấn mạnh rằng cân bằng quan trọng hơn việc cố gắng loại bỏ mã lặp bằng mọi giá
- Trong nhiều trường hợp, lặp lại đơn giản còn tốt hơn một cách triển khai DRY phức tạp
Nguyên tắc thiết kế phần mềm
- Thay vì nguyên tắc SoC (tách biệt mối quan tâm), tác giả chuộng tính cục bộ của hành vi, và cho rằng “mã thực hiện hành vi đó nên nằm ở chính đối tượng đó thì sẽ dễ bảo trì hơn”
- Cảnh báo rằng callback/closure, hệ thống kiểu, generic, trừu tượng hóa v.v. chỉ nên được dùng vừa đủ và đúng chỗ
- Lạm dụng closure trong JavaScript có thể tạo ra “địa ngục callback”
Logging, vận hành
- Logging rất quan trọng; nên ghi ở các nhánh chính, và trong môi trường cloud thì nên cấu hình để có thể truy vết bằng request ID v.v.
- Nếu có thể tận dụng mức log động và log theo từng người dùng thì sẽ rất hữu ích cho việc lần theo sự cố khi vận hành
Đồng thời, tối ưu hóa
- Với đồng thời, tác giả chỉ tin vào những mô hình đơn giản nhất có thể (web request không trạng thái, hàng đợi worker tách biệt, v.v.)
- Chỉ nên tối ưu hóa thật sự sau khi đã có dữ liệu profile hiệu năng thực tế
- Cần cẩn trọng với các chi phí ẩn như network I/O; chỉ nhìn độ phức tạp CPU thôi là rất nguy hiểm
Thiết kế API
- Một API tốt phải dễ dùng, và thiết kế hay trừu tượng hóa quá phức tạp sẽ làm hỏng trải nghiệm của lập trình viên
- Tác giả khuyến nghị cấu trúc gồm “API đơn giản phù hợp với use case” và “API phân tầng cho phép xử lý cả các trường hợp phức tạp”
Phát triển parser
- Recursive descent parser bị đánh giá thấp trong học thuật, nhưng lại là cách phù hợp nhất và dễ hiểu nhất cho mã sản phẩm thực tế
- Theo kinh nghiệm phát triển parser trong đa số trường hợp, parser sinh bởi công cụ thường cho ra kết quả quá phức tạp, thậm chí phản tác dụng trong việc giải quyết vấn đề
- Tác giả xem "Crafting Interpreters" là cuốn sách được khuyến nghị hàng đầu, với rất nhiều lời khuyên thực tiễn
Frontend và trào lưu
- Frontend hiện đại (React, SPA, GraphQL, v.v.) lại thường gọi thêm con quỷ của độ phức tạp và trong nhiều trường hợp là không cần thiết
- Bản thân Grug thích cách giảm độ phức tạp thông qua các công cụ đơn giản như htmx, hyperscript
- Ở frontend luôn có các thử nghiệm mới liên tục xuất hiện, nhưng cần lưu ý rằng nhiều thứ chỉ là lặp lại các ý tưởng cũ
Yếu tố tâm lý, hội chứng kẻ mạo danh
- Phần lớn lập trình viên thường cảm thấy “mình không biết mình đang làm gì”, và cần thoát khỏi hiện tượng FOLD (Fear Of Looking Dumb)
- Khi một lập trình viên senior công khai nói rằng “cái này ngay cả tôi cũng thấy khó, quá phức tạp”, thì các lập trình viên junior cũng có thể bớt áp lực hơn
- Hội chứng kẻ mạo danh là cảm xúc rất phổ biến, và tác giả khích lệ rằng ai cũng có thể tiếp tục học hỏi và trưởng thành
Kết luận
- Trong lập trình, độ phức tạp luôn là thứ phải cảnh giác, và việc giữ sự đơn giản là cốt lõi của phát triển thành công
- Kinh nghiệm, việc tận dụng công cụ hiệu quả, sự khiêm tốn và thái độ tôn trọng đoạn mã thực sự chạy được sẽ dẫn tới cách phát triển hiệu quả và có giá trị hơn về lâu dài
- "Độ phức tạp rất, rất tệ" — hãy luôn ghi nhớ câu này
1 bình luận
Ý kiến trên Hacker News