Chào bạn,

Đăng nhập xem việc làm phù hợp

Blog IT

Code dependency là tội ác.

Code dependency là tội ác.

“Thay Đổi là hằng số duy nhất…” – Heraclitus (Philosopher)

Những công cụ, thư viện, và framework ngày nay chúng ta sử dụng để xây dựng ứng dụng web đã rất khác với những thứ mà chúng ta từng sử dụng chỉ vài năm trước đây thôi.

Trong vài năm ngắn ngủi từ bây giờ, đa phần những công nghệ này sẽ một lần nữa thay đổi hoàn toàn. Tuy vậy, đa số chúng ta vẫn biến những công cụ này thành một phần trung tâm, không thể tháo gỡ khỏi các ứng dụng ta làm ra.

Chúng ta import, sử dụng, và kế thừa từ đủ thứ framework “thú vị trong tháng” như thể chúng sẽ mãi mãi tồn tại không thay đổi. À… không có chuyện đó đâu. Và vấn đề nằm ở đó.

Sau hơn 20 năm lập trình, thiết kế, và cấu trúc ứng dụng web. Tôi đã bắt đầu chiêm nghiệm được hai sự thật:

  1. External dependency là sự đe dọa đáng sợ đến độ ổn định và sống sót về lâu dài của bất kỳ ứng dụng nào.
  2. Xây dựng bất cứ kiểu ứng dụng không-tầm-thường nào mà không tận dụng external dependencies cũng cực kỳ khó khăn (nếu không muốn nói là không thể)

Bài viết này sẽ trung hòa hai sự thật này, từ đó giúp ứng dụng của chúng ta có cơ hội sống sót cao hơn.

Hang thỏ quả thật rất sâu.

Nếu ta bắt đầu nghĩ đến những thứ mà ứng dụng web của chúng ta cần có, danh sách này cũng lên đến… vài chục mục trước khi chúng ta động tay vô viết code được:

  • Nguồn
  • Kết nối
  • Tường lửa
  • DNS
  • Phần cứng dành cho server (CPU, Ổ cứng, RAM,…)
  • Tản nhiệt
  • Virtualization Platform
  • Container Platform
  • Operating System
  • Web Server Platform
  • App Server Platform
  • Trình duyệt Web

Là lập trình viên, để ý những thứ này cũng tối, nhưng thường ta chẳng thể quản được mấy cái này đâu. Vậy nên, giờ chúng ta hãy tạm gác danh sách này qua một bên và chỉ bàn về code thôi.

Trong code, có ba kiểu dependecies:

1. Dependencies do chúng ta kiểm soát

đây là code do chính chúng ta hoặc công ty của chúng ta viết ra.

2. Dependencies ta không quản được

Code do bên cung cấp thứ ba hoặc cộng đồng nguồn mở viết ra.

3. Dependencies once removed

Đây là những code dependencies cần có cho code dependencies của bên thứ ba.

Chúng ta chủ yếu sẽ bàn về những dependecies ta không điều khiển được.

Dependencies do chúng ta kiểm soát dependencies once removed vẫn có thể làm ta đau hết cả đầu đấy, nhưng với trong trường hợp với dependencies kiểm soát được, chúng ta sẽ có thể trược tiếp can thiệt và ngăn chặn bất cứ vấn đề nào.

Còn trường hợp dependencies once removed, chúng ta thường có thể dựa vào một bên thứ ba để giúp ta quản lý.

Code dependencies của bên thứ ba có gì hay?

Một phần lớn của ứng dụng web tồn tại để giải quyết các vấn đề thường gặp: xác minh, cấp phép, truy cập dữ liệu, xử lý lỗi, điều hướng, logging, mã hóa, hiển thị danh mục, kiểm duyệt nhập liệu vào form,…

Dù bạn có sử dụng tổ hợp công nghệ nào đi chăng nữa, thì rất có thể giải pháp cho những vẫn đề này đã có sẵn đâu đó rồi, chẳng hạn như thư viện chẳng hạn, bạn có thể dễ dàng tiếp cận và ‘gắn’ vào codebase của mình. Ngồi lọ mọ viết lại hoàn toàn những thứ có sẵn này chỉ tổ phí thời gian mà thôi.

Bạn sẽ muốn tập trung vào cách giải quyết những vấn đề bất thường đấy, hoặc chí ít là những vấn đề thông thường, nhưng theo một cách bất thường. Đó sẽ là thứ làm nên giá trị cho ứng dụng của bạn: đoạn code áp dụng quy luật kinh doanh đặc trưng chỉ có ở ứng dụng của bạn – công thức “nước chấm bí mật”.

Thuật toán tìm kiếm của Google, phần lọc timeline của Facebook, phần “gợi ý cho bạn” của Netflix – những đoạn code đằng sau các tính năng này đều là “nước chấm bí mật” cả.

Code của bên thứ ba – ở dạng thư viện – cho phép bạn nhanh chóng tích hợp những tính năng thương mại hóa của ứng dụng bạn đang phát triển, từ đó bạn có thể tập trung chăm chút cho “nước chấm bí mật” của mình.

Code dependencies của bên thứ ba có gì dở?

Hãy xét đến bất cứ ứng dụng web non-trivial được phát triển trong vài năm gần đây và bạn sẽ phải ngạc nhiên bởi lượng code đến từ các thư viện bên thứ ba. Vậy nếu một số thư viện này có sự thay đổi mạnh mẽ, biến mất, hoặc break thì sao?

Nếu là ứng dụng nguồn mở, có thể bạn sẽ tự giải quyết được. Nhưng thư viện đó đâu phải của bạn, liệu bạn hiểu rõ code trong đó đến mức nào? Đa phần lý do bạn tìm đến các thư viện này ngay từ ban đầu là để khỏi phải lo lắng về quá nhiều chi tiết. Nhưng bạn lại đi vào ngõ cụt. Bạn đã hoàn toàn trói buộc tài sản của mình với những depenencies không phải của mình, và chẳng thể kiểm soát được.

Cũng có thể tôi đang thổi phồng vấn đề, hoặc đang nói theo góc nhìn thuần lý thuyết. Nhưng tôi đảm bảo với các bạn – tôi đã từng chứng kiến nhiều vụ khách hàng hoàn toàn kẹt cứng khi đã embed quá nhiều code của bên thứ ba vào ứng dụng của mình. Sau đây chỉ là một ví dụ mới đây…

Một khách hàng cũ của tôi xây dựng ứng dụng thông qua nhà cung cấp Backend-as-a-Service do Facebook sỡ hữu, Parse. Họ dùng một JavaScript client library do Parse cung cấp để có thể sử dụng dịch vụ của Parse. Trong quá trình này, họ đã liên kết chặt chẽ tất cả code của mình (kể cả code “nước chấm bí mật”) với thư viện này.

Ba tháng sau sau lần ra mắt đầu tiên của vị khách hàng này – chỉ ngay khi họ bắt đầu thu hút được những khách hàng trả tiền thực sự – Parse lại tuyêt bố đóng cửa.

Và thay vì tập trung vào sản phẩm và mở rộng số khách hàng, vị khách hàng này phải tìm cách khắc phục lỗ hổng này, chuyển sang phiên bản Parse tự host, nguồn mở, hoặc thay thế hoàn toàn Parse luôn.

Sự gián đoạn này nghiệm trọng đến mức họ đã phải hoàn toàn loại bỏ ứng dụng này luôn, một ứng dụng tiềm năng nhưng còn quá non trẻ.

Cân bằng hay-dở

Vài năm trước, giải phải để giải giải quyết rủi ro, vừa tận dụng được lợi ích của các thư viện bên thứ ba là: gói chúng bằng Adapter Pattern.

Về cơ bản, bạn sẽ gói code bên thứ ba trong một class hoặc module adapter mà bạn đã viết ra. Thủ thuật này sẽ giúp bạn phần nào kiểm soát được các tính năng của thư viện bên thứ ba.

Khi sử dụng phương pháp này, nếu một thư viện hoặc framework bên thứ ba thay đổi, hoặc mất đi, bạn chỉ việc sửa đổi adapter code một chút thôi. Phần còn lại của ứng dụng vẫn không đổi.

Adapter pattern diagram from Dofactory.com

Về lý thuyết, nghe rất hợp lý phải không. Khi bạn có dependences khép kín chỉ cung cấp một vài tính năng, giải pháp này cũng đủ rồi. Nhưng mọi thứ sẽ nhanh chóng đổ vỡ.

Bạn có tưởng tượng được, phải gói cả một thư viện React (kể cả JSX) phải mất đến bao lâu không? Vậy còn jQuery, hay Angular, hay Spring framework trong Java nữa? Nghe thôi cũng đã hãi hùng rồi.

Vậy nên, tôi nghĩ các bạn nên đi theo cách tiếp cận linh hoạt hơn…

Với mỗi dependency mà bạn muốn thêm vào codebase, đánh giá mức độ nguy cơ của chúng dựa theo hai tiêu chí sau:

  1. Khả năng thay đổi (về chất) của dependency.
  2. Mức độ thiệt hại mà sự thay đổi (về chất) của dependency có thể gây ra cho ứng dụng.

Một thư viện hoặc framework sẽ ít có khả năng thay đổi hơn nếu một số hoặc tất cả điều dưới đây là đúng:

  • Đã đứng vững được nhiều năm và đã có nhiều lần phát hành lớn.
  • Được nhiều ứng dụng thương mại sử dụng rộng rãi.
  • Được một cộng đồng đông đảo hỗ trợ không ngừng.

Một thư viện hoặc framework sẽ gây ít thiệt hại hơn nếu một số hoặc tất cả điều dưới đây là đúng:

  • Chỉ được một phần nhỏ của ứng dụng sử dụng.
  • Phần code liên quan trực tiếp đến nó không dính đến “nước chấm bí mật” mà ta đã đề cập.
  • Xóa bỏ nó yêu cầu rất ít thay đổi đến codebase của chúng ta.
  • Toàn bộ ứng dụng của bạn rất nhỏ, và có thể viết lại nhanh chóng.

Thư viện nào càng rủi ro, thì bạn lại càng nên gói lại, hoặc tránh dùng hẳn luôn.

Bạn phải cực kỳ cần trọng và lo lắng bảo vệ phần giá trị trung tâm của ứng dụng, ‘nước chấm bí mật’. Hãy làm sao để phần code này càng độc lập càng tốt. Nếu bạn rất rất cần đến dependecy, hãy inject thay vì reference. Ngay cả khi inject, bạn vẫn phải cẩn thận.

Đôi khi, việc này đồng nghĩa bạn sẽ nó lời “tạ từ” với một thư viện nào đó rất hay ho (hay bạn rất muốn sử dụng vì nhiều lý do). Hãy thật mạnh mẽ nhé. Tin tôi đi, sự  hy sinh này sẽ được đền đáp. Cú xem trường hợp mấy người đầu tư mạnh tay lúc Angular mới ra mắt, hay ông khách hàng dùng Parse của tôi thì biết. Chả vui đâu.

Nói đến “vui”. Các bạn xem thử hình dưới đây nhé…

Dependency graph for TinyTag explorer

Hình ảnh trên là biểu đồ dependency cho ứng dụng mang tên TinyTag Explorer.

Vẽ ra một biểu đồ dependency cho các ứng dụng bạn đã tạo ra là một cách rất hay để hiểu thêm về mức độ rủi ro do các dependency mang lại. Đây là công cụ tôi hay sử dụng để tạo những biểu đồ những thế này với nhiều thứ ngôn ngữ như JavaScript, C#, Java, PHP, và Python.

Techtalk via medium

Nguồn: Techtalk.vn

Bài viết tương tự

Bài viết nổi bật

Công việc liên quan