6 điểm bởi GN⁺ 2025-01-01 | 2 bình luận | Chia sẻ qua WhatsApp
  • Các đề xuất thiết kế hệ thống bắt đầu bằng “let’s just” thường trở nên phức tạp hơn dự kiến, và phán đoán kiến trúc tốt phụ thuộc rất nhiều vào quy tắc kinh nghiệm cũng như hiểu biết ngữ cảnh
  • Kiến trúc plugin, thêm API, các lớp trừu tượng nghe có vẻ hợp lý, nhưng trên thực tế phải đồng thời gánh khả năng tương thích về hành vi, bảo trì, bảo mật, hiệu năng và nhu cầu của hệ sinh thái
  • Xử lý bất đồng bộ, kiểm soát truy cập, đồng bộ hóa dữ liệu có vẻ là những chủ đề quen thuộc, nhưng trong môi trường sản phẩm rất dễ dẫn đến các lỗi khó tái hiện, phải thiết kế lại mô hình bảo mật và những bài toán đồng bộ hóa nan giải
  • Cross-platform và lối thoát native (native escape) có thể hữu ích với sản phẩm ban đầu đơn giản, nhưng khi tính năng nền tảng và trạng thái nội bộ bắt đầu tách rời nhau, rất khó duy trì chất lượng và tính nhất quán
  • Những mẫu này không phải lúc nào cũng sai, nhưng phần lớn là không cần thiết hoặc có phương án thay thế; thay vì chọn cách tiếp cận dễ thất bại, cần giải lại vấn đề từ các nguyên lý nền tảng (first principles)

Vì sao “let’s just” lại nguy hiểm

  • Các đề xuất theo sau “let’s just” trong 9 trên 10 trường hợp sẽ trở thành công việc phức tạp hơn nhiều so với những gì được hình dung trong phòng họp
  • Kỹ thuật cũng có tính chất của khoa học xã hội, nên việc cái gì hoạt động được là phụ thuộc vào ngữ cảnh
  • Khi nói rằng một cách tiếp cận nào đó không hoạt động, với kỹ sư điều đó rất dễ được hiểu như một lời thách thức phải chứng minh phản ví dụ
  • Một phần đáng kể của quản lý kỹ thuật và kiến trúc phần mềm là sự kết hợp giữa quy tắc kinh nghiệm (rule of thumb) rút ra từ kinh nghiệm và những bài học khó khăn

“Cứ làm cho nó hỗ trợ plugin là được”

  • Khi một triển khai duy nhất có vẻ chưa đủ, việc gắn thêm một triển khai mới vào cùng kiến trúc có vẻ như sẽ giúp người gọi API nhận được cải tiến hoặc tính năng mới mà không cần thay đổi gì
  • Nhưng vì “API không phải là file header hay tài liệu, mà là chính hành vi”, gần như không có plugin nào chỉ cần cắm vào là chạy được
  • Thành phần gần với plugin nhất trong phần mềm hiện đại là device driver
    • Trước đây driver có chất lượng hành vi kém nên ngày nay không còn được chấp nhận, hoặc các hệ điều hành hiện đại đã chuyển sang hướng tự xây dựng driver của mình
  • Để tạo ra một kiến trúc thật sự có thể cắm plugin, cần thiết kế cả triển khai thứ hai cùng lúc với triển khai mặc định; như vậy ít nhất mới có bằng chứng rằng nó từng hoạt động

“Cứ thêm API là được”

  • Sau khi sản phẩm hoặc công ty đạt được một mức thành công nhất định, việc thêm API với lý do “phải trở thành nền tảng để thu hút nhà phát triển” diễn ra khá thường xuyên
  • Nhà cung cấp API phải liên tục đánh đổi giữa việc thêm tính năng và khả năng tương thích·khả năng liên thông, còn hành vi hiện có và đặc tính hiệu năng khiến mức độ tự do khi thay đổi bị thu hẹp đáng kể
  • Có API không có nghĩa là chắc chắn sẽ có người muốn dùng
    • API mới thường xuất hiện khi sản phẩm muốn một chức năng nào đó nhưng không đặt đủ ưu tiên nội bộ cho nó
    • Thị trường mục tiêu nhỏ, chuyên biệt theo ngành dọc hoặc giới hạn trong một miền cụ thể, nên kỳ vọng đối tác bên ngoài sẽ dùng API để lấp khoảng trống
    • Nhưng những đối tác đó cũng có hoạt động kinh doanh và khách hàng riêng, nên họ có thể không muốn giải quyết vấn đề bằng cách đưa thêm một sản phẩm khác vào
  • Trở thành nền tảng là một hoạt động kinh doanh có nhu cầu thực tế lớn; chỉ cung cấp vài API hiếm khi tạo được nền tảng kinh tế cho bên thứ ba

“Thêm một lớp trừu tượng nữa thôi”

  • Câu nói của Butler Lampson rằng “mọi vấn đề trong khoa học máy tính đều có thể được giải quyết bằng một lớp gián tiếp khác” có phần đúng trong thực tế
  • Thất bại chủ yếu xuất hiện theo hai cách
    • Trừu tượng hóa được đưa vào quá sớm sẽ nằm lại trong kiến trúc như trừu tượng hóa quá mức mà không có kế hoạch sử dụng thực tế
    • Trừu tượng hóa được thêm vào sau có thể khiến bảo trì, bảo mật và tối ưu hiệu năng trở nên cực kỳ phức tạp
  • Windows NT ngay từ đầu đã có nhiều lớp trừu tượng quá mức nhưng thực tế không được dùng
  • Trong quá trình tiến hóa của Mac OS, có những lớp trừu tượng ban đầu trông kỳ lạ nhưng trở nên hữu ích sau hai bản phát hành; điểm khác biệt là có tồn tại kế hoạch
  • Nếu trừu tượng hóa bổ sung sau chỉ được dùng ở một phần mã, sẽ có nhiều mã không dùng trừu tượng hóa mới, làm tăng gánh nặng bảo trì

“Làm bất đồng bộ đi”

  • Một phần đáng kể trong 25 năm đầu của khoa học máy tính được dành cho bài toán hiểu và triển khai hành vi bất đồng bộ
  • Trong các chương trình sau đại học những năm 1980, người ta dành nhiều thời gian cho các chủ đề như các triết gia ăn tối, producer-consumer, và thợ cắt tóc ngủ
  • Ngày nay, nhờ các quy tắc ở tầng dữ liệu và web framework, nhiều kỹ sư xử lý phần lớn vấn đề bất đồng bộ ở dạng đã được trừu tượng hóa
  • Nếu tự quản lý bất đồng bộ bên ngoài framework hoặc tầng dữ liệu, ban đầu có thể trông như hoạt động tốt, nhưng một năm sau có thể xuất hiện lỗi khó tái hiện
  • Chỉ còn biết hy vọng lỗi đó không phải là vấn đề làm hỏng dữ liệu

“Kiểm soát truy cập để sau thêm cũng được”

  • Vị trí đặt kiểm soát truy cập từng là chủ đề tranh luận mang tính lý thuyết, nhưng các hệ thống hiện nay đang chịu tấn công liên tục nên nằm trong môi trường phức tạp hơn nhiều
  • Ai cũng biết cần bảo mật ngay từ đầu, nhưng vì tốc độ đưa sản phẩm ra thị trường, rất ít hệ thống thiết kế đầy đủ kiểm soát truy cập và mô hình bảo mật ngay từ đầu
  • Nếu không bắt đầu từ góc nhìn của khách hàng và kẻ tấn công, rất khó tạo ra thiết kế kiểm soát truy cập phù hợp với sản phẩm
  • Cách gắn thêm kiểm soát truy cập về sau có thể thất bại, hoặc dẫn đến tình huống phải viết lại sản phẩm trong tương lai
  • Việc viết lại đó sẽ là trải nghiệm không tốt cho tất cả mọi người, bao gồm cả khách hàng

“Đồng bộ hóa dữ liệu đi”

  • Trong môi trường có nhiều thiết bị, ứng dụng SaaS và kho lưu trữ dữ liệu, đề xuất “cứ đồng bộ hóa dữ liệu là được” xuất hiện rất thường xuyên
  • Như Ray Ozzie, người tiên phong về client/server và đồng bộ hóa dữ liệu, từng nhấn mạnh, đồng bộ hóa là một bài toán khó
  • Trong khoa học máy tính, bài toán khó nghĩa là một vấn đề rất hóc búa với nhiều thách thức chỉ có thể học qua kinh nghiệm
  • Ngay cả với kho dữ liệu có ngữ nghĩa đầy đủ và giao dịch, đồng bộ hóa vẫn khó; khi có blob, dữ liệu phi cấu trúc và chuyển đổi dữ liệu, độ khó tăng vọt
  • Xây giải pháp dựa trên đồng bộ hóa dữ liệu gần như luôn là lựa chọn không mong muốn, và đây cũng là lý do tồn tại những công ty trị giá hàng chục tỷ USD chỉ xoay quanh đồng bộ hóa

“Làm cross-platform đi”

  • Cross-platform là cuộc tranh luận đã lặp lại từ lâu; sẽ luôn có người nói mã của họ hoạt động tốt hoặc nêu Unity và game làm ví dụ
  • Khi nói sẽ làm thứ gì đó cross-platform, thực chất gần như đang cam kết xây một trong các thứ: hệ điều hành, nhà cung cấp đám mây hoặc trình duyệt
  • Cross-platform hoạt động tốt trong hai trường hợp
    • Khi nền tảng còn mới và đơn giản, ví dụ cloud chỉ ở mức compute và lưu trữ đơn giản
    • Khi ứng dụng hoặc sản phẩm còn mới và đơn giản
  • Điều kiện này sẽ bị phá vỡ khi bắt đầu tách khỏi nền tảng cơ sở, hoặc khi tạo ra các tính năng được biểu diễn hoàn toàn khác nhau trên từng nền tảng mục tiêu
  • Microsoft đã fork mã Office vào năm 1998 vì việc dùng cùng một mã cho Office cho Mac và Office cho Windows trở nên khó khăn, và sau đó không quay lại nữa
  • Ban đầu Microsoft tồn tại như một doanh nghiệp tạo ứng dụng cross-platform, nhưng đó là cách vận hành hiệu quả ở thời kỳ tài liệu API hệ điều hành chỉ khoảng 100 trang và mỗi OS đều phái sinh từ CP/M
  • Bài liên quan: Divergent Thoughts on Cross Platform

“Nếu cần thì cho thoát ra native”

  • Vì cross-platform chỉ hoạt động tốt trong thời gian ngắn, các framework và lớp trừu tượng API thường cung cấp lối thoát native (native escape)
  • Ý tưởng này là khi nền tảng phát triển, có thể gọi trực tiếp chức năng từ nền tảng native mà framework chưa kịp phơi bày
  • Nhưng framework hoặc API cung cấp lớp trừu tượng cũng duy trì trạng thái nội bộ hoặc cache
  • Khi gọi trực tiếp nền tảng native, các cấu trúc dữ liệu và trạng thái mà framework không biết tới sẽ bị thay đổi
  • Một số framework cung cấp cơ chế trao đổi dữ liệu hoặc trạng thái giữa mã thoát ra ngoài và framework, nhưng đó gần giống với một giải pháp đưa lại kiểu kiến trúc malloc/free trong thời đại quản lý bộ nhớ tự động

Có thể chọn, nhưng không phải mặc định

  • Không phải lúc nào cũng phải trả lời “không” với những cách tiếp cận này
  • Trong một số ngữ cảnh cụ thể, các cách này có thể hoạt động
  • Tuy nhiên, trong phần lớn trường hợp, những mẫu này là không cần thiết hoặc có cách tốt hơn
  • Thay vì chọn ngay các mẫu phần mềm có xác suất thất bại cao, cần giải quyết vấn đề bằng các nguyên lý nền tảng

2 bình luận

 
ndrgrd 2025-01-02

Trong trường hợp Plug, việc thiết kế interface bằng cách lọc ra tối đa các hành vi bắt buộc là quan trọng nhất.
Nếu xây dựng interface chỉ bằng cách bê nguyên cấu trúc từ code hiện tại thì tất nhiên nó sẽ trở thành một interface không cần thiết và bị ràng buộc bởi implementation đó, nhưng tình huống này thực sự xảy ra rất nhiều...

 
GN⁺ 2025-01-01
Ý kiến trên Hacker News
  • Vấn đề của những ý tưởng kiểu này không nằm ở bản thân ý tưởng, mà ở cách tiếp cận kiểu “cứ làm thử thôi” hoặc kỳ vọng đặt phía trước nó
    Ví dụ, nếu xem API như “chỉ là một tính năng” của sản phẩm theo kiểu “cứ thêm API vào thôi”, thì có lẽ tỷ lệ thành công cũng tương tự “cứ thêm UI vào thôi”
    Để làm một UI tốt, cần phải thận trọng và kỹ lưỡng, và cũng cần chuyên gia trong lĩnh vực đó
    Không có lý do gì để các giao diện khác của sản phẩm lại khác đi; điểm cốt lõi không phải là ý tưởng đó tệ hay hay, mà là đó là việc không thể làm theo kiểu cứ thế mà làm

    • Ở công ty cũ của tôi từng có một quy tắc
      Người duy nhất được nói “cứ” là lập trình viên thực sự chịu trách nhiệm làm cho nó chạy được; nếu một lập trình viên khác nói từ đó, coi như họ tự nguyện nhận việc ấy
      Quy tắc đó rất hợp với chúng tôi
    • Đúng vậy. Khi thêm API thì không có chuyện “cứ”
      Để làm API cho đúng, cần rất nhiều thiết kế và độ phức tạp
      Nếu thiết kế không tốt, client có thể phải gọi nhiều lần cho việc đáng lẽ chỉ cần một lần, hoặc API gây nhầm lẫn khiến họ gọi sai hay thậm chí không dùng được
      Cũng cần xác thực và phân quyền, nên phải thiết lập OAuth2, hoặc ít nhất phải có cách tạo, lưu trữ và kiểm tra API token an toàn
      Dữ liệu cũng phải được xử lý an toàn, và nếu hiệu năng kém thì cơ sở dữ liệu có thể bị tải đè nặng
      Nếu thêm caching, sẽ phát sinh thêm độ phức tạp về vô hiệu hóa cache và về máy chủ/quy trình để hỗ trợ cache
      Nếu không có giới hạn tốc độ, client viết cẩu thả có thể nện API liên tục; còn tài liệu kém thì về cơ bản API trở nên vô dụng
      Tùy trường hợp còn phải cung cấp SDK cho nhiều ngôn ngữ, và nếu không có thông báo lỗi tốt, người dùng mới sẽ không biết vì sao lời gọi thất bại
    • Phần lớn lời khuyên chuyên môn giống lời khuyên hẹn hò
      Người ta khái quát hóa những gì từng không hiệu quả với mình, nhưng nếu không phải cùng một người trong một hoàn cảnh rất giống thì hầu như không thể áp dụng y nguyên
      Những lời khuyên nhất quán đúng thường lại quá chung chung, kiểu “hãy suy nghĩ cẩn thận, cố làm điều đúng, rồi nhìn lại kết quả”, đến mức gần như vô dụng
      Những câu như vậy thì khó bán được bài blog hay sách
    • Nghề presales dường như tồn tại gần như hoàn toàn vì từ “cứ”
      Vì họ phải đảm bảo khách hàng không hoảng sợ khi bắt đầu kéo phần còn lại của tảng băng lên khỏi mặt nước
    • Đúng vậy. Cách định vị bài viết sẽ tốt hơn nhiều nếu là “thực ra cần những gì để khiến các ý tưởng hệ thống cứ thế mà chạy được
      Khi đó, bài viết không còn là chuyện có thể/không thể, mà là về cách thành công và thất bại
      Khi đã biết các chi tiết cần thiết, cũng khó mà nói “cứ” trong khi còn không biết mình không biết gì
      Tuy nhiên, về DSL thì tôi gần như đồng ý 100%
      Nó gần với kiểu làm phức tạp không cần thiết và cố tỏ ra dễ thương; người đủ tư cách tạo ra nó nên là người đã tạo một ngôn ngữ lập trình thành công, rồi còn cập nhật đến bản thứ hai hoặc một ngôn ngữ thứ hai sau khi rút kinh nghiệm từ sai lầm, nhưng dù vậy vẫn sai rất nhiều thứ
  • (1) DSL đôi khi hoạt động rất tốt. Xem https://www.jooq.org/
    (2) Elastic Load Balancer là một vòng lặp điều khiển phản ứng với tải công việc, và loại này đã là công nghệ phổ dụng
    (3) Trong hầu hết các ngành, việc provision thiếu là rất phổ biến. Xem https://erikbern.com/2018/03/27/waiting-time-load-factor-and...https://www.amazon.com/Goal-Process-Ongoing-Improvement/dp/0...
    (4) Phát hiện bất thường về bản chất không phải là vấn đề hệ phân tán như các mục khác, nhưng ai từng bị một cú đau lớn có thể cảm thấy nó là cần thiết
    Đây cũng là một lĩnh vực khó về mặt trí tuệ
    Thuật toán đầu tiên khiến tôi cảm thấy nó khá thông minh là https://scikit-learn.org/1.5/modules/outlier_detection.html#..., và đôi khi nó hoạt động như phép màu
    Khi áp dụng cho văn bản bằng embedding dựa trên CNN mà tôi dùng năm 2018 thì khá hợp, nhưng với SBERT thì hoàn toàn không gặp may

    • Tôi từng viết hai DSL, một cái làm cùng nhóm, và tôi xem cả hai đều thành công
      Chúng giải quyết được vấn đề và không ai chửi bới gì
      Yếu tố quan trọng nhất có lẽ là cả hai đều nhỏ
      Chúng rất giống nhau và cũng tái sử dụng mã
      Một cái dùng để viết quy tắc kiểm tra hợp lệ cho các biểu mẫu khổng lồ, cái còn lại dùng để viết quy tắc ra quyết định dựa trên phản hồi biểu mẫu
      Một DSL tốt giúp người trước đây không làm được trở nên làm được
      DSL được tạo ra để tiết kiệm thời gian có khả năng hữu ích thấp hơn nhiều, vì rất có thể nó thực ra không tiết kiệm được thời gian
      Trong cả hai trường hợp, chúng tôi đều phải đưa hành vi miền phức tạp vào chương trình
      Vì vậy hoặc phải dạy miền cho lập trình viên, hoặc ghép lập trình viên với chuyên gia miền, hoặc dạy lập trình cho chuyên gia miền
      Nếu khối lượng việc lớn, việc trao quyền vào tay chuyên gia miền trở nên hấp dẫn
      Lập trình viên có thể làm việc khác và vòng lặp phản hồi cũng ngắn hơn
      Nếu miền sâu, bạn sẽ không muốn gửi lập trình viên đi học để nắm nó; nếu miền nông, một người rẻ hơn cũng có thể xử lý
      DSL đi kèm gánh nặng nhận thức lớn
      Tuy nhiên, nếu phương án thay thế là học một ngôn ngữ lập trình đầy đủ thì nó trở nên hợp lý hơn
      DSL để tiết kiệm thời gian là trường hợp người đã biết viết code muốn viết ít code hơn, nhưng mức tiết kiệm nhỏ nên nhìn chung không hay lắm
      Rồi khi lập trình viên muốn thay đổi thứ gì đó, họ phải học hoặc nhớ lại toàn bộ DSL này thay vì đọc mã trực quan
      Một kinh nghiệm đơn giản hơn là: DSL dành cho lập trình viên ít có khả năng là ý tưởng tốt hơn DSL dành cho người không phải lập trình viên
    • jOOQ là một thảm họa và tôi không khuyên ai dùng
      Tôi viết truy vấn SQL, kiểm thử trong công cụ như DataGrip, rồi mất hàng giờ tìm cách chuyển nó sang DSL
      Nếu dùng các tính năng SQL “ngoại lai” như biểu thức JSON thì vấn đề còn tệ hơn
      Debug trở thành luồng “in SQL được sinh ra, copy vào nơi như DataGrip, tinh chỉnh truy vấn, rồi tìm cách nhét lại nó vào DSL”
      Đó là sự lãng phí thời gian khủng khiếp
      Điểm bán hàng cốt lõi của jOOQ là truy vấn an toàn kiểu, nhưng khi IntelliJ bắt đầu kiểm chứng SQL dạng chuỗi trong code theo dữ liệu thực, tầm quan trọng của nó biến mất
      Luồng chỉnh sửa SQL trực tiếp và kiểm thử ngay với database đơn giản là tốt hơn
      jOOQ củng cố luận điểm của bài gốc về DSL
    • Ngoại trừ những thứ như biểu thức chính quy, tôi chưa thấy DSL nào tốt, mà ngay cả cái đó tôi cũng nghe nói nhiều người bất mãn với chính ngôn ngữ này
      Ví dụ về các DSL phổ biến có thể xem là tệ hoặc gần như thất bại gồm HCL, E4X, XUL, ngôn ngữ định dạng chuỗi của Common Lisp, v.v.
      HCL là ngôn ngữ cấu hình Terraform, và rõ ràng ngay từ đầu nó đã không xử lý bài toán rất phổ biến là provision số lượng thiết bị tương tự theo số lượng biến
      Các nỗ lực thêm tính năng về sau cũng gượng gạo và không giải quyết trọn vẹn vấn đề
      E4X là DSL JavaScript để thao tác XML; trong các trường hợp đơn giản nó giúp biểu diễn thao tác XML gọn hơn, nhưng rất nhanh có thể trở nên khó đọc như một bức tường dấu câu
      Tương tự LINQ của Microsoft, nó hoàn toàn không cho người viết biết độ phức tạp tính toán của mã bên trong là cỡ nào
      Cuối cùng, mã dùng DSL này thường được viết lại theo cách kém ngắn gọn hơn nhưng dễ phân tích hơn
      XUL là ngôn ngữ UI cho phần chrome mở rộng trình duyệt Firefox, và với mục đích tạo extension Firefox thì nó ổn
      Nhưng Firefox cũng muốn bán nó như công nghệ nền tảng cho ứng dụng nội bộ doanh nghiệp, và ở lĩnh vực đó thì nó còn thiếu rất nhiều
      Để làm việc đơn giản cần rất nhiều mẹo và cách vòng vèo
      Ngôn ngữ định dạng chuỗi của Common Lisp cũng tương tự: ổn cho vấn đề nhỏ nhưng không có khả năng mở rộng
      Một số vấn đề định dạng đòi hỏi giải pháp rất kỳ quặc hoặc vốn dĩ không có đáp án, và nhìn mã gọi đệ quy format thật sự khiến tôi ghét
      Nhìn chung, vấn đề thường gặp nhất của cách tiếp cận này là nó mang tính chắp vá và không mở rộng tốt
      Bạn sẽ sớm đụng vào vấn đề không thể giải quyết đúng cách, và các chương trình lớn viết bằng DSL thường là ác mộng để xử lý
    • Mỗi lần thấy sự ghét bỏ DSL tôi lại thấy lạ, rồi nhận ra thứ mọi người phê phán không phải DSL nói chung mà là DSL phải tự viết từ đầu
      Nếu đặt DSL lên trên Lisp, bạn chỉ cần viết logic miền chứ không phải ngôn ngữ nền
      Phần lớn công việc đã xong, và ngôn ngữ hữu ích ngay từ ngày đầu
      Nếu làm thành DSL được host trên Lisp thì nó có thể thật sự được dùng; tôi không hiểu vì sao cứ phải tạo một ngôn ngữ mới từ đầu rồi nhìn nó héo chết
    • DSL hoạt động tốt khi có IDE hỗ trợ tự động hoàn thành và vòng lặp phản hồi nhanh hoặc tức thì
  • Tất cả các mục này đều có nhiều trường hợp thành công
    Khó có thể dùng từ “gần như” làm lối thoát
    Điều này trông chỉ giống chủ nghĩa bi quan và sự hoài nghi mệt mỏi
    Tôi hiểu tâm trạng đó và cũng từng cảm thấy như vậy; đôi khi cũng khó khiến một kỹ sư đầy nhiệt huyết từ bỏ một ý tưởng tồi
    Nhưng bầu không khí kiểu này với tôi có vẻ độc hại

    • Có vẻ như đây chỉ là cách diễn đạt “những thứ này khó hơn vẻ ngoài để xây dựng cho đúng hoặc triển khai hiệu quả” theo kiểu dễ thu hút, nói thẳng ra là giật tít
    • Đằng sau khá nhiều trường hợp “thành công” như vậy là một đội kỹ sư dày dạn trận mạc xử lý mọi thất bại của ý tưởng đó
      Những vòng lặp điều khiển lao vọt tới vô cực hoặc các biên tối đa/tối thiểu, cache không thể phục hồi sau sự cố phân tán, trạng thái bị hỏng trong lúc live migration, các burst gây quá tải vào những thời điểm khó chịu, những cảnh báo phát hiện bất thường giả chỉ vì ngày nghỉ lễ trên toàn thế giới, v.v.
      Bên dưới tất cả các ý tưởng đó là một mớ phức tạp đan xen mà gần như ai cũng đánh giá thấp
    • Nhìn chung tôi đồng ý
      Đây là danh sách các ý tưởng hệ thống khó hơn so với suy nghĩ ban đầu, cần được tiếp cận nghiêm túc chứ không nên xem nhẹ
      Nghe hơi giống “trông có vẻ hay nhưng gần như không bao giờ hoạt động”, nhưng xét chi tiết thì hoàn toàn khác
      Nếu coi đó là vấn đề khó và đầu tư tương xứng, việc làm cho nó hoạt động đúng là một kết quả bình thường có thể đạt được
      Nếu là tính năng gắn thêm sau hoặc ngây thơ nghĩ là dễ, nó thường sẽ sai
    • Tôi không đọc nó thành “chủ nghĩa bi quan và sự hoài nghi mệt mỏi”
      Tôi đọc nó như câu chuyện về việc các kỹ sư mải mê tối ưu hóa sớm dù không có lợi ích kinh doanh
      Điều này thật sự lan tràn trong ngành: thay vì chuẩn bị bản backup có thể triển khai trong vài giờ và over-provision server 200% để có thể chỉ tốn dưới một phần mười chi phí, họ lại thấy thú vị khi thiết kế và xây dựng một con tàu vũ trụ tự động mở rộng có dự phòng
      Có lúc những ý tưởng như vậy là hợp lý, nhưng đó là sau khi chúng trở nên cần thiết
      Chúng không phải thứ nên thiết kế sẵn vào giai đoạn sản phẩm ban đầu
      Rất hiếm sản phẩm thật sự cần tới quy mô, độ khả dụng và độ phức tạp mà các triển khai này đang cố giải quyết
    • Có vẻ Steven không nói rằng những thứ này là bất khả thi, mà là chúng khó và thường diễn ra không suôn sẻ một cách bất thường
  • Có vẻ nhiều người ở đây đang cố tìm một hàm phán đoán tinh tế để phân biệt ngoại lệ, nhưng thật ra rất đơn giản
    Những ý tưởng này sẽ tuyệt vời nếu tôi làm, còn nếu thằng ngốc đi trước tôi làm thì chúng tuyệt đối không bao giờ hoạt động như dự định

    • Chính xác là vậy
      Và đôi khi thằng ngốc đi trước đó lại là chính tôi vài tháng trước
  • Tôi cũng muốn thêm thiết kế hướng miền vào đây
    Cố gắng căn chỉnh ứng dụng theo cấu trúc kinh doanh rồi đóng băng thiết kế nghiệp vụ là công thức dẫn tới thảm họa
    Nếu doanh nghiệp nhỏ hoặc trì trệ thì có thể không cảm thấy vấn đề
    Nhưng nếu doanh nghiệp thành công hoặc tăng trưởng, bạn sẽ nhanh chóng hối hận vì đã cố tạo ra những domain mang tên mô tả kinh khủng, bị trói vào các thông lệ công việc đã lỗi thời
    Thay vào đó, thiết kế xoay quanh các tầng chức năng theo cách đã được kiểm chứng trong nhiều thập kỷ, và đặt business logic càng nhiều càng tốt vào cấu hình, các hàng trong cơ sở dữ liệu và workflow của người dùng sẽ linh hoạt hơn nhiều

    • Cả hai lựa chọn đều sẽ khiến bạn hối hận
      Bạn nêu các cạm bẫy của thiết kế hướng miền là ngôn ngữ lỗi thời và khả năng tái sử dụng code/hệ thống thấp cho các thử nghiệm mới; nhưng ngược lại, một thiết kế trừu tượng cao độ nhồi toàn bộ business logic vào cấu hình, workflow, v.v. cũng chỉ linh hoạt khi cả tổ chức hiểu khá rõ các trừu tượng, cấu hình và vô số tổ hợp đó
      Các tổ hợp ấy nhanh chóng bùng nổ thành một mê cung, tạo ra những hành vi không rõ và không lường trước mà mọi người bắt đầu phụ thuộc vào
      Chi phí onboarding lập trình viên mới và thay đổi đội phát triển cũng trở nên khó gánh
      Tổ chức sẽ nói hai ngôn ngữ khác nhau
      Phần lớn yêu cầu tính năng nhìn bề ngoài đơn giản hoặc trở thành một cuộc tái thiết kế hệ thống khổng lồ nếu phá vỡ trừu tượng, hoặc trở thành “hãy cứ hack trừu tượng này để hiện tại trông như một thay đổi nhỏ và an toàn hơn”
      Trường hợp trước luôn cực kỳ khó, ngay cả khi có những kỹ sư xuất sắc hiểu hoàn toàn hành vi của toàn hệ thống và codebase, cùng các thực hành/quy trình kỹ thuật tốt, và có thể mất nhiều tháng hoặc nhiều năm
      Trường hợp sau xảy ra thường xuyên hơn, và đó là lý do các dự án “trừu tượng hóa cao, phân tầng theo chức năng, dựa trên cấu hình, với business logic mang tính phát sinh” ban đầu trông hoàn hảo và linh hoạt nhưng cuối cùng lại thành “cái quái gì thế này”
      Sau khi hệ thống được triển khai, toàn bộ business logic phát sinh đó trở thành ngôn ngữ mà mọi người nói
      Khi tổ chức nói hai hoặc ba ngôn ngữ hoàn toàn không thể dung hòa với nhau, điều đó rất đau đớn; nếu không có nhiều người có thể dịch trôi chảy qua lại theo chiều dọc lẫn chiều ngang, bạn sẽ cảm thấy đáng ra nên biểu đạt domain sát hơn
    • “Hãy làm cho trạng thái bất khả thi không thể biểu diễn được” cũng thuộc nhóm này
      Nếu bạn thiết kế để một trạng thái nào đó không thể biểu diễn bằng kiểu dữ liệu, bạn phải chắc chắn rằng trong suốt vòng đời của thiết kế đó, trạng thái ấy thật sự là trạng thái bất khả thi
  • Tôi không hiểu rõ mục vòng lặp điều khiển phản ứng với tải
    Đây là thành phần cơ bản và nền tảng của vô số hệ thống
    Bộ điều tốc ly tâm của động cơ hơi nước thế kỷ 19 hay máy phát đĩa Victrola thế kỷ 20 cũng là vòng lặp điều khiển phản ứng với tải
    Toàn bộ ngành điện tử là một mạng lưới các vòng lặp điều khiển phản ứng với tải, và hộp số tự động của ô tô cũng vậy

    • Vấn đề thường gặp là thêm vòng lặp điều khiển mà không hiểu đủ tín hiệu, hoặc thêm mà không xét tới các vòng lặp điều khiển khác
      Mức sử dụng CPU là một ví dụ thú vị
      Chẳng hạn, bạn có thể thấy bộ cân bằng tải liên vùng đấu với cơ chế cắt tải bên trong tiến trình
      Vì tín hiệu của bộ cân bằng tải không phản ánh việc cắt tải, hoặc phản ánh theo cách không chính xác
      Một vấn đề khác là vòng lặp điều khiển cố tối ưu kết quả cục bộ của dịch vụ nhưng lại làm hại kết quả tổng thể
      Nhìn chung, tôi nghĩ nên đặt một số ít vòng lặp điều khiển ở những vị trí có ảnh hưởng lớn
    • Không hoàn toàn rõ, nhưng có thể đặc biệt là nói về tải CPU
      Tải CPU có một số vấn đề như được mô tả trong https://arxiv.org/abs/2312.10172
  • Những vấn đề này có một mô thức chung
    Tất cả đều là mối quan tâm trực giao bổ sung ràng buộc vào mô hình lập trình xử lý dữ liệu tuần tự vốn quen thuộc với lập trình viên
    Mỗi lần thêm một ràng buộc, mọi lập trình viên về sau sẽ có thêm điều phải tiếp tục nghĩ đến trong suốt quá trình phát triển trên hệ thống đó
    Hệ thống dễ rơi vào tình trạng bị ràng buộc quá mức, đến mức không thể tiến lên nếu không nới lỏng một số ràng buộc
    Nếu không phải là bất khả thi thì cũng sẽ chậm lại
    Vì mỗi lần, lập trình viên phải cân nhắc tính năng mới sẽ tương tác thế nào với API, bảo mật, đồng bộ hóa, độ trễ, các nền tảng khác và mã native mà họ đã cam kết hỗ trợ
    Vì vậy cũng có thể hỗ trợ tất cả các thuộc tính này
    Ví dụ, nếu lấy đồng bộ hóa dữ liệu minh bạch làm giá trị cốt lõi của nền tảng, thì mọi phát triển về sau sẽ ưu tiên hỗ trợ nó và phát triển tập tính năng khả thi trong các ràng buộc đó
    Tập tính năng đó có thể không đúng chính xác những gì người dùng muốn, nhưng đó là phạm vi mà nó hỗ trợ
    Sản phẩm sẽ trông hấp dẫn với những khách hàng đặt thuộc tính đó làm tiêu chí số một khi quyết định mua

  • Tôi từng làm các dự án dùng nhiều DSL, cache P2P và mô hình song song hỗn hợp, và tất cả đều hoạt động
    Việc xây dựng chúng cũng thật sự rất thú vị
    Trừ một ngoại lệ, đó đều là các khoản đầu tư tốt
    Cache P2P rốt cuộc không thu hồi được nhiều vì nó không cần thiết
    Vì vậy nói rằng những thứ như thế gần như không bao giờ hoạt động rõ ràng là sai
    Chúng phức tạp, nhưng sự phức tạp đó đem lại những chức năng khó có được theo cách khác
    Bài học rút ra từ ví dụ cache P2P là trước hết phải chắc chắn rằng chức năng đó thật sự cần thiết

  • Đọc hơi lạ, vì tôi đã triển khai thành công khá nhiều ý tưởng trong số này

    • Hoặc là bạn thật sự biết rất rõ mình đang làm gì, hoặc là bạn thật sự chẳng biết gì cả; chỉ một trong hai
  • “Cứ đồng bộ dữ liệu là được” là lý do khiến, theo tiêu chuẩn của tôi, có những ngày rất vất vả
    Tôi đã thấy quá nhiều hệ thống thêm queue, xử lý sự kiện, v.v. với suy nghĩ kiểu “quy mô Internet”, trong khi phạm vi tự nhiên thực tế lại thấp hơn ngưỡng đó rất nhiều
    Những đội như vậy hoặc là ngây thơ, hoặc tệ nhất là đang lợi dụng ban lãnh đạo không hiểu kỹ thuật để kéo tiền về rồi vui vẻ nghịch những vấn đề này

    • Tôi đọc “cứ đồng bộ dữ liệu là được” như một kỳ vọng ngây thơ rằng có thể đọc ở một phía, ghi ở phía kia, mà hai nguồn sự thật không bị tách rời
      Muốn làm cho đúng thì queue và xử lý sự kiện là bắt buộc