Inside Figma: a case study on strict null checks

Bài tóm tắt Figma 2020 3. Quy mô lớn Độ khó: Trung bình

Figma · 2020 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc

Bối cảnh

  • Figma dùng TypeScript từ trước bản 2.0 (khi strictNullChecks ra đời); code mới vẫn không nhất thiết qua được cờ này. Bật cờ trên frontend khoảng 1.162 file sinh hơn 4.000 lỗi, sửa một lỗi thường lộ thêm lỗi khác.
  • Theo số liệu sự cố của Figma, một vài sự cố mức nghiêm trọng cao đã có thể bị chặn bởi strict null checks.
  • Codebase tăng từ 376 nghìn lên 464 nghìn dòng trong thời gian migration.

Thay đổi chính

  • Cân ba cách: dừng cả công ty (không song song hoá đủ, cản việc sản phẩm), đập chuột (sửa dần nhưng không bật cờ, nên code mới lại sinh lỗi, thanh tiến độ có lúc lùi), và allowlist tăng dần (chọn cách này, theo công cụ của đội VS Code).
  • Compile hai lần: toàn bộ không strict, và một tsconfig.strictNullChecks.json chỉ chứa các file đã qua. Một file chỉ vào allowlist khi mọi phụ thuộc của nó đã ở trong đó, vì compile một file kéo theo các file nó import. Script liệt kê ứng viên (file có đủ phụ thuộc đã strict), sắp theo số file phụ thuộc vào nó; công cụ vẽ đồ thị để lập kế hoạch và xem tiến độ. Công cụ được mở mã nguồn.
  • Vòng phụ thuộc coi là một nút (strongly connected component). Vòng lớn nhất hơn 500 file, gần nửa codebase; phá bằng cách sửa các mẫu gây vòng (model Redux nằm chung file với reducer, hằng số của modal nằm trong modal.ts), mỗi lần bớt hàng trăm file.
  • Nhóm nhỏ dọn đường (công cụ, phá vòng, bỏ barrel import), rồi hai sprint ba ngày mở cho mọi team; tổng 30 engineer tham gia. Chính sách: khuyến khích refactor để chứng minh nullability thay vì rải | null và !, nhưng cho phép đi tắt khi quá tốn; chấp nhận vài hồi quy vì người viết còn đó để sửa.
  • Khi phần lớn file đã strict, đổi allowlist thành denylist (exclude) để file mới mặc định strict; vài người làm nốt phần còn lại.

Vì sao

  • Theo Figma, lợi ích lớn nhất khó đo là code dễ đọc hơn: chỗ TypeScript khó suy nullability thường cũng là chỗ người khó hiểu. Lỗi null không còn trên dashboard lỗi.
  • Tình trạng nửa vời có giá (hai lần compile ăn CPU), nên mục tiêu là làm xong chứ không phải refactor hoàn hảo.

Bài học cho SWE

  • Migration theo đơn vị có phụ thuộc thì đi theo thứ tự của đồ thị phụ thuộc, ưu tiên nút có nhiều thứ đứng sau.
  • Allowlist khi phần đã chuyển còn nhỏ, denylist khi phần chưa chuyển còn nhỏ: điểm đổi chính là lúc mặc định cho code mới đổi theo.
  • Vòng phụ thuộc lớn là nợ kiến trúc lộ ra khi migrate; phá nó có lợi riêng.

Liên quan