1 điểm bởi GN⁺ 2024-02-26 | 1 bình luận | Chia sẻ qua WhatsApp
  • Với các dự án mã nguồn mở quy mô 10k~200k dòng, việc đặt tài liệu ARCHITECTURE cạnh README, CONTRIBUTING có thể giảm chi phí để người đóng góp mới nắm được cấu trúc mã
  • Trong một dự án xa lạ, vấn đề lớn hơn việc viết patch chậm đi khoảng 2 lần là mất nhiều thời gian hơn gấp 10 lần để tìm ra cần sửa ở đâu
  • Tài liệu này nên trình bày ngắn gọn cấu trúc cấp cao và những nội dung không thay đổi thường xuyên; thay vì liên tục đồng bộ với mã, cách phù hợp hơn là rà soát vài lần mỗi năm
  • Thành phần cốt lõi là phần tổng quan vấn đề và bản đồ mã (codemap), cho thấy các mô-đun lớn và mối quan hệ giữa chúng để trả lời câu hỏi “mã phụ trách X nằm ở đâu”
  • Nên ghi lại các tên quan trọng, các bất biến kiến trúc, ranh giới giữa các tầng/hệ thống và các mối quan tâm xuyên suốt; đồng thời khuyến khích tìm kiếm theo tên thay vì liên kết trực tiếp để giảm gánh nặng bảo trì

Chi phí mà tài liệu ARCHITECTURE giúp giảm bớt

  • Trong các dự án mã nguồn mở, khác biệt lớn nhất giữa người thỉnh thoảng đóng góp và nhà phát triển cốt lõi là họ có biết kiến trúc vật lý của dự án hay không
  • Với một codebase xa lạ, người ta thường phải đọc tuần tự các tệp như những mảnh logic được đặt theo thứ tự ngẫu nhiên
  • Nhà phát triển đã từng đóng góp có ý nghĩa thì trong đầu đã có bản đồ mã, nên có thể đi thẳng tới vị trí cần thiết; nếu chưa có thì họ thậm chí còn có thể di chuyển đoạn mã đó sang nơi phù hợp
  • Tệp ARCHITECTURE là một cách chi phí thấp để thu hẹp khoảng cách này
  • Tài liệu nên ngắn gọn
    • Vì tất cả những người đóng góp lặp lại đều phải đọc nó
    • Càng ngắn thì càng ít khả năng bị các thay đổi trong tương lai làm cho lỗi thời
  • Tiêu chí cho ARCHITECTURE là chứa những nội dung không thay đổi thường xuyên
    • Không cố gắng liên tục đồng bộ với mã
    • Thay vào đó, xem lại vài lần mỗi năm

Nên chứa những gì

  • Trước hết, hãy tóm tắt vấn đề mà dự án giải quyết ở mức toàn cảnh
  • Sau đó viết một bản đồ mã (codemap) với mức chi tiết vừa phải
    • Mô tả các mô-đun lớn và mối quan hệ giữa chúng
    • Phải trả lời được câu hỏi “thứ làm X nằm ở đâu”
    • Đồng thời cũng phải trả lời “thứ tôi đang nhìn này làm gì”
  • Không đi quá sâu vào cách vận hành bên trong của từng mô-đun
    • Những nội dung đó nên chuyển sang tài liệu riêng, hoặc tốt hơn là tài liệu inline
    • Bản đồ mã là bản đồ quốc gia, không phải tập bản đồ chi tiết từng bang/tỉnh
  • Trong quá trình viết bản đồ mã, bạn cũng có thể rà soát lại cấu trúc dự án
    • Có thể kiểm tra xem những thứ nên nằm gần nhau trên bản đồ mã có thực sự nằm gần nhau trong kết quả chạy tree . hay không
  • Nên nêu rõ tên của các tệp, mô-đun, kiểu dữ liệu quan trọng
    • Tránh liên kết trực tiếp vì chúng có thể hỏng theo thời gian
    • Thay vào đó, hướng người đọc tìm kiếm biểu tượng theo tên sẽ giúp họ phát hiện cả những mục liên quan có tên tương tự mà không làm tăng gánh nặng bảo trì
  • Các bất biến kiến trúc cần được viết ra một cách tường minh
    • Nhiều bất biến quan trọng thường xuất hiện dưới dạng một thứ gì đó “không tồn tại”
    • Ví dụ, trong phát triển web, việc tầng model không phụ thuộc vào view có thể là điều khó nhận ra nếu chỉ đọc mã
  • Cũng cần chỉ ra ranh giới giữa các tầng và các hệ thống
    • Ranh giới ngụ ý thông tin về cách triển khai của hệ thống phía sau nó
    • Đồng thời ràng buộc mọi cách triển khai khả dĩ
    • Vì ranh giới tốt rất khó tìm ra ngẫu nhiên trong mã, nên việc tài liệu hóa là hữu ích
  • Sau bản đồ mã, hãy thêm một mục riêng cho các mối quan tâm xuyên suốt
  • Một ví dụ đáng tham khảo có thể xem trong architecture.md của rust-analyzer

1 bình luận

 
GN⁺ 2024-02-26
Ý kiến trên Hacker News
  • Tôi thích ý tưởng này, và cho rằng bất kể kích thước repository thế nào, phần mô tả kiến trúc cũng có chỗ trong README
    Ví dụ, vì nghĩ rằng việc mọi độc giả nhìn thấy và hiểu luồng làm việc là quan trọng, tôi đã cố ý đưa một sơ đồ tuần tự Mermaid[1] vào README chính[2]
    [1] https://mermaid.js.org/syntax/sequenceDiagram.html
    [2] https://github.com/hbcondo/revenut-app?tab=readme-ov-file#-w...

    • Sẽ khá tuyệt nếu có một công cụ tạo sơ đồ Mermaid bằng ngôn ngữ tự nhiên. Đáng để suy nghĩ
  • Nghe như một lời khuyên thực sự hay
    Tôi mong các công cụ trực quan hóa kiến trúc của hệ thống đang chạy trở nên tốt hơn. Thật lạ là đến giờ cách hiện đại nhất vẫn là đọc code hoặc xem file Markdown. Nếu may mắn thì có thể có một sơ đồ Mermaid
    Tôi muốn kiến trúc tự bộc lộ theo thời gian thực. Sẽ tốt nếu khả năng quan sát ở quy mô vĩ mô được tích hợp sẵn theo mặc định, và tôi nghĩ điều đó cũng giúp mọi người hiểu điện toán tốt hơn, đồng thời giúp nhân loại tự tăng cường năng lực của mình

    • Sẽ hay nếu gắn tìm kiếm từ khóa hoặc tìm kiếm vector bằng LLM vào các trực quan hóa như dep-tree. Khi truy vấn, các file và cụm liên quan sẽ được làm nổi bật
  • Với các dự án mã nguồn mở có nhiều người đóng góp tạm thời, đây có vẻ là một mô hình ít cần bảo trì khá tốt. Nếu dự án có kỹ sư chuyên trách, ADR cũng đáng cân nhắc
    ADR cần bảo trì nhiều hơn, nhưng vì nó lưu lại “vì sao đã làm như vậy” và “những phương án thay thế đã xem xét”, nên rất hữu ích khi thiết kế lại
    Tham khảo: https://adr.github.io/

    • Ở đây có lẽ nói “cùng với” đúng hơn là “thay vì”
      ARCHITECTURE.md chứa trạng thái kiến trúc hiện tại, còn ADR là bản ghi các quyết định đã dẫn đến trạng thái đó. Cả hai đều rất hữu ích
    • Ở công ty chúng tôi cũng có những tài liệu vô dụng do các kiến trúc sư viết, phần lớn theo kiểu này
      Microservice, Kafka, Kubernetes: vì hiện giờ chỉ có 4 nghìn người dùng, nhưng lỡ sau này thành 1 tỷ thì sao
      GraphDB: vì lỡ SQL không đủ thì sao
      ElasticSearch: vì lỡ phải làm cả tìm kiếm toàn văn cùng với thống kê thì sao
      Nhưng phần lớn các tài liệu như vậy rốt cuộc chỉ là cách nói ngắn gọn của “tôi muốn thử kiến trúc/công nghệ mới vì nó thú vị, đồng nghiệp làm ở FAANG dùng nó, tôi đã đọc cuốn sách đó, nó trông đẹp trên CV”
      Sau khi họ chuyển sang thiết kế dự án lớn tiếp theo, đội chúng tôi phải tiếp tục giữ đồng bộ số dịch vụ nhiều hơn cả số thành viên trong đội, cùng các DB và công nghệ nói trên. Tất nhiên, những vấn đề kiểu này được xem là đơn giản hơn rất nhiều so với việc đưa ra “quyết định kiến trúc lớn”
  • Chợt nghĩ ra rằng mọi IDE tôi từng dùng đều hiển thị cấu trúc thư mục của dự án ở bên trái dưới dạng cây thư mục tiêu chuẩn. Có IDE nào cho phép khám phá dự án dưới dạng đồ thị phụ thuộc không?

    • Không phải câu trả lời trực tiếp cho câu hỏi, nhưng rốt cuộc có vẻ là muốn “trực quan hóa cấu trúc file tốt hơn”
      Tôi nghĩ cách biểu diễn kiểu mục lục không thật phù hợp. Trong workflow lập trình hiện nay, tôi mở terminal, chạy ranger trong đó, rồi chuyển tab sang terminal đó khi cần duyệt cấu trúc thư mục trái/phải. Kiểu như mở VSCode, trong terminal chia đôi thì phía trên là terminal bình thường, phía dưới chạy Ranger. Midnight Commander cũng được, thực ra chỉ cần một trình duyệt file TUI là đủ
      Tôi cũng đã bắt đầu đưa bản đồ code vào file architecture.md của dự án. Chạy tree -L sẽ cho sơ đồ cây cấu trúc file ở độ sâu mong muốn, rất tiện để đưa vào Markdown. Tôi thêm output đó vào Markdown, rồi sau mỗi file/thư mục gắn một chú thích dưới 10 từ để mô tả mục đích
      Ranger - https://github.com/ranger/ranger
      Midnight Commander - https://midnight-commander.org/
    • Tôi cũng đã nghĩ đúng y như vậy
      Tôi có hai ý tưởng về việc nó có thể trông như thế nào
      Thứ nhất là nhiều cây thư mục tổ chức file theo các chiều trực giao bằng symbolic link. Cấu trúc thư mục thông thường chia thành client/server, nhưng nếu muốn chia theo tính năng thì sao? IDE có thể làm việc đó dễ hơn nhiều
      Thứ hai, theo hướng của bài viết này, tôi muốn IDE cho phép tạo bookmark và di chuyển qua lại giữa chúng để dễ giải thích code cho người khác. Thỉnh thoảng tôi muốn để lại chú thích rồi bấm vào để nhảy sang một vị trí khác trong codebase. Nếu nối những thứ đó lại, ta có thể tạo thành một câu chuyện giải thích cách hoạt động trên toàn bộ codebase
      Tôi tò mò liệu có ai đang làm theo hướng này không
    • Trên thực tế nó sẽ trông như thế nào? Ví dụ, có thể xử lý phụ thuộc vòng ra sao?
    • Có lẽ thứ bạn muốn là multitree
  • Tôi nghĩ nên cẩn trọng khi mở rộng quá mức điều tác giả nói ở đây sang các dự án phần mềm nói chung
    Với các dự án mã nguồn mở lớn có nhiều người đóng góp thiếu ngữ cảnh, việc duy trì tài liệu kiểu này rất đáng giá. Nhưng trong các dự án công việc nhỏ, tôi đã thấy tài liệu do developer commit cuối cùng đều rơi vào trạng thái không được bảo trì

    • Tôi đã làm vài phiên kiến trúc với các đội, và lần nào cũng có giá trị
      Ít nhất, chúng tôi nhận ra rằng các thành viên trong đội có những suy nghĩ rất khác nhau về kiến trúc hiện tại và kiến trúc lý tưởng. Chỉ riêng việc làm rõ điều đó cũng đã đủ đáng để tạo tài liệu
      Và “tài liệu không được bảo trì” là một lý do rất yếu để không viết tài liệu. Bởi vì bất kỳ tài liệu nào, dù đã cũ hoặc sai lệch đôi chút, vẫn tốt hơn là “không có tài liệu”
  • Vài năm trước, tôi từng thử nghiệm một cách tương tự trong một dự án phụ lớn
    https://github.com/shipmight/shipmight/blob/master/src/ARCHI...
    Ở đầu mỗi tệp, tôi đặt một cây liên kết tới các tệp ARCHITECTURE.md khác trong kho. Ví dụ như sau: ARCHITECTURE.md <- vị trí hiện tại, backend/ARCHITECTURE.md, backend/api/ARCHITECTURE.md, backend/cli/ARCHITECTURE.md, backend/ui/ARCHITECTURE.md, backend/utils/ARCHITECTURE.md, frontend/ARCHITECTURE.md, internal-charts/ARCHITECTURE.md

    • Nếu đặt README.md trong từng package/module/thư mục cấp cao nhất, GitHub UI sẽ render nó bên dưới danh sách tệp
      Nó sẽ hiển thị ở những vị trí như thế này: https://github.com/shipmight/shipmight/tree/master/src/backe...
      Tôi cũng từng thấy cách này trong một vài dự án
  • Càng ngắn thì càng ít có khả năng bị vô hiệu hóa bởi các thay đổi trong tương lai. Quy tắc kinh nghiệm cốt lõi của ARCHITECTURE là chỉ viết những nội dung không thay đổi thường xuyên. Đừng cố đồng bộ nó với code
    Interface ít có khả năng thay đổi hơn, và cũng khó thay đổi hơn. Đây là góc nhìn của Parnas về tiêu chí dùng để phân rã hệ thống thành các module
    Tôi đồng ý rằng việc hiểu codebase là khó. Việc đặt tên cho các “pattern” cũng giúp phần nào, nhưng rốt cuộc vẫn phải đọc khá nhiều
    Trên GitHub, commit message theo từng tệp thường có cảm giác giống như phần giải thích. Liệu cái đó có thể hữu ích hơn không?

  • Trong mọi dự án tôi từng tham gia, khi onboarding tôi đều được xem sơ đồ kiến trúc và phần mô tả ngắn về các thành phần
    Vì vậy tôi ngạc nhiên khi điều này lại không phổ biến như thế trong mã nguồn mở

    • “Mô tả thành phần” chính là vấn đề. Vì phải có ai đó làm việc đó
      Dự án mã nguồn mở không có ngày đi làm đầu tiên của nhân viên, nên cũng không có phần giới thiệu kiểu này
      Nếu không có rất nhiều thời gian, những phần giải thích như vậy thường không tốt lắm. Người ta kỳ vọng nhân viên sẽ mất vài tuần để tự làm được việc gì đó, còn chúng tôi là tư vấn bảo mật thì cứ mỗi 2 tuần lại nghe một phần giải thích hoàn toàn mới. Vấn đề là những phần giải thích này mang tính ứng biến, không có cấu trúc, và người nói vì lời nguyền tri thức nên thường nói rất nhiều chi tiết không liên quan
      Có lẽ tốt nhất là một người mới đến kho lưu trữ viết nó một lần, rồi sau đó chỉ cần bảo trì. Phương án kém hơn nhưng vẫn tốt là thay vì để bất kỳ ai ứng biến giải thích mỗi lần, hãy dành khoảng 1 phút nghĩ xem nên đưa gì vào và bỏ gì ra rồi viết lại. Như tác giả đã nói, tệp này nên mô tả kiến trúc cấp cao của dự án và nên ngắn gọn. Mọi người đóng góp lặp lại đều nên đọc nó, và càng ngắn thì càng ít có khả năng bị vô hiệu hóa bởi các thay đổi sau này
  • Tôi luôn cảm thấy đây là một thực hành rất hữu ích. Nhiều dự án có một vài tệp cốt lõi, hoặc package/module, nơi phần lớn thay đổi diễn ra
    Nếu người đóng góp mới hoặc người quay lại sau một thời gian dài có thể nhanh chóng nắm được chúng, thời gian khởi động dự án sẽ giảm đáng kể
    Tôi đã thêm tệp kiến trúc vào các dự án ở nhiều nơi làm việc [0], [1] và phản hồi khá tốt. Không hoàn hảo, nhưng có vẫn hơn không
    [0]: https://github.com/zapier/zapier-platform/pull/324
    [1]: https://github.com/stripe/stripe-cli/blob/master/ARCHITECTUR...

    • Có cách nào xem tự động những thứ như vậy trên GitHub không? Ví dụ như heatmap thay đổi tệp chẳng hạn
  • Trước đây tôi thích các chuẩn nhỏ kiểu documentation/diagram-as-code như thế này
    README-driven development, ARCHITECTURE.md, ADR, arc42, C4, v.v.
    Giờ thì tôi chỉ đặt một vault Obsidian trong thư mục /docs của git repo
    Thay vì dùng chuẩn của người khác, tôi liên tục sắp xếp và refactor tài liệu giống như quản lý ghi chú cá nhân trong Obsidian
    Ban đầu tôi muốn dùng một tập con Markdown chung hoạt động được cả với GFM của GitHub lẫn Obsidian, nhưng đã bỏ cuộc; giờ tôi dùng nguyên Markdown kiểu Obsidian, kể cả các tính năng riêng như plugin Dataview và template
    Mermaid và LaTeX được tích hợp sẵn trong Obsidian, và cũng có plugin PlantUML. Với hình vẽ/sơ đồ trực quan, tôi dùng Canvas tích hợp sẵn, DrawIO, Excalidraw