Phát triển phần mềm dài hạn (long term)
(berthub.eu)- Phần mềm hiện đại được cập nhật thường xuyên thông qua triển khai liên tục (CD) và kiểm thử tự động (CI), nhưng “phần mềm được sử dụng trong dài hạn” cần một cách tiếp cận khác
- Ví dụ: nhà máy điện hạt nhân, máy bay, máy trợ tim, hệ thống bầu cử, v.v.
- Trong các lĩnh vực mà độ tin cậy và tính ổn định là quan trọng, người ta ưu tiên sự ổn định và các thay đổi có thể dự đoán hơn là thay đổi liên tục
- Ví dụ: nhà máy điện hạt nhân, máy bay, máy trợ tim, hệ thống bầu cử, v.v.
Các nguyên tắc cốt lõi của phát triển phần mềm dài hạn
Phụ thuộc (Dependencies)
- Các phụ thuộc của phần mềm là yếu tố quan trọng đối với thành công dài hạn
- Phần mềm phải tính đến sự tương tác với thế giới bên ngoài, và những lựa chọn nền tảng như ngôn ngữ lập trình là rất quan trọng
- Hiểu các tầng phụ thuộc của phần mềm
- Thế giới bên ngoài: phần mềm phía client mà chúng ta không thể kiểm soát (ví dụ: trình duyệt, v.v.).
- Lựa chọn nền tảng: những yếu tố chỉ có thể thay đổi nếu phải viết lại toàn bộ stack, như ngôn ngữ lập trình.
- Framework: những thứ gắn chặt với codebase như Spring Framework, React, v.v. Có thể thay đổi, nhưng chi phí rất cao.
- Cơ sở dữ liệu: phần lớn có thể thay thế, nhưng cần tinh chỉnh và công sức đáng kể.
- Thư viện hỗ trợ: các thư viện có thể thay thế, cung cấp những chức năng cụ thể.
- Theo thời gian, các phụ thuộc và thế giới bên ngoài sẽ thay đổi:
- Thay đổi ở phụ thuộc có thể dẫn đến sửa code hoặc thay đổi hành vi.
- Các bản phát hành major version mới có thể gây ra vấn đề tương thích.
- Có rủi ro dự án bị ngừng hoặc biến mất.
- Rủi ro bảo mật: phụ thuộc có thể bị tác nhân xấu xâm hại (npm, PyPI, v.v.).
- Thương mại hóa: chủ sở hữu mới từ quỹ đầu tư mạo hiểm (VC) chuyển sang mô hình trả phí.
- Vấn đề xung đột giữa các phụ thuộc.
- Những điểm cần kiểm tra khi chọn phụ thuộc cho mục tiêu sử dụng dài hạn:
- Trình độ kỹ thuật: có thể nhìn mã nguồn để đánh giá chất lượng hay không.
- Tệp người dùng: kiểm tra ai đang sử dụng.
- Mục đích phát triển: xác định ai là người phát triển và mục tiêu là gì.
- Hỗ trợ tài chính: có được tài trợ hay không và nguồn tài trợ đến từ đâu.
- Bảo trì: kiểm tra xem có các bản phát hành bảo mật định kỳ hay không.
- Cộng đồng có khả năng tiếp quản việc bảo trì hay không.
- Bản thân mình có thể trực tiếp bảo trì hay không.
- Khi cần, có nên hỗ trợ tài chính để đảm bảo tính bền vững của dự án hay không.
- Phụ thuộc của phụ thuộc:
- Cũng cần xem xét lịch sử bảo mật của các phụ thuộc cấp dưới.
- Cách tiếp cận thực tế
- Giới hạn phụ thuộc:
- Dự án có hơn 1600 phụ thuộc có khả năng cao sẽ trở nên bất ổn khi code thay đổi quá nhanh.
- Với dự án có vô số phụ thuộc, thậm chí còn khó biết chính xác mình đang triển khai đoạn mã nào.
- Bổ sung thận trọng:
- Khi thêm phụ thuộc, hãy tự áp một mức độ khó kỹ thuật để có thời gian xem xét một cách tự nhiên.
- Trong các dự án dài hạn, nên tránh những phụ thuộc không thực sự cần thiết.
- Giới hạn phụ thuộc:
Phụ thuộc thời gian chạy (Runtime Dependencies)
- Những gì bàn tới ở trên chỉ giới hạn ở phụ thuộc build/compile.
- Tuy nhiên, các dự án hiện đại thường còn bao gồm phụ thuộc thời gian chạy:
- Ví dụ: Amazon S3, Google Firebase.
- Một số gần như được coi là tiêu chuẩn thực tế (như S3).
- Nhưng phần lớn phụ thuộc thời gian chạy có tính chất khóa chặt (lock-in) vào một dịch vụ cụ thể.
- Sau 10 năm, việc tìm một giải pháp thay thế cho dịch vụ đang dùng hiện tại có thể tốn chi phí cực lớn.
- Cần giảm thiểu hoặc đưa về rỗng danh sách phụ thuộc vào dịch vụ bên thứ ba:
- Đặc biệt trong phát triển phần mềm cloud native, việc dùng nhiều dịch vụ bên thứ ba cao cấp là chuyện phổ biến.
- Với dự án dài hạn, các phụ thuộc như vậy đi kèm rủi ro lớn.
- Phụ thuộc vào dịch vụ ở thời điểm build cũng là một yếu tố quan trọng:
- Ví dụ: nếu
npm installkhông còn hoạt động, bản thân việc build phần mềm sẽ trở nên bất khả thi. - Điều này có thể làm suy giảm nghiêm trọng khả năng tái sử dụng của dự án.
- Ví dụ: nếu
- Rà soát kỹ phụ thuộc thời gian chạy:
- Nhận thức các vấn đề lock-in tiềm ẩn, đồng thời giảm hoặc loại bỏ phụ thuộc.
- Đảm bảo khả năng duy trì dài hạn:
- Cần cân nhắc trước khả năng thay thế dịch vụ cloud hoặc dịch vụ bên thứ ba.
Kiểm thử, kiểm thử, và kiểm thử
- Sự cần thiết của kiểm thử là nguyên tắc cơ bản mà ai cũng đồng ý:
- Viết càng nhiều kiểm thử càng tốt.
- Không phải mọi kiểm thử đều có giá trị như nhau, nhưng hiếm khi phải hối tiếc vì đã viết kiểm thử.
- Đặc biệt với các dự án có nhiều phụ thuộc, kiểm thử là bắt buộc:
- Khi phụ thuộc thay đổi hoặc bị drift, kiểm thử giúp phát hiện vấn đề sớm.
- Vai trò của kiểm thử
- Hỗ trợ giải quyết vấn đề:
- Có thể nhanh chóng điều chỉnh theo tình huống thay đổi.
- Hỗ trợ refactor:
- Mang lại sự tự tin khi loại bỏ hoặc thay đổi các phụ thuộc trong code.
- Hữu ích cho bảo trì dài hạn:
- Ngay cả sau khi việc phát triển bị dừng hơn 3 năm, vẫn có thể dùng kiểm thử để xác nhận hệ thống còn hoạt động.
- Kiểm tra xem chức năng có còn được giữ nguyên trên compiler, runtime và hệ điều hành mới hay không.
- Hỗ trợ giải quyết vấn đề:
- Kiểm thử không phải là chi phí mà là đầu tư
- Viết thêm nhiều kiểm thử hơn:
- Kiểm thử là nền tảng của bảo trì và ổn định.
- Khi sửa hoặc mở rộng code, kiểm thử đóng vai trò hỗ trợ tinh thần rất lớn.
- Viết thêm nhiều kiểm thử hơn:
Độ phức tạp: trùm cuối của phát triển phần mềm
- Độ phức tạp là kẻ thù tối hậu của phát triển phần mềm:
- Ngay cả những lập trình viên hay đội ngũ giỏi nhất cũng có thể gục ngã trước độ phức tạp.
- Do ảnh hưởng của entropy và hành vi con người, độ phức tạp luôn tăng lên.
- Nếu không chủ động quản lý độ phức tạp, dự án có thể rơi vào trạng thái không thể duy trì.
- Mối tương quan giữa độ phức tạp và lượng mã
- Lượng mã và độ phức tạp:
- Khi lượng mã còn ít, dù hơi phức tạp vẫn có thể quản lý được.
- Càng nhiều code thì càng phải giữ tính đơn giản để còn kiểm soát được.
- Mức độ phức tạp có thể quản lý phải nằm trong năng lực của đội ngũ và bên trong “tam giác màu xanh lá”.
- Giới hạn của độ phức tạp:
- Dù tăng số lượng người trong nhóm hay thuê các lập trình viên cực kỳ giỏi, vẫn có giới hạn trong việc xử lý độ phức tạp.
- Một khi vượt quá giới hạn đó, dự án sẽ rơi vào trạng thái không thể bảo trì.
- Lượng mã và độ phức tạp:
- Vì sao code luôn di chuyển về “góc trên bên phải” (trên đồ thị):
- Nhiều yêu cầu tính năng hơn.
- Cố tối ưu hóa không cần thiết.
- Khi sửa bug, thay vì giảm độ phức tạp hiện có thì lại thêm mã mới.
- Chi phí của thiết kế API sai:
- Ví dụ: hàm
CreateFiletrong hầu hết trường hợp lại không tạo file. - Sự mơ hồ như vậy làm tăng gánh nặng nhận thức và khả năng mắc lỗi.
- Ví dụ: hàm
- Chiến lược quản lý độ phức tạp
- Refactor sớm và thường xuyên:
- Loại bỏ mã không cần thiết và dành thời gian để đơn giản hóa.
- Đầu tư vào kiểm thử:
- Càng nhiều kiểm thử thì việc giảm độ phức tạp càng dễ hơn.
- Tầm quan trọng của quản lý độ phức tạp:
- Nếu không nỗ lực từ trước để đơn giản hóa, dự án dài hạn cuối cùng có nguy cơ rơi vào “trạng thái không thể bảo trì”.
- Refactor sớm và thường xuyên:
Hãy viết code nhàm chán và đơn giản. Còn đơn giản hơn nữa. Và còn nhàm chán hơn nữa.
“Debug còn khó gấp đôi việc viết chương trình. Vì vậy nếu bạn viết code thông minh hết mức có thể, thì làm sao bạn có thể debug nó?” - Brian Kernighan
- Viết code siêu nhàm chán và rõ ràng:
- Ưu tiên code ngây thơ (näive) nhưng có thể hiểu được một cách trực quan.
- “Tối ưu hóa quá sớm là nguồn gốc của mọi điều xấu.”
- Chỉ tối ưu hóa khi thực sự cần:
- Nếu đơn giản quá mà thành vấn đề, sau này thêm độ phức tạp cũng không khó.
- Có thể khoảnh khắc đó sẽ không bao giờ đến.
- Tránh viết code phức tạp:
- Hãy chờ đến thời điểm thật sự cần thiết.
- Khả năng phải hối tiếc vì đã viết code đơn giản là rất thấp.
- Code hiệu năng cao hoặc tính năng cao cấp có thể chỉ hoạt động trong những môi trường nhất định.
- Ví dụ:
- LMDB: đã gặp rất nhiều khó khăn trước khi được dùng ổn định trong PowerDNS.
- RapidJSON: thư viện JSON tăng tốc bằng SIMD. Hiệu năng rất tốt nhưng điều kiện sử dụng khắt khe.
- Ví dụ:
- Ngay cả khi bạn tự tin rằng “mình có thể vượt qua ràng buộc này”:
- Năm nay có thể làm được, nhưng sau 5 năm chính bạn hoặc người kế nhiệm có thể gặp khó khăn.
- Nguyên tắc tương tự cũng áp dụng với ngôn ngữ lập trình phức tạp.
- Kết luận:
- Hãy đơn giản hóa code:
- Thật đơn giản. Còn đơn giản hơn nữa.
- Để tối ưu hóa lại sau:
- Độ phức tạp có thể được thêm vào khi cần, nhưng nếu làm mọi thứ phức tạp ngay từ đầu thì việc bảo trì sẽ trở nên khó khăn.
- Hãy đơn giản hóa code:
Phát triển phần mềm dựa trên LinkedIn
- Thực tế vs. lý tưởng
- Cách tiếp cận lý tưởng: cần đánh giá và xem xét kỹ lưỡng khi chọn phụ thuộc (dùng checklist nêu ở trên).
- Cách tiếp cận thực tế: đôi khi người ta có xu hướng thử một công nghệ hấp dẫn, nếu chạy được thì cứ thế dùng tiếp.
- Lý do hấp dẫn
- Công nghệ được người nổi tiếng hay influencer trên LinkedIn khuyên dùng.
- “Framework mới nhất” được tung hô mạnh trên các cộng đồng như Hacker News.
- Công nghệ theo trào lưu thiếu kiểm chứng dài hạn:
- Có thể không phù hợp với “các dự án phần mềm phải được duy trì hơn 10 năm”.
- Công nghệ mới ở giai đoạn đầu có xác suất cao gặp vấn đề về ổn định và khả năng bảo trì.
- Khuyến nghị
- Chỉ dùng trong khu vực thử nghiệm:
- Trước tiên hãy thử công nghệ mới trong dự án nhỏ hoặc khu vực không cốt lõi.
- Cân nhắc hiệu ứng Lindy:
- Tuổi thọ của công nghệ có xu hướng tỷ lệ với thời gian nó đã tồn tại.
- Công nghệ càng lâu đời thì càng có thể kỳ vọng sự ổn định dài hạn.
- Chỉ dùng trong khu vực thử nghiệm:
- Công nghệ mới rất hấp dẫn, nhưng với các dự án dài hạn thì công nghệ đã được kiểm chứng và ổn định sẽ phù hợp hơn.
Logging, telemetry, hiệu năng
- Khi phần mềm không được cập nhật hoặc triển khai liên tục:
- Có khả năng cao là bạn sẽ không nhận được phản hồi tức thì khi website bị hỏng.
- Có thể mất rất lâu từ lúc triển khai đến lúc xử lý được vấn đề thực tế.
- Triển khai logging và telemetry kỹ lưỡng ngay từ bản phát hành đầu tiên:
- Ghi lại hiệu năng, thất bại, lịch sử hoạt động của phần mềm.
- Dữ liệu tích lũy theo thời gian cực kỳ hữu ích để xử lý các bug hiếm gặp.
- Vấn đề do thiếu logging:
- Đã triển khai UI cho người dùng nhưng có người tạo ra 3000 thư mục rồi báo lỗi.
- Người dùng chỉ nói “không hoạt động”, nên mất nhiều tháng mới tìm ra nguyên nhân gốc rễ.
- Nếu có logging hiệu năng và telemetry, vấn đề đã có thể được giải quyết nhanh hơn rất nhiều.
- Logging và telemetry là bắt buộc:
- Hãy thiết kế để có thể giám sát kỹ lưỡng hoạt động của phần mềm.
- Chúng giúp ích rất lớn trong việc giải quyết các vấn đề bất ngờ trong quá trình triển khai và bảo trì dài hạn.
Tài liệu hóa
- Tầm quan trọng của tài liệu hóa:
- Không chỉ đơn thuần là viết tốt tài liệu API, mà còn phải giải thích “vì sao lại thiết kế như vậy”.
- Ghi lại các ý tưởng và triết lý về cách hệ thống hoạt động.
- Cần để lại lý do tách giải pháp và cơ sở cho các quyết định thiết kế không trực quan.
- Những tài liệu hữu ích ngoài tài liệu kiến trúc:
- Bài viết blog nội bộ: nơi các lập trình viên chia sẻ thảo luận cởi mở về thiết kế hệ thống.
- Phỏng vấn trong nhóm: lưu lại các cuộc trò chuyện chứa bối cảnh của các quyết định thiết kế.
- Những tài liệu như vậy cho phép tri thức được truyền lại trong nhóm theo thời gian.
- Hãy để lại comment trong code:
- Bất chấp xu hướng “code tốt thì không cần comment”, những comment giải thích “vì sao” của code là bắt buộc.
- Điều quan trọng là giải thích lý do tồn tại của một hàm cụ thể.
- Viết commit message:
- Commit message là cốt lõi của lịch sử công việc. Nhờ đó có thể lần theo lý do của các thay đổi trong code.
- Hãy tạo môi trường để mọi người có thể dễ dàng xem commit message.
- Dành thời gian cho tài liệu hóa:
- Vào những ngày việc phát triển không suôn sẻ, hãy dành thời gian để viết các comment và ghi chép hữu ích.
- Ở cấp độ nhóm, hãy thường xuyên phân bổ thời gian cho tài liệu hóa.
- Hãy ghi lại vì sao lại thiết kế như vậy:
- Sau 7 năm, tài liệu có thể truyền lại triết lý và bối cảnh cho một đội ngũ mới sẽ vô cùng quý giá.
- Để lại lịch sử qua comment và commit message:
- Đây là yếu tố thiết yếu không chỉ trong quá trình phát triển mà còn cho cả bảo trì dài hạn.
Cấu trúc đội ngũ
- Tính liên tục của đội ngũ và thành công dài hạn của phần mềm:
- Một số phần mềm được thiết kế để được hỗ trợ trong 80 năm. Với các dự án dài hạn như vậy, việc duy trì đội ngũ là then chốt.
- Trong môi trường phát triển hiện đại, thời gian làm việc trung bình khoảng 3 năm đã được xem là gắn bó lâu.
- Tài liệu hóa và kiểm thử tốt có thể bù đắp phần nào cho việc thay người trong đội, nhưng có giới hạn.
- Lợi thế của việc gắn bó lâu dài:
- Giữ thành viên trong nhóm hơn 10 năm:
- Điều quan trọng là tuyển họ làm nhân viên thực thụ và quản lý lập trình viên thật tốt.
- Đây được xem là một “hack” cốt lõi cho thành công của dự án dài hạn.
- Giữ thành viên trong nhóm hơn 10 năm:
- Vấn đề của việc phụ thuộc vào outsource:
- Lập trình viên thuê ngoài thường bàn giao code cho hệ thống rồi rời đi.
- Đây là cách làm rất kém hiệu quả nếu mục tiêu là chất lượng phần mềm có thể tồn tại hơn 10 năm.
- Hãy tạo ra môi trường để các thành viên có thể đồng hành cùng nhau trong dài hạn.
- Cần có chiến lược giảm phụ thuộc vào tư vấn bên ngoài và tăng tính bền vững của đội ngũ nội bộ.
Hãy cân nhắc mã nguồn mở
- Ưu điểm của mã nguồn mở:
- Duy trì chất lượng code thông qua việc được rà soát từ bên ngoài:
- Ánh mắt từ bên ngoài buộc lập trình viên phải giữ tiêu chuẩn cao hơn.
- Một cơ chế rất mạnh để duy trì tiêu chuẩn code tốt hơn.
- Duy trì chất lượng code thông qua việc được rà soát từ bên ngoài:
- Thực tế trong quá trình chuẩn bị mở mã nguồn:
- Doanh nghiệp hoặc chính phủ thường nói rằng việc chuẩn bị open source mất từ vài tháng đến vài năm.
- Lý do:
- Bên trong tổ chức, việc viết ra những đoạn code khó có thể công khai ra ngoài là chuyện khá phổ biến.
- Vì vậy cần dọn dẹp code trước khi mở mã nguồn.
- Đánh giá khả năng áp dụng:
- Open source không phải lúc nào cũng là lựa chọn khả thi.
- Nếu có thể, đây là cách tốt để nâng cao chất lượng code và tính minh bạch.
- Mã nguồn mở là một chiến lược quan trọng nên tận dụng khi có thể.
- Góc nhìn bên ngoài và tiêu chuẩn cao giúp giữ dự án đi đúng hướng.
Kiểm tra sức khỏe phụ thuộc
- Vấn đề của sự thay đổi ở phụ thuộc:
- Phụ thuộc có thể thay đổi hoặc trôi lệch khỏi kỳ vọng theo thời gian.
- Nếu để mặc như vậy, có thể dẫn đến:
- Bug
- Build thất bại
- Và nhiều kết quả đáng thất vọng khác.
- Khuyến nghị kiểm tra sức khỏe định kỳ:
- Rà soát phụ thuộc theo chu kỳ:
- Giúp có cơ hội phát hiện vấn đề từ sớm.
- Đồng thời có thể khám phá tính năng mới của phụ thuộc để đơn giản hóa code hoặc loại bỏ phụ thuộc khác.
- Tầm quan trọng của bảo trì phòng ngừa:
- Nếu bạn không chủ động lên lịch thời gian kiểm tra, cuối cùng sự cố sẽ ép bạn phải dành thời gian cho nó.
- Rà soát phụ thuộc theo chu kỳ:
- Ẩn dụ về bảo trì:
- Châm ngôn của thợ máy:
- “Hãy tự lên kế hoạch cho thời gian bảo trì. Nếu không, thiết bị sẽ lên kế hoạch đó thay bạn.”
- Châm ngôn của thợ máy:
- Việc rà soát phụ thuộc định kỳ là hoạt động thiết yếu cho sự ổn định và hiệu quả của phần mềm về lâu dài.
- Hãy xem đó là cơ hội để giải quyết vấn đề sớm và phát hiện các thay đổi tích cực.
Sách tham khảo chính
- The Practice of Programming (Brian W. Kernighan, Rob Pike)
- The Mythical Man-Month (Fred Brooks)
- A Philosophy of Software Design (John Ousterhout)
- Kill It with Fire: Manage Aging Computer Systems (Marianne Bellotti)
Cuối cùng
Các khuyến nghị cốt lõi cho phát triển phần mềm dài hạn:
- Giữ sự đơn giản:
- Hãy đơn giản, còn đơn giản hơn nữa! Vì có thể thêm độ phức tạp khi cần, nên đừng làm mọi thứ phức tạp quá mức ngay từ đầu.
- Để giữ được sự đơn giản, cần refactor định kỳ và xóa code.
- Suy nghĩ cẩn trọng về phụ thuộc:
- Càng ít phụ thuộc càng tốt. Hãy xem xét kỹ và kiểm toán chúng.
- Nếu không thể kiểm toán 1600 phụ thuộc, bạn nên nghĩ lại kế hoạch của mình.
- Tránh những lựa chọn chạy theo xu hướng hay mốt nhất thời (ví dụ: phát triển dựa trên LinkedIn).
- Kiểm tra phụ thuộc định kỳ: liên tục theo dõi tình trạng của các phụ thuộc.
- Kiểm thử, kiểm thử, và kiểm thử:
- Giúp phát hiện kịp thời các phụ thuộc đang thay đổi.
- Mang lại sự tự tin khi refactor và giúp duy trì tính đơn giản.
- Tài liệu hóa:
- Không chỉ tài liệu cho code mà cả triết lý, ý tưởng, và bối cảnh của câu hỏi “vì sao lại làm như vậy”.
- Đây sẽ là tài sản quý giá cho các thành viên trong tương lai.
- Duy trì một đội ngũ ổn định:
- Với đầu tư cho dự án dài hạn, hãy cân nhắc tuyển dụng dài hạn.
- Hỗ trợ để các thành viên có thể gắn bó với dự án trong thời gian dài.
- Cân nhắc open source:
- Nếu có thể, hãy dùng mã nguồn mở để duy trì tiêu chuẩn code cao hơn.
- Log và telemetry hiệu năng:
- Đóng vai trò quan trọng trong việc phát hiện và giải quyết vấn đề sớm.
- Những khuyến nghị này có thể không mới, nhưng vì được các lập trình viên giàu kinh nghiệm nhấn mạnh nên rất đáng để suy nghĩ nghiêm túc.
4 bình luận
Sức mạnh kỹ thuật quan trọng nhất là tách lớp mà tính ổn định là then chốt với lớp mà tốc độ là then chốt, rồi xử lý mối quan hệ giữa hai lớp đó như thế nào.
Nếu Toss chỉ theo đuổi sự ổn định thì đã chẳng khác gì các ngân hàng khác.
Nguy hiểm thì SpaceX cũng vậy. Tesla cũng vậy..
Có phải phát triển theo định hướng CV là vấn đề không.
Ý kiến trên Hacker News
Việc chủ động cập nhật chuỗi công cụ là một phần quan trọng của quá trình phát triển. Nhiều công ty loại việc nâng cấp chuỗi công cụ khỏi danh sách ưu tiên, dẫn đến các vấn đề như lỗ hổng bảo mật. Với mỗi bản phát hành mới của trình biên dịch hoặc hệ thống build, hãy tạo một nhánh để kiểm tra trạng thái build; nếu có lỗi thì coi đó là bug và xử lý ngay lập tức. Điều này giúp hiện đại hóa và tái cấu trúc codebase dần dần bằng các tính năng ngôn ngữ mới nhất.
Phụ thuộc bên thứ ba thường gây thất vọng về lâu dài. Ở các dự án mới, có thể dùng phụ thuộc bên thứ ba để giải quyết vấn đề trong ngắn hạn, nhưng về lâu dài thì tốt hơn nên thay thế bằng mã tự viết.
Cần vendor hóa các dependency và quản lý chúng thông qua code review. Chất lượng của mã bên thứ ba thường thấp, nên nhiều khi tự viết còn tốt hơn.
Đang thực hiện một dự án hướng tới khả năng mở rộng dài hạn bằng Qt, CMake và C++ hiện đại. Stack công nghệ này liên tục mang lại tính năng mới và các cải tiến.
Làm việc với Emacs Lisp là một trải nghiệm mới mẻ. Ưu điểm là thư viện vẫn hoạt động ổn định ngay cả khi không được cập nhật. Trải nghiệm với Gatsby và Node thì khó khăn hơn do các vấn đề cập nhật.
Viết mã đơn giản là điều quan trọng. Chỉ nên viết mã phức tạp khi thực sự cần, còn mã đơn giản thì hiếm khi phải hối tiếc.
Tài liệu hóa hệ thống và mã là rất quan trọng. Càng có nhiều kinh nghiệm phát triển phần mềm, người ta càng nhận ra tầm quan trọng của tài liệu hóa.
Kiểm thử đóng vai trò quan trọng trong kế hoạch. Nên tham khảo cách phát triển của NASA và tập trung vào việc tìm lỗi lập trình. Trong phát triển phần mềm y tế, người ta tránh các diễn giải mơ hồ và không dùng cấp phát bộ nhớ động.
Cách tốt nhất để viết phần mềm tồn tại lâu dài là viết mã “nhàm chán”. Cần tránh phụ thuộc và bám sát các nguyên tắc cơ bản.
Có kinh nghiệm gặp khó khăn vì vấn đề phụ thuộc trong Python. Điều này được gọi là “DLL Hell”, và COM từng cố giải quyết nhưng không thành công.
Các thực hành áp dụng trong phần mềm công nghiệp không đủ vững chắc để áp dụng nguyên vẹn cho phần mềm nói chung. Các kỹ sư cố gắng giảm thiểu rủi ro, và chúng ta cũng nên tập trung vào việc đó.