- Phụ thuộc trông như một tính năng miễn phí, nhưng trên thực tế lại đi kèm nhiều chi phí và độ phức tạp
- Một phụ thuộc tồi có thể gây ra nhiều rủi ro như đường cong học tập, thay đổi giao diện đột ngột, vấn đề triển khai/cài đặt
- Ví dụ tiêu biểu, TigerBeetle theo đuổi chính sách "không phụ thuộc" để đảm bảo bảo mật, hiệu năng và sự đơn giản trong vận hành
- Tác giả đề xuất khung đánh giá phụ thuộc (mức độ phổ biến, tính ổn định, độ sâu, tính tiện dụng, tính kín kẽ)
- Cần có tư duy phản biện để phân biệt phụ thuộc tốt và phụ thuộc xấu; khi chọn phụ thuộc, không chỉ nhìn vào năng suất ngắn hạn mà còn phải cân nhắc tổng chi phí và rủi ro
Một phụ thuộc tồi tốn kém hơn NIH (Not Invented Here)
Nhược điểm và chi phí ẩn của phụ thuộc
- Nhiều lập trình viên xem việc thêm phụ thuộc như "tính năng miễn phí", nhưng thực tế lại phát sinh những chi phí sau
- Thời gian học và độ phức tạp
- Việc cài đặt chính phụ thuộc đó cũng thường xuyên gặp khó khăn
- Khi nâng cấp phiên bản lớn, mã của tôi cũng phải chỉnh sửa đáng kể
- Cuối cùng, phụ thuộc bắt buộc phải có mặt trong môi trường triển khai/cài đặt (gây thêm phức tạp như container, bundling, v.v.)
- Việc đưa vào một phụ thuộc tồi đôi khi còn tạo ra cấu trúc triển khai phức tạp không liên quan đến chức năng cốt lõi
Nguyên tắc không phụ thuộc của TigerBeetle
- TigerBeetle là cơ sở dữ liệu tài chính xây trên Vanilla Zig, áp dụng chính sách không phụ thuộc
- Chỉ phát triển bằng ngôn ngữ Zig, không dùng phụ thuộc bên ngoài ngoài toolchain Zig
- Mục tiêu là tránh các vấn đề như rủi ro tấn công chuỗi cung ứng, suy giảm hiệu năng, tăng thời gian cài đặt do phụ thuộc gây ra
- Phần mềm càng bám rễ sâu trong hạ tầng thì chi phí do phụ thuộc gây ra càng bị khuếch đại trên toàn bộ stack
- Việc dùng một hộp công cụ nhỏ đã được chuẩn hóa có lợi hơn cho bảo trì và hiệu suất phát triển
- Tập trung xử lý nhanh các vấn đề mới và giảm độ phức tạp chỉ với Zig
Khung đánh giá phụ thuộc
- Dù thừa nhận rằng không thể hoàn toàn không có phụ thuộc, tác giả vẫn nhấn mạnh rằng phụ thuộc cần được đánh giá cẩn trọng
- Tác giả đề xuất đánh giá phụ thuộc theo 5 tiêu chí sau
- Mức độ phổ biến (Ubiquity): Nó hiện diện phổ biến đến mức nào trong môi trường mục tiêu? Có cần triển khai/cài đặt riêng hay không
- Tính ổn định (Stability): Tần suất phát sinh các vấn đề như tương thích ngược, thay đổi API, ngừng hỗ trợ
- Độ sâu (Depth): Lượng chức năng thực sự ẩn dưới API là bao nhiêu, và nếu tự triển khai thì khó đến mức nào
- Tính tiện dụng (Ergonomics): API có trực quan/khai báo và dễ dùng hay không, tài liệu ra sao
- Tính kín kẽ (Watertightness): Mức độ trừu tượng hoạt động tốt đến đâu, và cần quan tâm đến công nghệ nền bên dưới nhiều hay ít
- Cộng đồng phát triển thường chỉ bàn về tính tiện dụng, còn bốn yếu tố còn lại thường bị bỏ qua
Ví dụ về phụ thuộc tốt
-
POSIX system call
- Tính phổ quát: Có thể dùng trên gần như mọi nền tảng như Linux, Android, macOS, BSD
- Tính ổn định: Tính tương thích giao diện rất cao và hầu như không thay đổi
- Độ sâu: Một API đơn lẻ che giấu hàng trăm nghìn dòng mã kernel
- Tính tiện dụng: Dù mang phong cách C khá truyền thống nhưng không gây trở ngại lớn khi sử dụng
- Tính hoàn chỉnh: Hầu hết không có vấn đề, dù vẫn có vài chi tiết như xử lý độ bền dữ liệu trên thiết bị lưu trữ
-
Mã điều khiển terminal ECMA-48
- Tính phổ quát: Được hỗ trợ trên hầu hết terminal, ngoại trừ
cmd.exe của Windows
- Tính ổn định: Không thay đổi từ sau năm 1991
- Độ sâu: Tự tạo ra một tiêu chuẩn như vậy là điều khó đến mức phi thực tế
- Tính tiện dụng: Ngoài việc ký tự Esc làm mã hơi khó đọc thì nhìn chung vẫn ổn
- Tính hoàn chỉnh: Rất ít phải lo về sự phụ thuộc vào phần cứng
-
Nền tảng web (Web API, HTML, JS, CSS, v.v.)
- Tính phổ quát: Trình duyệt web được cài đặt trong gần như mọi môi trường trên toàn thế giới
- Tính ổn định: Chính sách tương thích ngược rất mạnh
- Độ sâu: Tự làm một trình duyệt gần như là bất khả thi trong thực tế
- Tính tiện dụng: Có hơi phức tạp đôi chút nhưng tài liệu và công cụ phát triển rất tốt
- Tính hoàn chỉnh: Ngoài vài trường hợp đặc thù như file, âm thanh, video thì mức độ hoàn chỉnh rất cao
Kết luận
- Thay vì copy-paste và lạm dụng phụ thuộc, cần có tư duy phản biện và phân tích tổng thể chi phí
- Khi đưa vào phụ thuộc, cần đánh giá một cách phản biện chi phí và lợi ích của mọi phụ thuộc, đồng thời
nhất định phải cân nhắc rủi ro và chi phí tiềm ẩn của toàn bộ hệ thống để đưa ra lựa chọn thận trọng; về dài hạn, cách này rẻ hơn và an toàn hơn rất nhiều
2 bình luận
TigerBeetle: cơ sở dữ liệu OLTP chuyên cho kế toán
Ý kiến trên Hacker News
Đây là lần đầu tôi nghe đến ví dụ TigerBeetle nên đã tự tìm hiểu. Nếu đang xây dựng một cơ sở dữ liệu sổ cái tài chính dựa trên Zig, nơi độ an toàn và hiệu năng là tối quan trọng, và phải xử lý một triệu giao dịch mỗi giây trên một lõi CPU, thì việc tránh thêm phụ thuộc vì rủi ro cao là hoàn toàn hợp lý. Nhưng đa số lập trình viên bình thường đang làm các hệ thống CRUD thông thường, và nhìn chung họ không mạnh đến mức đó. Nhiều phụ thuộc có thể đầy lỗi, nhưng trong không ít trường hợp vẫn có chất lượng tốt hơn so với thứ mà phần lớn lập trình viên tự viết. Doanh nghiệp trên thực tế cũng bị nghẽn bởi trình độ của những lập trình viên họ có thể tuyển được. Mỗi người có một bối cảnh khác nhau, nên cần nhớ rằng những lời khuyên trái ngược nhau vẫn có thể đều đúng trong từng ngữ cảnh riêng
Tôi thực sự đang làm việc tại TigerBeetle. Cốt lõi là bối cảnh. TigerBeetle hay rust-analyzer đều có văn hóa kỹ thuật mạnh, nhưng do giải các bài toán khác nhau nên hình thành những văn hóa khác nhau. Những phụ thuộc được nhắc trong bài, như POSIX, ECMA-48 hay nền tảng web, thực ra gần với giao diện hệ thống hơn là thư viện. Nếu thư viện có vấn đề thì có thể tự viết lại, nhưng những thứ nền tảng như giao diện hệ thống thì gần như không thể thay hoặc chi phí thay là rất lớn. Việc quyết định không làm những thứ nằm ngoài phạm vi phần mềm của mình là một sức mạnh. Ví dụ, nếu có một đội chuyên viết mã nhân ma trận, thì họ có thể dùng thư viện bên ngoài cho những phần không liên quan đến nghiệp vụ cốt lõi, nhưng theo tôi thì nên thiết kế phân rã trách nhiệm của sản phẩm tốt hơn. Làm vậy sẽ cô lập các phụ thuộc thiết yếu trong hệ thống hiệu quả hơn
Vấn đề của góc nhìn này là nó chỉ đúng khi bạn chỉ nghĩ đến những lập trình viên ở tầng đáy. Ngành kỹ thuật có xu hướng pha loãng lời khuyên để phù hợp với mức thấp nhất, nhưng thành thật mà nói, gần như chưa từng có nơi nào tôi làm mà lập trình viên yếu đến mức không thể sao chép một phụ thuộc bị lỗi để tự sửa vấn đề. Cũng có quá nhiều sự coi thường dành cho CRUD, trong khi các trừu tượng tệ hại có thể gây lãng phí thời gian và đau khổ khủng khiếp trong CRUD. Ngay cả những thứ phổ biến cũng đầy vấn đề và kém năng suất nếu đi xa hơn mức tutorial cơ bản nhất
Đây không phải câu chuyện liên quan đến trình độ lập trình viên. Dù dùng toolchain hay sản phẩm nào thì cuối cùng bạn vẫn đang dùng các phụ thuộc do người khác tạo ra. Xung quanh tôi rất ít người tự triển khai mã nhân ma trận, và ngay cả họ cũng không tự hiện thực mọi thư viện mã nguồn mở không liên quan đến mình. Thường người ta chỉ ám ảnh với phụ thuộc khi có yêu cầu tuân thủ, sở thích cá nhân, hoặc sự gắn bó với một thư viện cụ thể. Nếu ai cũng áp dụng nguyên tắc này một cách triệt để thì chắc chúng ta đều đang đi nhặt cát ngoài bãi biển mà sống
Ý kiến rằng "các lập trình viên CRUD trung bình không mạnh" là quá võ đoán. Phần lớn lập trình viên không thể chọn hệ thống mình sẽ làm việc, và nguồn lực trong thời gian phát triển thì luôn thiếu. Phải tận dụng những phụ thuộc 'rẻ' thì mới có thể phát hành phần mềm chạy được trong thời gian ngắn. Có vẻ đây là nhận xét được đưa ra mà không thật sự hiểu thực tế đó
TigerBeetle là một startup ra mắt tương đối gần đây, thậm chí còn chưa có bản 1.0 chính thức. Tôi nghĩ còn quá sớm để khẳng định cách tiếp cận này có thực sự hiệu quả hay không
Bản thân NIH (Not Invented Here) rất hữu ích khi được dùng với đánh giá thực tế về phạm vi trách nhiệm. Ví dụ, một framework frontend web khớp chính xác với domain của tôi thường đáng để tự làm và tự bảo trì. Nhưng với cơ sở dữ liệu, game engine, web server, hay các chức năng nền tảng về mật mã thì lại là câu chuyện khác. Nếu mức độ khó cao đến mức các giải pháp sẵn có không giải quyết nổi, thì trước tiên nên nghĩ đến việc định nghĩa lại bài toán. So với việc làm lại toàn bộ bộ test của SQLite từ đầu, tôi nghĩ việc sắp xếp lại chính bài toán còn rẻ hơn nhiều
Tuy vậy, cũng có nhiều trường hợp cơ sở dữ liệu, game engine, web server hay cryptographic primitive tự làm lại là lựa chọn tốt hơn. Nếu chỉ cần file đơn giản và chỉ mục runtime thì ngay cả SQLite cũng thường là quá mức cần thiết. Nhiều game, nhất là với đội nhỏ, lại có lợi hơn khi dùng engine tự làm. Lợi thế lớn nhất của các engine hoàn chỉnh là pipeline, nhưng đổi lại là overhead rất lớn. Web server thì không khác biệt nhiều về độ phức tạp so với ứng dụng FastCGI. Với mật mã cũng vậy, không phải tình huống nào cũng là vấn đề bảo mật; những thứ như kiểm tra hash đơn giản thì tự triển khai cũng không sao. Tôi nghĩ không nên rơi vào trạng thái bất lực học được trước các chủ đề trông có vẻ khó. Việc một giải pháp sẵn có giải được bài toán không có nghĩa đó là cách tối ưu hay hiệu quả nhất
Vậy thì vì sao lại có nhiều engine cơ sở dữ liệu đến thế? Rốt cuộc, các hệ thống máy tính phức tạp đều có những trade-off riêng. Ràng buộc, khả năng mở rộng, đồng thời, bảo mật, đặc tính dữ liệu, cách lưu trữ... đều có rất nhiều lựa chọn. Tôi cho rằng chi phí của một phụ thuộc càng lớn thì tôi càng tin tưởng phụ thuộc đó nếu chính nó cũng theo đuổi việc tối thiểu hóa các phụ thuộc khác. Hệ thống quản lý phụ thuộc tự động thường có xu hướng khiến vấn đề phức tạp hơn, nên quản lý thủ công cẩn trọng lại giúp giảm gánh nặng
Tôi nghĩ có hai lý do để dùng phụ thuộc bên thứ ba. (1) Nhà cung cấp dịch vụ tự publish nó, và vòng đời của nó tương đối trùng với vòng đời dịch vụ. (2) Nó thay thế phần mã phức tạp mà tôi không muốn tự viết. Trường hợp (1) không có vấn đề vì đó là lý do kinh doanh. Chỉ cần chấp nhận rằng khi dịch vụ đó cập nhật thì có thể sẽ có thay đổi lớn. Trường hợp (2) thì giá trị phụ thuộc vào độ phức tạp của phần mã mà tôi đang muốn tránh. Việc đưa vào một phụ thuộc đồng nghĩa với chuyện tôi phải tiêu tốn thời gian và nguồn lực để cập nhật/test theo lịch của bên kia, và gánh lấy trách nhiệm đó
Bạn sẽ sớm gặp những bài toán mà RDBMS không giải quyết tốt. RDBMS được thiết kế xoay quanh việc sửa đổi dữ liệu đồng thời và hỗ trợ tập dữ liệu biến đổi; nếu không cần những thứ đó thì chỉ với chỉ mục đơn giản cũng có thể đạt mức tăng hiệu năng rất lớn. Nếu dữ liệu là bất biến thì hoàn toàn có thể tự làm một giải pháp nhanh hơn RDBMS rất nhiều
Ví dụ về RDBMS rất thú vị. Wikipedia liệt kê hơn 100 RDBMS, và mỗi cái có bài toán giải được cũng như không giải được riêng. Đó là kết quả của việc suy nghĩ về lời giải thực dụng và thực sự triển khai nó
Phụ thuộc tạo ra rủi ro, nhưng nếu hoàn toàn không dùng thì có thể tụt lại trong năng lực phát triển và tốc độ ra thị trường. Vì vậy quy trình quản lý phụ thuộc là rất quan trọng
Mục 4 và 5 thực sự quan trọng nhưng hay bị quên. Ngay cả với dự án cá nhân, sau một thời gian bỏ đó rồi quay lại tôi cũng từng gặp trường hợp phụ thuộc đã lỗi thời hoặc repo bị xóa. Vì vậy giờ tôi thường fork kín cả source, tự build thử, và fork ở cấp source cả phụ thuộc của các phụ thuộc. Làm vậy có thể giảm thiểu thiệt hại nếu hệ sinh thái thay đổi đột ngột sau này. Tôi ngày càng thích thư viện dạng source hơn là binary
Ở mục 5, việc fork có thể là gánh nặng quá lớn. Vendor phụ thuộc vào git riêng hoặc cache proxy của mình cũng là cách ổn. Đặc biệt phù hợp hơn với các dự án sống lâu. Với những hệ như NodeJS có quá nhiều file phụ thuộc thì các công cụ như Yarn hay PNPM hiệu quả hơn
Liên quan mục 4, những phụ thuộc nổi tiếng như SQLite sẽ tồn tại lâu hơn sản phẩm tôi làm ra rất nhiều. Nếu tôi nghĩ sản phẩm của mình sẽ sống lâu hơn chính phần mềm mã nguồn mở đó thì đó còn là thái độ ngạo mạn hơn. Tôi cũng không định tự build Linux kernel
Mọi mã nguồn ít nhất phải có khả năng build mà không cần kết nối mạng. Tốt nhất là không có binary artifact, dù không phải lúc nào cũng khả thi trong thực tế
Một nhận định rất sắc bén. Tôi định thêm nội dung này vào tài liệu quy trình đưa phụ thuộc vào của mình. Vấn đề là những trường hợp như JavaScript, nơi cây phụ thuộc quá sâu
Sau nhiều năm trải nghiệm, cuối cùng mọi thứ vẫn là "It Depends™". Hồi trẻ tôi thường bám vào nguyên tắc và không chấp nhận ngoại lệ, rồi ép dùng thư viện hay mô hình không phù hợp và tạo ra những đoạn mã tệ nhất. Giờ thì những gì tôi dùng thường xuyên tôi sẽ tự đóng gói thành thư viện riêng, còn những gì cần mà tôi không làm tốt thì dùng phụ thuộc bên ngoài. Miễn là tôi thấy thỏa đáng về chất lượng và khả năng quản lý thì tôi sẵn sàng chấp nhận đồ bên ngoài
Ngành năng lượng có xu hướng cố ý tránh phụ thuộc. Khi đưa phụ thuộc bên ngoài vào thì phải xem xét toàn bộ thay đổi của nó. Công cụ viết mã bằng AI giúp ích rất nhiều, chủ yếu tôi dùng để tạo công cụ CLI. Tài liệu OpenAPI cũng được tôi tạo bằng LLM, rồi phục vụ bằng thư viện chuẩn Go. Bản thân LLM là phụ thuộc bên ngoài, nhưng các công cụ CLI nó tạo ra lại không liên quan đến mã thực tế nên yêu cầu chất lượng thấp hơn. Tất nhiên các lập trình viên frontend sẽ không muốn làm việc mà không có React, nhưng những sản phẩm như vậy là phần xử lý hướng ra bên ngoài nên là ngoại lệ. Nếu cung cấp cho kỹ sư những công cụ nhỏ giúp họ bớt ám ảnh với phụ thuộc phục vụ chất lượng, thì chính sách tối thiểu hóa phụ thuộc sẽ dễ thực hiện hơn
Khả năng phân biệt phụ thuộc tốt và xấu là một năng lực quan trọng. Theo tôi, phụ thuộc trả phí đa phần là bất lợi. Công ty cung cấp nó rất có thể đã thiết kế để thúc đẩy lock-in. "Dependency minimalism" là một khái niệm hay (tweet liên quan của VitalikButerin)
Phụ thuộc trả phí chỉ có một nơi hỗ trợ, nên nếu công ty cung cấp đóng cửa thì cả dự án sẽ gặp nguy cơ. Đa số công ty không tồn tại mãi mãi, vì vậy cần cân nhắc kỹ việc tương lai của phụ thuộc có ảnh hưởng đến lộ trình dự án của mình hay không
Tôi từng có trải nghiệm tệ với các phụ thuộc trả phí bị ép dùng bởi đội không có nền tảng kỹ thuật. Ngược lại, các phụ thuộc kiểu 'open-core' được cộng đồng dùng rộng rãi như Sidekiq đáng tin hơn nhiều vì xác suất biến mất đột ngột thấp hơn hẳn. Ưu điểm của đồ trả phí là nếu công ty vận hành lành mạnh thì không cần quá lo về hỗ trợ
Với cả component trả phí lẫn miễn phí do công ty cung cấp, vendor lock-in đều tồn tại. Quản trị rủi ro là việc của đội tích hợp; cần tìm phương án thay thế hoặc mô-đun hóa để kiểm soát rủi ro
Nếu là phụ thuộc trả phí thì nhất định phải được cung cấp qua giao diện dựa trên chuẩn mở hoặc có hiện thực thay thế, để tránh lock-in. Khi còn lựa chọn khác thì vẫn giữ được khả năng chuyển đổi
Việc cho rằng phụ thuộc trả phí là xấu có thể là dấu hiệu ngân sách chưa đủ. Tôi muốn việc hỗ trợ code của mình là 'công việc' của ai đó. Nếu có nhiều người hỗ trợ hoặc nhiều lập trình viên tự nguyện gánh trách nhiệm thì sẽ ổn định hơn; với dự án cá nhân, kể cả source có mở thì khi có vấn đề vẫn cần người hỗ trợ. Điều quan trọng là ý thức trách nhiệm để không bị bỏ mặc ngay cả khi công ty dừng hoạt động
Nhiều người có xu hướng ám ảnh với việc viết mã mới, nhưng thực tế thì đến 90% trường hợp, dùng cả một phụ thuộc tệ vẫn hiệu quả hơn nhiều
Phụ thuộc là con dao hai lưỡi. Các tập đoàn phần mềm lớn thường chọn viết lại vì bỏ công bảo trì mã cũ còn đắt hơn. Ở các công ty web/branding nhỏ thì backend chất lượng cao gần như không cần thiết. Nhưng lý do các enterprise pattern đáng sợ xuất hiện là để bảo đảm mã vẫn có thể được cô lập và bảo trì sau 5 năm, kể cả khi tài liệu và ký ức của ngành đã biến mất, mà không phải lệ thuộc vào bên ngoài. Phụ thuộc bên ngoài luôn kèm hai rủi ro: ngừng hỗ trợ hoặc thay đổi phá vỡ tương thích. Cuối cùng nó đều tác động đến dòng chảy phát triển tính năng. Nếu là component nội bộ thì những trade-off này còn có thể kiểm soát trong nhà. Nếu là SaaS thì dùng phụ thuộc thật nhanh để đạt thành công ngắn hạn là hợp lý; còn nếu an toàn và hỗ trợ dài hạn là bắt buộc thì phải nhìn xa mới thành công. Việc viết mã mới hiếm khi là nút thắt của tổ chức
Tôi tò mò các công ty nghiêm túc đến mức nào trong việc quản lý lỗ hổng bảo mật và giấy phép. Trước đây tôi khá dễ dãi với phụ thuộc, nhưng từ khi chuyển sang công ty rất nghiêm về bảo mật và giấy phép thì góc nhìn của tôi thay đổi rất nhiều
Sự khác biệt giữa library và framework cũng quan trọng. Library là công cụ làm tốt một việc, còn framework thì quy định toàn bộ cấu trúc ứng dụng. Cộng đồng Go có xu hướng tránh các framework lớn, ưu tiên thư viện chuẩn, thư viện nhẹ, và nếu cần thì copy/paste source. Ví dụ các framework như Gin (web API), GORM (ORM) có trải nghiệm dùng tốt, nhưng lại giới hạn cấu trúc bên trong và làm tăng độ phức tạp. Bản thân SDK chuẩn của Go đã khá mạnh, nên tôi nghĩ không nên thêm phụ thuộc quá mức cần thiết
Tác giả là người New Zealand. Đằng sau đó là tinh thần Number 8 wire của New Zealand, tức thái độ 'có gì dùng nấy, khéo tay thì xoay xở được' (trang wiki về Number 8 wire)
Nhiều lập trình viên giàu kinh nghiệm ở các nước khác ngoài New Zealand cũng thường đồng ý theo hướng này. Hầu như ai cũng từng khổ sở vì chọn sai phụ thuộc hoặc vì phải nâng cấp một thư viện không cần thiết
Là người New Zealand, tôi có cảm giác tinh thần Number 8 Wire đã biến mất từ 20 năm trước rồi
Hôm nay tôi mới biết điều này. Nó làm tôi nhớ đến câu cửa miệng của Úc: "She'll buff out, mate"
Quy mô triển khai lớn và tính thương mại của phụ thuộc cũng ảnh hưởng đến khả năng mở rộng. Nếu một công cụ đã được dùng ở nơi triển khai lớn hơn tôi từ 100 đến 1000 lần, thì xác suất nó chạm giới hạn trong bài toán của tôi sẽ thấp hơn. Bug cũng có xu hướng được phát hiện hoặc sửa trước ở quy mô đó, nên cuối cùng với tôi nó an toàn hơn
Thư viện lớn đôi khi lại không chạy nổi trong môi trường nhỏ. Ví dụ trước đây trình biên dịch protocol buffer cho Swift từng bị crash ở những field không được dự đoán trước. Nhiều công ty lớn cũng không thực sự test đường đi đó nếu nó không liên quan đến mục đích ở quy mô lớn của họ
Tôi từng phát hiện bug nghiêm trọng trong các thư viện nổi tiếng do những công ty lớn như Meta, Google, Microsoft làm ra. Dù có báo issue thì việc sửa rất lâu và họ cũng rất ngại thay đổi. Trong những tình huống đó, tự làm lại rốt cuộc còn nhanh hơn và hiệu năng cũng tốt hơn. Đặc biệt trong ngành tư vấn, hướng công việc thường bị khách hàng kéo lệch bởi các yêu cầu vô lý. Khi sự tự tin rằng mình có thể tự làm tăng lên, tôi thấy nhiều lúc tự triển khai còn tốt hơn là mang vào một phụ thuộc khổng lồ bên ngoài. Tất nhiên tôi không đụng đến những bài toán thật sự khổng lồ như trình duyệt hay mô hình AI, nhưng những thứ như engine suy luận chạy cục bộ, HTML renderer, hay graph database tự viết thì tôi vẫn làm. Điều khách hàng kỳ vọng không phải cái mới mẻ hay đổi mới, mà là giảm rủi ro. Khi tự phát triển, tôi thấy việc bám sát tiến độ dễ hơn nhiều. So với thời gian ngồi đọc tài liệu của Google hay các ông lớn khác, tự làm còn hiệu quả hơn. Ba tháng gần đây tôi làm đêm 12 tiếng để cứu một dự án mà đội trước bỏ mặc, nên gần đây tôi suy nghĩ khá nhiều về chuyện này