- Phần mềm có rất nhiều điều bất định.
- Vì sao lại bất định?
- Lý do lớn nhất là vì tồn tại sự phức tạp của business
- Do tính phức tạp, tình huống liên tục thay đổi và vì thế dự đoán của developer có xác suất cao là sai.
- Tòa tháp được dày công xây dựng sụp đổ và nguyên vẹn biến thành technical debt.
- Lý do nhỏ hơn là do thiếu kiến thức và kinh nghiệm
- Nếu không có kiến thức và kinh nghiệm, developer có thể tự tạo ra technical debt cho chính mình
- Sự phức tạp của business là yếu tố bên ngoài mà developer không thể kiểm soát, trong khi kiến thức và kinh nghiệm là yếu tố bên trong mà developer có thể kiểm soát
- Có ba con đường dành cho developer
- Con đường bi quan cho rằng việc chống lại sự phức tạp là vô nghĩa
- Sẽ nói những câu như 'Dù gì rồi cũng sẽ thay đổi nên cứ làm đi', 'Kỹ thuật là vô nghĩa'
- Vì đây là con đường dễ chịu và thoải mái nên ta có thể vô thức chọn nó
- Con đường phớt lờ sự phức tạp và chỉ nghĩ đến những ảo tưởng lý tưởng
- Tin rằng có thể giải quyết mọi thứ bằng một công nghệ duy nhất mà mình cho là lý tưởng
- Dần có lối tư duy cứng nhắc và đồng nhất
- Đây là con đường developer rất dễ rơi vào nhưng lại khó thoát ra
- Con đường chấp nhận sự phức tạp và chiến đấu trực diện
- Dù chấp nhận rằng không thể hoàn hảo, vẫn tiếp tục tìm con đường tốt hơn
- Đây là con đường khó khăn và cần sự chịu đựng
- Phát triển phần mềm từ trước đến nay luôn chiến đấu với sự phức tạp
- Architecture, methodology, Agile, v.v.
- Tiếp theo sẽ xuất hiện điều gì?
- Phát triển theo định hướng phá bỏ
- Nhìn vào thực tế, rất dễ rơi vào suy nghĩ bi quan rằng đằng nào rồi cũng sẽ bị xóa đi.
- Vì việc tự tay xóa đoạn code mà ta đã dốc sức viết ra, trong khi cảm thấy nó là một sản phẩm thất bại, là điều vô cùng đau đớn
- Hay là đảo ngược cách nghĩ và thay vào đó tạo ra thứ có thể xóa đi một cách dễ dàng thì sao?
- Phá bỏ có phải là điều tốt?
- Nếu không có phá bỏ thì cái mới không thể ra đời
- Trong phần mềm, có thể thấy hai dạng phá bỏ lớn - pivot và refactoring
- Pivot giúp tổ chức và sản phẩm có thể chọn con đường tốt hơn
- Refactoring là việc chắc chắn cần thiết để kéo dài vòng đời của phần mềm hơn nữa
- Vậy phát triển theo định hướng phá bỏ là gì?
- Là phương pháp phát triển chấp nhận sự thật rằng một ngày nào đó code sẽ bị phá bỏ, và phát triển theo định hướng đó
- Hướng đến ba nguyên tắc lớn
- Nếu có sự bất định thì giảm nó nhiều nhất có thể.
- Nếu có thể chọn giữa nhiều cách thì chọn cách dễ phá bỏ hơn.
- Chỉ giữ lại những gì cần thiết. Vì vậy, mọi thứ không cần thiết đều bị xóa bỏ.
- Phân tích -> tách biên giới -> triển khai code -> loại bỏ độ phức tạp
- Cốt lõi là giảm sự bất định do yếu tố bên trong gây ra và chuẩn bị cho sự phá bỏ do yếu tố bên ngoài không thể tránh khỏi
- Tách biên giới
- Sự bất định là tốc độ thay đổi, và có thể tách dựa trên điều này
- Developer phải chuẩn bị cho các yếu tố bên ngoài và đồng thời giảm tối đa tốc độ thay đổi do các yếu tố bên trong gây ra
- Vì tốc độ thay đổi của từng yếu tố có thể khác nhau theo từng tổ chức nên không thể biểu diễn bằng con số cố định -> đo bằng phương pháp heuristic
- Cần quyết định mức độ abstraction để xác định sẽ tách theo tiêu chí nào
- Khả năng phá bỏ
- Khi triển khai, hãy chọn phía dễ phá bỏ hơn theo các nguyên tắc lớn
- Có thể đánh giá khả năng phá bỏ bằng cách xem xét tính độc lập, khả năng nhận thức và khả năng kiểm soát
- Tính độc lập được đánh giá qua mức độ coupling và cohesion, cũng như việc tuân thủ nguyên tắc trách nhiệm đơn lẻ đến đâu
- Khả năng nhận thức là mức độ developer có thể nhìn vào code và hiểu được
- Khả năng kiểm soát là việc đánh giá đó có phải là phạm vi mà developer có thể kiểm soát hay không
- Loại bỏ độ phức tạp
- Cần kiểm tra xem có thứ gì không cần thiết hay không và loại bỏ nó. Tức là cuối cùng trong codebase chỉ nên còn lại những gì cần thiết
- Nếu khó thực hiện do các vấn đề như deadline thì chỉ cần ghi lại rồi làm sau cũng không có vấn đề gì
- Vì yếu tố bên trong có thể kiểm soát được
- Cốt lõi là duy trì tính đơn giản tối đa để chuẩn bị cho sự phá bỏ
- Kỹ thuật phá bỏ code
- Có nhiều nguyên tắc và phương pháp để xóa code cho tốt
- Chia nhỏ từng bước (mẫu refactoring)
- Giữ tính tham chiếu minh bạch
- Tuân thủ nguyên tắc trách nhiệm đơn lẻ
- Tuân thủ nguyên tắc tách biệt interface
- Mẫu strangler fig
- Chuyên biệt hóa method
- Viết code trùng lặp
- Ghi lại tốc độ thay đổi
6 bình luận
Tôi không mấy đồng tình với nhận định rằng nợ mã phát sinh do thiếu kiến thức và kinh nghiệm.
-> Có thể là không đủ thời gian được giao để triển khai yêu cầu, và trong trường hợp làm việc cộng tác thì cũng có khi phải chấp nhận một ít nợ kỹ thuật để hài hòa với người khác; tôi nghĩ tình huống rất đa dạng.
Tôi cũng không chắc việc xem kiến thức và kinh nghiệm là những yếu tố nội tại mà lập trình viên có thể kiểm soát là đúng.
-> Kinh doanh vốn phức tạp nên không thể dự đoán mọi tình huống sẽ xảy ra, cũng không thể học hết mọi khả năng ngay tại từng thời điểm. Ngay cả khi gặp tình huống đó rồi mới học, thì lần sau vẫn có thể phát sinh một vấn đề hoàn toàn mới khiến kiến thức trước đó trở nên không còn hữu dụng nữa.
Xin chào. Cảm ơn bạn đã chia sẻ ý kiến.
Tôi cho rằng chỉ khi nhìn vào những trường hợp cực đoan thì mới có thể thấy được bản chất. Xét trên khía cạnh đó, tôi nghĩ rằng nếu hiểu một cách hoàn toàn đầy đủ về “kiến thức và kinh nghiệm” thì ta có thể tạo ra code không phải là nợ kỹ thuật trong thời gian cho phép.
Việc thiếu thời gian có thể chia thành hai trường hợp. Thứ nhất là theo đúng nghĩa đen, thời gian cần thiết để triển khai là không đủ. Trong trường hợp này, bất kể kiến thức và kinh nghiệm ra sao, thời gian vật lý để viết code là không đủ. Vì vậy ngay từ đầu đây đã là điều kiện khiến mục tiêu không thể đạt được. Thứ hai là không có đủ thời gian để nhận ra điều gì là tốt. Trong trường hợp này, vì không đủ thời gian để tìm hiểu cách triển khai hoặc tìm kiếm một phương án tốt hơn, nên người ta kết thúc công việc bằng cách viết code chỉ với phần kiến thức mình đang có. Khi hoàn thành như vậy, ta biết rằng “có gì đó không ổn”, nhưng lại không biết chính xác phải sửa như thế nào. Nếu có kiến thức chính xác và có được sự tự tin nhờ kinh nghiệm liên quan, thì vấn đề như vậy sẽ không xảy ra.
Tôi nghĩ phần thiếu thời gian được viết ở trên là điều củng cố cho quan điểm của tôi. Tất nhiên, trong thực tế đây là một vấn đề cực kỳ khó. Tôi chỉ đang nói về một trạng thái lý tưởng mà thôi. Trạng thái có đầy đủ hoàn hảo cả kiến thức lẫn kinh nghiệm là rất hiếm, và như bạn nói, rõ ràng cũng có những trường hợp người ta cố ý chấp nhận điều đó vì tổ chức. Có thể sẽ có cảm giác oan ức, nhưng tôi nghĩ rằng nếu “nhìn một cách cực đoan” thì đây vẫn là vấn đề phát sinh do thiếu kiến thức và kinh nghiệm.
Thứ hai, về yếu tố nội tại mà bạn đề cập thì khá đơn giản. “Kinh doanh phức tạp nên không thể dự đoán được tình huống nào sẽ xảy ra…” — phần này trong bài tôi viết chính là câu chuyện về “độ phức tạp của kinh doanh”. Nói cách khác, đó là vấn đề do yếu tố bên ngoài gây ra. Vì là yếu tố bên ngoài nên lập trình viên không thể kiểm soát và cảm thấy sợ hãi. Nếu cũng nhìn điều này theo hướng cực đoan và giả định rằng không có sự phức tạp của kinh doanh, thì thứ còn lại chỉ là code do lập trình viên viết ra. Khi đó, chỉ còn lại vấn đề “kiến thức và kinh nghiệm” mà ta có thể kiểm soát từ bên trong.
Tất nhiên, bài viết của tôi cũng chỉ là ý kiến cá nhân. Hoàn toàn có thể tồn tại những phản ví dụ. Tôi nghĩ việc trao đổi quan điểm là cơ hội để tiến đến một con đường tốt hơn. Mong tiếp tục nhận được nhiều ý kiến từ bạn. Cảm ơn bạn.
Cảm ơn bạn đã trả lời rất nhiệt tình.
Tôi đã đọc bài rất hay. Có vẻ như tùy theo giai đoạn của tổ chức mà việc gì bị xem là tối ưu hóa quá sớm hay over-engineering cũng khác nhau. Điểm khó là đây vừa là đoạn mã kiểu gì rồi cũng phải viết lại, nhưng cũng lại là đoạn mã không biết liệu khoảnh khắc phải viết lại có thực sự đến hay không. Tôi đôi khi cũng dùng câu hỏi: khi nói rằng dịch vụ xxx, tính năng đó sẽ bị loại bỏ, thì mã yyy và dữ liệu nên được đặt ở đâu là phù hợp? để đưa ra phán đoán, nhưng tôi cũng tò mò về cách làm của những người khác.
Tôi có xu hướng nghĩ không chỉ mã nguồn mà cả dữ liệu hoặc schema cũng có thể biến mất hoặc bị thay đổi.
Tôi cũng muốn đưa thêm nội dung về dữ liệu vào, nhưng khá khó để nghĩ ra cho thấu đáo. Vì đây là phần rất quan trọng nên không dễ đụng vào, và cũng khá dè dặt vì lỡ đâu có thể rơi vào địa ngục migration.
Như bạn nói, có vẻ giai đoạn thiết kế ban đầu là cực kỳ quan trọng; cốt lõi có lẽ là làm sao để tích lũy RAW tốt nhất có thể. Hoặc xét ở khía cạnh xóa, kiến trúc event sourcing cũng có thể là một lựa chọn thuận lợi. Tất nhiên, vì tôi chưa từng thực sự áp dụng kiến trúc đó một cách bài bản, nên cũng không chắc nó có thực sự hiệu quả hay không.