Migrating millions of lines of code to TypeScript
Bài tóm tắt Stripe 2022 3. Quy mô lớn Độ khó: Trung bình
Stripe · 2022 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc
Đối chiếu với Yelp 2026: cùng bài toán Flow → TypeScript, Yelp đi từng package trong 3,5 năm.
Bối cảnh
- Stripe làm front-end lớn từ 2012; năm 2016 dùng sớm Flow (type checker của Meta). Engineer khổ với Flow: tốn bộ nhớ tới mức treo laptop, tích hợp editor chậm và không ổn định; TypeScript thành yêu cầu hàng đầu. Cuối 2020 lập đội JavaScript Infrastructure ngang hàng.
- Codebase lớn nhất là Dashboard: các thành phần gắn chặt, không có đồ thị phụ thuộc gọn. Chuyển dần nghĩa là developer phải làm việc với cả hai ngôn ngữ cho cùng một việc, cộng một lớp đồng bộ kiểu giữa hai bên.
Thay đổi chính
- Chọn chuyển một lần: học từ Airtable và Zapier (chạy script chuyển đổi trên toàn codebase, merge một commit), lấy codemod của Airtable làm nền.
- Chuẩn bị theo tầng: design system Sail (component React gõ kiểu chặt) được sinh định nghĩa TypeScript thay vì chuyển code, kèm test để định nghĩa không lệch với Flow; rồi thử trên vài ứng dụng nội bộ vài trăm module. Nhiều tháng sửa codemod cho hàng chục trường hợp cú pháp (ví dụ arrow function generic trả object literal bị mất ngoặc).
- Không sửa hết lỗi trước: lượt thứ hai của codemod chèn
@ts-expect-errorcho lỗi còn lại, ưu tiên bỏ Flow càng sớm càng tốt. Lần đầu trên Dashboard ra hơn 97.000 suppression, cải tiến codemod còn 37.000 (khoảng một trên nghìn dòng; Flow có khoảng 5.000). Type coverage của TypeScript vẫn cao hơn Flow. - Bộ nhớ của compiler: Dashboard hàng chục nghìn module làm TypeScript tốn bộ nhớ; suy ra cấu trúc module rồi tạo project references mà không phải refactor code.
- Ngày land: chọn Chủ nhật 6/3/2022, khoá monorepo, merge một PR hơn 3,7 triệu dòng. Tuần trước đó: cho build qua CI, deploy lên QA, sửa các công cụ khác cũng đọc source (ESLint, Jest, Webpack, Metro), chỉ sinh
.tsxđể sửa hàng loạt snapshot Jest; tắt tạm vài rule ESLint viết cho parser Flow. Đội sản phẩm test tay vì end-to-end test ít (bắt được lỗi bản dịch không tải do hard-code.js). Thứ Bảy chạy lại script trên nhánh mới, QA lại; Chủ nhật merge, deploy, chuyển traffic không có bất ngờ.
Kết quả (theo Stripe)
- Thứ Hai, hàng trăm engineer viết TypeScript; một người gọi đây là cú tăng năng suất lớn nhất thời gian họ ở Stripe. Sau đó vẫn có lỗi lặt vặt (khác biệt CI/local vì kiểu từ npm cập nhật). Cùng công cụ dùng để chuyển các codebase khác (Payments UI, Checkout); codemod được mở mã.
Vì sao
- Với codebase gắn chặt, thời kỳ hai hệ kiểu cùng tồn tại tốn hơn một lần chuyển lớn được chuẩn bị kỹ; chấp nhận suppression để tách "bỏ Flow" khỏi "sửa hết lỗi kiểu" (Quản lý fleet và migrate code hàng loạt, Đổi ngôn ngữ và runtime).
Bài học cho SWE
- Big-bang chỉ an toàn khi script tất định chạy lại được ngay trước giờ land, và giờ land được chọn để ít người commit nhất.
- Tách mục tiêu: chuyển công cụ trước, trả nợ suppression sau, và đo nợ đó.
- Đừng quên các công cụ khác đọc source (lint, test, bundler); chúng thường là phần ngốn lịch.
Liên quan
#Flow sang TypeScript#Codemod#Big-bang migration#Monorepo lock#ts-expect-error#Project references#Jest snapshot#Type coverage
Được nhắc tới trong
Công ty (1)