Building a Dependency Graph of Our Python Codebase

Bài tóm tắt Hudson River Trading 2023 4. Điều chỉnh Độ khó: Trung bình

Hudson River Trading · 2023 · 4. Điều chỉnh · Độ khó: Trung bình — bài gốc

Bối cảnh

  • Trong khoảng năm năm, codebase Python hướng nghiên cứu của HRT lớn theo cấp số nhân lên hàng triệu dòng: văn hoá "đủ tốt hơn hoàn hảo", chia sẻ code giữa team, giai đoạn tăng trưởng nhanh.
  • Kết quả là code "tangle" (mượn từ bài của Dropbox): đồ thị phụ thuộc có nhiều chu trình chồng nhau, phần không liên quan bị buộc vào nhau qua đường import gián tiếp. Import một module trong chu trình là import cả chu trình và phụ thuộc của nó.
  • Thời gian import tăng một bậc, có chương trình mất hơn 30 giây chỉ để import; lint và mypy chậm tới mức vô dụng; thay đổi đắt để test. Theo HRT, chuyện này nặng với họ hơn nơi khác vì process Python sống ngắn chiếm phần đáng kể workload tính toán.
  • Nguồn overhead: pkg_resources quét filesystem (trên NFS càng chậm), C extension lớn (pandas, NumPy, extension nội bộ), khởi tạo tĩnh lúc import (dò môi trường, đăng ký class/callback).

Thay đổi chính

  • Mục tiêu: kiến trúc phân tầng, tầng dưới không import tầng trên; chấp nhận chu trình nhỏ trong một (sub)package. Làm mà không dừng phát triển.
  • Tangle Tools dựng đồ thị phụ thuộc toàn codebase (node là module, cạnh là import) bằng ast song song qua concurrent.futures, lưu bằng networkx; có CLI và giao diện web.
  • Quy trình gỡ: tìm một phụ thuộc bắc cầu không mong muốn → tính mạng luồng giữa nguồn và đích, chỉ ra cạnh nào cắt thì giảm luồng → refactor tự động bằng libcst (ví dụ chuyển một symbol sang module mới và sửa mọi tham chiếu) → viết luật phụ thuộc cho module, kiểm trong CI để không tái phát.

Vì sao

  • Trước đó tìm phụ thuộc import là việc tay chậm, refactor là "đập chuột". Có đồ thị thì tìm được refactor hiệu quả nhắm vào gốc của tangle.

Bài học cho SWE

  • Coupling trong monorepo Python lộ ra thành chi phí runtime (import) chứ không chỉ chi phí bảo trì.
  • Gỡ một lần chưa đủ; luật phụ thuộc trong CI mới giữ được kết quả.
  • Thuật toán đồ thị (max-flow/min-cut) chỉ ra chỗ cắt ít tốn nhất.

Liên quan