Kỹ thuật phần mềm thực chất là quản lý độ phức tạp, không phải viết code
Bài viết phân tích sự khác biệt giữa việc viết code và xây dựng phần mềm, nhấn mạnh rằng AI chỉ hỗ trợ tạo mã lệnh chứ không thể thay thế tư duy kiến trúc và ra quyết định của kỹ sư. Tác giả đưa ra các nguyên tắc để quản lý độ phức tạp, đánh giá tradeoff và duy trì quyền sở hữu codebase trong kỷ nguyên AI.
Kỹ thuật phần mềm thực chất là quản lý độ phức tạp, không phải viết code
Trong bối cảnh AI đang len lỏi vào mọi ngóc ngách của quy trình phát triển, chúng ta dễ nhầm lẫn giữa viết code và xây dựng phần mềm. Bài viết này chỉ ra rằng giá trị thực sự của kỹ sư phần mềm nằm ở khả năng ra quyết định kiến trúc dựa trên bối cảnh cụ thể, chứ không phải ở việc gõ phím hay tạo ra nhiều dòng lệnh hơn.
Khác biệt cốt lõi là: Viết code là chuyển ý tưởng thành chỉ dẫn cho máy tính; Xây dựng phần mềm là quyết định những chỉ dẫn nào nên tồn tại, chúng tương tác ra sao, chấp nhận đánh đổi gì, và hệ thống có thể tiến hóa mà không sụp đổ dưới sức nặng của chính nó hay không. AI rất giỏi việc đầu tiên, nhưng phần khó nhất của kỹ thuật phần mềm nằm ở việc thứ hai, nơi không có công cụ nào có thể thay thế con người.
Phần khó không bao giờ là gõ code
Hãy xem một yêu cầu tưởng chừng đơn giản: xử lý sự kiện đến và cập nhật dữ liệu. Ngay lập tức nảy sinh hàng loạt câu hỏi:
- Xử lý đồng bộ hay đưa vào hàng đợi (queue)?
- Cần xử lý exactly-once, hay at-least-once là đủ?
- Hệ thống có chấp nhận nhất quán cuối cùng (eventual consistency) không?
- Xử lý thất bại giữa chừng thì sao? Thử lại bao nhiêu lần?
- Nếu consumer không khả dụng trong 3 giờ thì thế nào?
- Sự kiện có cần giữ thứ tự không?
- Lưu lượng dự kiến hôm nay là bao nhiêu? Hai năm nữa?
- Hậu quả nếu một sự kiện bị xử lý hai lần là gì?
Những câu hỏi này gần như không liên quan đến cú pháp ngôn ngữ lập trình. Việc chọn ngôn ngữ quan trọng vì ảnh hưởng đến sự trôi chảy của đội ngũ, hiệu năng, tính an toàn và khả năng bảo trì, nhưng nó không trả lời được các vấn đề nền tảng.
Phần khó là chọn kiến trúc đại diện cho đúng tập hợp các sự đánh đổi. Và hiếm khi có một câu trả lời đúng tuyệt đối cho mọi trường hợp.
Một bài toán có thể có N lời giải hoàn toàn khác nhau
Điều này đặc biệt rõ ràng khi phần mềm tồn tại trong bối cảnh doanh nghiệp. Hai công ty có thể yêu cầu đội kỹ thuật xây dựng một tính năng nghe có vẻ giống hệt nhau trên giấy tờ, nhưng:
- Công ty A có 500 người dùng, công ty B có 20 triệu.
- Công ty A có 3 kỹ sư bảo trì, công ty B có 200.
- Một bên cần nhất quán mạnh vì sai sót gây hậu quả tài chính nghiêm trọng; bên kia chấp nhận eventual consistency để đổi lấy khả năng sẵn sàng và thông lượng.
- Một bên phải ra mắt trong 3 tuần, bên kia kỳ vọng hệ thống vận hành 15 năm.
- Một bên đã có Kafka, Kubernetes, PostgreSQL và đội ngũ quen thuộc với hệ phân tán; bên kia chỉ có một máy chủ ứng dụng và 4 lập trình viên.
Giải pháp ấn tượng về mặt kỹ thuật cho công ty này có thể là giải pháp vô trách nhiệm cho công ty kia. Vì vậy, câu hỏi đúng không phải là "Cách tốt nhất để triển khai X là gì?", mà là "Với những ràng buộc này, đội ngũ này, hạ tầng này, ngân sách này, rủi ro này và lộ trình sản phẩm này, đâu là cách phù hợp nhất để triển khai X vào hôm nay?"
Đó là một câu hỏi hoàn toàn khác — và không AI nào có thể trả lời thay bạn.
AI tạo giải pháp, kỹ sư sở hữu sự đánh đổi
AI cực kỳ hữu ích trong việc tạo boilerplate, khám phá API, gợi ý triển khai, tìm bug, viết test, hoặc giảm khối lượng công việc cơ học. Nhưng có một xu hướng nguy hiểm là mở rộng khả năng này thành việc ủy quyền cả phán đoán kỹ thuật cho máy.
Bạn có thể:
- Đưa yêu cầu và bảo AI thiết kế hệ thống "không sai sót" — nó sẽ thiết kế một cái.
- Bảo nó chọn database — nó sẽ chọn.
- Hỏi có nên dùng queue, microservices, caching, CQRS, event sourcing, Kubernetes hay Redis — nó sẽ trả lời.
Nhưng sự tồn tại của một câu trả lời không có nghĩa là vấn đề kỹ thuật đã được giải quyết. Vì quyết định đúng phụ thuộc vào rất nhiều bối cảnh cần hàng giờ, hàng ngày, thậm chí hàng tuần để phân tích — thường liên quan đến nhiều phòng ban, và để lại những vùng xám khó hình thức hóa.
Phần lớn bối cảnh đó không nằm trong tài liệu. Nó nằm trong các cuộc trò chuyện với khách hàng, lịch sử sản phẩm, kỹ năng của đội ngũ, sự cố vận hành 3 năm trước, ràng buộc ngân sách, cam kết hợp đồng, hành vi kỳ lạ của một hệ thống cũ không ai muốn đụng vào. Và đôi khi nó nằm trong kinh nghiệm: nhận ra rằng một kiến trúc thanh lịch trên lý thuyết sẽ trở thành cơn ác mộng vận hành cho đội ngũ phải bảo trì nó.
Không có kiến trúc nào không có đánh đổi
Kỹ thuật phần mềm chủ yếu là kỷ luật quyết định bạn muốn gánh chịu những vấn đề nào. Ngay cả lựa chọn "đơn giản" cũng có nhiều yếu tố cần cân nhắc:
- Chuẩn hóa database mạnh tay thì mất tính toàn vẹn, nhưng một số query trở nên phức tạp hơn.
- Phi chuẩn hóa thì cải thiện hiệu năng đọc, nhưng tăng độ phức tạp đồng bộ.
- Thêm caching thì giảm độ trễ, nhưng phát sinh vấn đề invalidate.
- Xử lý bất đồng bộ giúp tách rời thành phần, nhưng tạo ra các failure mode mới.
- Tách monolith thành microservices mang lại khả năng triển khai độc lập, nhưng đổi lại là độ phức tạp hệ phân tán.
- Tối ưu mạnh tay có thể tăng hiệu năng nhưng hy sinh khả năng đọc hiểu.
- Trừu tượng hóa quá mức giảm trùng lặp nhưng khiến hệ thống khó hiểu hơn.
Mọi tối ưu đều tiêu tốn độ phức tạp ở một nơi nào đó. Công việc của kỹ sư không phải là làm cho hệ thống hoạt động, mà là quyết định độ phức tạp nên thuộc về đâu.
Tư duy thuật toán quan trọng hơn, không phải ít hơn
Trong thời đại AI, tư duy thuật toán càng trở nên quan trọng hơn, nhưng không chỉ là biết các thuật toán trong sách giáo khoa. Nó là việc định nghĩa các nguyên tắc cơ bản và áp dụng hằng ngày:
- Phân rã bài toán.
- Xác định các bất biến (invariants).
- Hiểu luồng dữ liệu.
- Chọn cấu trúc dữ liệu phù hợp.
- Suy luận về độ phức tạp thời gian và không gian.
- Xác định điểm tranh chấp (contention).
- Hiểu các failure mode.
- Nhận biết thao tác nào đắt đỏ và thao tác nào thực sự quan trọng.
- Tách biệt độ phức tạp thiết yếu khỏi độ phức tạp ngẫu nhiên.
- Hiểu điều gì xảy ra khi giả định không còn đúng.
AI có thể tạo ra 10 bản triển khai trong thời gian kỹ sư viết một bản trước đây. Thách thức là dùng AI để áp dụng các nguyên tắc này qua các vòng lặp có kiểm soát, với con người luôn trong vòng lặp. Điều đó làm cho việc chọn đúng bản triển khai quan trọng gấp 10 lần — đó mới là ý nghĩa thực sự của "tăng năng suất nhờ AI".
Ngôn ngữ lập trình là công cụ, không phải bản sắc
Chọn đúng công cụ cho công việc là điều quan trọng — ngôn ngữ khác nhau về hệ sinh thái, hiệu năng, hệ thống kiểu dữ liệu, mô hình đồng thời và đảm bảo an toàn. Nhưng trong nhiều hệ thống kinh doanh, việc chọn giữa hai ngôn ngữ hợp lý sẽ ít ảnh hưởng hơn nhiều so với việc chọn sai kiến trúc, sai mô hình dữ liệu hoặc sai chiến lược vận hành.
Năm yếu tố thực sự quan trọng:
- Sự trôi chảy của đội ngũ.
- Độ trưởng thành của hệ sinh thái.
- Khả năng bảo trì.
- Khả năng gỡ lỗi.
- Khả năng triển khai.
Đừng chọn ngôn ngữ dựa trên giả định LLM "xử lý tốt hơn" với ngôn ngữ này hay ngôn ngữ kia. Hãy luôn đặt đội ngũ lên hàng đầu. Hãy trở thành người đa ngôn ngữ, dùng AI làm công cụ học tập chứ không chỉ là máy tạo code, và luôn đối chiếu với tài liệu chính thức.
Mục tiêu tối ưu thực sự là vòng đời của hệ thống
Một sai lầm khác là tối ưu phần mềm chỉ cho yêu cầu hiện tại. Phần mềm hiện đại không thể tĩnh tại — nó phải đáp ứng sự thay đổi của người dùng, khách hàng, lưu lượng, mô hình kinh doanh, quy định, đội ngũ và hạ tầng. Một tính năng vô hại hôm nay có thể trở thành quy trình cốt lõi hai năm sau.
Nhưng cũng đừng rơi vào cái bẫy ngược lại: thiết kế những trừu tượng phức tạp cho tương lai giả định mà có thể không bao giờ đến. Sự cân bằng này khó, vì bạn phải tự hỏi:
- Khả năng yêu cầu này thay đổi là bao nhiêu?
- Chúng ta nên cố ý đưa sự linh hoạt vào ở đâu?
- Chỗ nào nên giữ đơn giản?
- Loại nợ kỹ thuật nào là chấp nhận được?
- Nợ kỹ thuật nào có thể trở nên quá đắt về cấu trúc?
- Tối ưu gì bây giờ?
- Cố ý không tối ưu chỗ nào cho đến khi có số liệu chứng minh?
Đây chính là phán đoán kỹ thuật. Không có AI nào trả lời đúng các câu hỏi này, cũng không có prompt nào khiến chúng biến mất.
Code do AI tạo ra tạo ra vấn đề sở hữu mới
AI khiến việc tạo code trở nên cực kỳ rẻ. Nhưng sản xuất code và duy trì code là hai hoạt động kinh tế khác nhau. Một kỹ sư có thể tạo ra hàng nghìn dòng code hợp lý chỉ trong vài phút. Merge xong, test pass, feature chạy — năng suất có vẻ tăng vọt. Nhưng hãy tự hỏi:
- Đội ngũ có thực sự hiểu kiến trúc tổng thể không?
- Họ có hiểu cấu trúc dữ liệu không?
- Họ có biết tại sao một sự trừu tượng nào đó tồn tại không?
- Họ có hiểu hành vi lỗi không?
- Họ có biết những giả định nào mà bản triển khai phụ thuộc vào không?
- Liệu họ có thể tự tin sửa đổi nó sau 6 tháng mà không cần hỏi AI giải thích chính code mà AI tạo ra?
Nếu câu trả lời là không, đội ngũ đó không hề loại bỏ độ phức tạp — họ đã tạo ra những độ phức tạp mới, và chúng sẽ ngày càng đắt đỏ theo thời gian.
Dòng code rẻ, sự hiểu biết thì đắt. Một pull request 3.000 dòng do AI tạo vẫn là thêm 3.000 dòng vào bề mặt nhận thức của hệ thống. Bộ test pass không phải là câu trả lời — nó chỉ tạo cảm giác an toàn giả tạo vì hệ thống "dường như" hoạt động như ý định.
Nếu bạn phát hành nó, bạn sở hữu nó
Quy tắc đơn giản cho quản lý kỹ thuật: Không bao giờ phát hành code mà bạn không sở hữu — sở hữu nghĩa là hiểu hoàn toàn codebase và đã xác thực mọi phần của nó. Code do AI tạo ra không được hưởng tiêu chuẩn giám sát thấp hơn chỉ vì nó được tạo nhanh bởi một mô hình LLM đắt tiền.
Thách thức hiện nay là quyết định có nên đầu tư thời gian review các pull request khổng lồ hay nên đi chậm hơn với các vòng lặp nhỏ, nơi từng phần logic ứng dụng được phát triển qua trao đổi có kiểm soát giữa người và AI.
Kỹ thuật phần mềm có thể đang trở nên khó hơn
Một nghịch lý thú vị đang xuất hiện: AI làm cho việc lập trình dễ hơn nhưng có thể làm cho kỹ thuật phần mềm khó hơn. Chúng ta tạo code nhanh chưa từng thấy — prototype tức thì, tích hợp thư viện lạ không cần đọc doc, sinh test, API, database layer, infrastructure config, frontend component, migration, toàn bộ service. Nhưng khả năng hiểu hệ thống không tăng theo cùng tốc độ.
Nút thắt mới không còn là "viết nhanh bao nhiêu?" mà là "Tổ chức này có thể hiểu và kiểm soát bao nhiêu độ phức tạp?"
Các thước đo năng suất truyền thống đã hỏng: đội tạo ra gấp 5 lần code không có nghĩa là năng suất gấp 5 lần. Những lợi ích ban đầu từ LLM đang phải trả giá dưới hình thức chi phí duy trì và tiến hóa các hệ thống có độ phức tạp trung bình. Từ nay, năng suất nên được đo bằng cách chúng ta giải quyết vấn đề hiệu quả như thế nào trong khi kiểm soát độ phức tạp kết quả.
AI nên tăng đòn bẩy kỹ thuật, không thay thế phán đoán kỹ thuật
Không phải là lập luận chống lại phát triển với AI — ngược lại, AI có lẽ là công cụ tăng năng suất mạnh mẽ nhất mà kỹ sư phần mềm có được trong nhiều thập kỷ. Chúng ta nên dùng nó để dành nhiều thời gian hơn cho kỹ thuật, không phải ít hơn. Hãy để AI viết adapter lặp lại, tạo bản đầu tiên của test suite, giải thích thư viện lạ, điều tra lỗi, đề xuất 3 cách triển khai hoặc refactor code máy móc. Nó là trợ lý, là bệ phóng — nhưng không phải là người ra quyết định.
Code chưa bao giờ là sản phẩm
Kỹ thuật phần mềm không phải là một môn học thuần túy học thuật. Chúng ta ở đây để phục vụ người dùng, vì vậy nó là một thực hành gắn với thực tế, phải luôn kết nối với thế giới thực đang thay đổi nhanh hơn bao giờ hết. Đội của bạn phải quyết định AI sẽ xuất hiện ở đâu, khi nào và như thế nào trong quy trình phát triển.
Ngôn ngữ lập trình sẽ tiến hóa. Framework sẽ biến mất. Mô hình AI sẽ mạnh hơn đáng kể. Chi phí tạo code sẽ tiến gần đến con số không.
Nhưng độ phức tạp sẽ luôn ở đó. Nó thuộc về những kỹ sư có thể hiểu nhiều nhất và tạo ra ít độ phức tạp không cần thiết nhất. AI có thể giúp chúng ta đến đó — nhưng không thể giải phóng chúng ta khỏi trách nhiệm với những gì chúng ta phát hành.