Làm mới cấu trúc Công cụ cho nhà phát triển: di chuyển Công cụ cho nhà phát triển sang TypeScript

Tim van der Lippe
Tim van der Lippe

Bài đăng này nằm trong loạt bài đăng trên blog mô tả những thay đổi mà chúng tôi đang thực hiện đối với cấu trúc Công cụ cho nhà phát triển và cách xây dựng công cụ này.

Tiếp nối quá trình di chuyển sang mô-đun JavaScriptdi chuyển sang Thành phần web, hôm nay chúng tôi tiếp tục loạt bài đăng trên blog về những thay đổi đang được thực hiện đối với kiến trúc của Devtools và cách xây dựng kiến trúc. (Nếu bạn chưa xem thì chúng tôi đã đăng một video về công việc Nâng cấp kiến trúc Công cụ cho nhà phát triển lên web hiện đại, trong đó có 14 mẹo về cách cải tiến các dự án web của bạn.)

Trong bài đăng này, chúng tôi sẽ mô tả hành trình 13 tháng của mình từ trình kiểm tra loại Trình biên dịch đóng sang TypeScript.

Giới thiệu

Do kích thước của cơ sở mã Công cụ cho nhà phát triển và nhu cầu mang lại sự tin cậy cho các kỹ sư đang xây dựng công cụ này, việc sử dụng công cụ kiểm tra kiểu là cần thiết. Để đạt được mục tiêu đó, DevTools đã sử dụng Trình biên dịch đóng cửa vào năm 2013. Sử dụng các kỹ sư Hỗ trợ công cụ cho nhà phát triển để tự tin thực hiện thay đổi; trình biên dịch Phương thức đóng sẽ thực hiện các hoạt động kiểm tra kiểu nhằm đảm bảo rằng tất cả hoạt động tích hợp hệ thống đều được định kiểu chính xác.

Tuy nhiên, theo thời gian, công cụ kiểm tra kiểu thay thế đã trở nên phổ biến trong quá trình phát triển web hiện đại. Hai ví dụ đáng chú ý là TypeScriptFlow. Hơn nữa, TypeScript trở thành ngôn ngữ lập trình chính thức tại Google. Mặc dù những công cụ kiểm tra kiểu mới này ngày càng phổ biến, nhưng chúng tôi cũng nhận thấy rằng mình đã gửi hồi quy mà trình kiểm tra kiểu đáng ra phải phát hiện được. Do đó, chúng tôi quyết định đánh giá lại sự lựa chọn về trình kiểm tra loại và tìm ra các bước tiếp theo cho hoạt động phát triển trên Công cụ cho nhà phát triển.

Đánh giá trình kiểm tra loại

Vì Công cụ cho nhà phát triển đã sử dụng công cụ kiểm tra kiểu nên câu hỏi chúng tôi cần trả lời là:

Chúng ta sẽ tiếp tục sử dụng Trình biên dịch đóng cửa hay chuyển sang một trình kiểm tra kiểu mới?

Để trả lời câu hỏi này, chúng tôi phải đánh giá trình kiểm tra loại dựa trên một số đặc điểm. Vì việc sử dụng công cụ kiểm tra kiểu tập trung vào sự tự tin của các kỹ sư, nên khía cạnh quan trọng nhất đối với chúng tôi là tính đúng kiểu. Nói cách khác: Công cụ kiểm tra loại có đáng tin cậy trong việc phát hiện vấn đề thực sự không?

Đánh giá của chúng tôi tập trung vào các lần hồi quy mà chúng tôi đã chuyển và xác định nguyên nhân gốc rễ của các lần hồi quy đó. Giả định ở đây là vì chúng ta đã sử dụng dưới cùng (Trình biên dịch đóng) nên wrap (Trình biên dịch đóng) sẽ không phát hiện được các vấn đề này. Do đó, chúng ta sẽ phải xác định xem liệu có trình kiểm tra loại nào khác không.

Độ chính xác của kiểu trong TypeScript

Vì TypeScript là ngôn ngữ lập trình được hỗ trợ chính thức tại Google và nhanh chóng trở nên phổ biến, nên chúng tôi quyết định đánh giá TypeScript trước tiên. TypeScript là một lựa chọn thú vị, vì chính nhóm TypeScript sử dụng Công cụ cho nhà phát triển làm một trong những dự án thử nghiệm của họ để theo dõi khả năng tương thích với tính năng kiểm tra loại JavaScript trên đó. Kết quả kiểm thử tham chiếu cơ sở của họ cho thấy TypeScript đang phát hiện một lượng lớn vấn đề về loại – những vấn đề mà trình biên dịch Đóng chưa chắc đã phát hiện được. Nhiều vấn đề trong số này có thể là nguyên nhân gốc của các lần hồi quy mà chúng tôi đã vận chuyển; đổi lại, điều này khiến chúng tôi tin rằng TypeScript có thể là một lựa chọn khả thi cho Công cụ cho nhà phát triển.

Trong quá trình di chuyển sang mô-đun JavaScript, chúng tôi phát hiện ra rằng Trình biên dịch đóng cửa đã phát hiện ra nhiều vấn đề hơn trước đây. Việc chuyển sang định dạng mô-đun tiêu chuẩn đã giúp mở rộng khả năng hiểu cơ sở mã của TẮT, từ đó nâng cao hiệu quả của trình kiểm tra kiểu. Tuy nhiên, nhóm TypeScript đang sử dụng phiên bản cơ sở của Công cụ cho nhà phát triển có trước quá trình di chuyển mô-đun JavaScript. Do đó, chúng tôi phải tìm hiểu xem việc di chuyển sang các mô-đun JavaScript có làm giảm số lượng lỗi mà trình biên dịch TypeScript phát hiện được hay không.

Đánh giá TypeScript

Công cụ cho nhà phát triển đã tồn tại hơn thập kỷ, trong đó Công cụ cho nhà phát triển đã phát triển thành một ứng dụng web có quy mô đáng kể và nhiều tính năng. Tại thời điểm viết bài đăng trên blog này, Công cụ cho nhà phát triển chứa khoảng 150.000 dòng mã JavaScript của bên thứ nhất. Khi chúng tôi chạy trình biên dịch TypeScript trên mã nguồn, số lượng lỗi rất lớn. Chúng tôi có thể tìm ra rằng trong khi trình biên dịch TypeScript phát ra ít lỗi hơn liên quan đến độ phân giải mã (~ 2.000 lỗi), thì vẫn còn 6.000 lỗi khác trong cơ sở mã của chúng tôi liên quan đến khả năng tương thích kiểu.

Điều này cho thấy mặc dù TypeScript có thể hiểu cách giải quyết các loại, nhưng lại phát hiện thấy một lượng không tương thích đáng kể về loại trong cơ sở mã của chúng tôi. Một phân tích thủ công về các lỗi này đã cho thấy rằng TypeScript (trong hầu hết các trường hợp) là chính xác. Lý do TypeScript có thể phát hiện các kiểu này và Đóng là không phải vì thường xuyên, trình biên dịch Đóng sẽ suy ra một kiểu là Any, trong khi TypeScript thực hiện suy luận kiểu dựa trên các phép gán và suy ra một kiểu chính xác hơn. Do đó, TypeScript thực sự hiểu cấu trúc của các đối tượng tốt hơn và đã phát hiện ra cách sử dụng có vấn đề.

Một điểm quan trọng cần lưu ý là việc sử dụng trình biên dịch Đóng trong Công cụ cho nhà phát triển bao gồm cả việc sử dụng @unrestricted thường xuyên. Việc chú thích một lớp bằng @unrestricted sẽ tắt hiệu quả các hoạt động kiểm tra thuộc tính nghiêm ngặt của trình biên dịch đóng cho lớp cụ thể đó. Tức là nhà phát triển có thể tuỳ ý bổ sung định nghĩa lớp mà không cần đảm bảo an toàn về kiểu. Chúng tôi không tìm thấy bối cảnh trước đây nào về lý do việc sử dụng @unrestricted trở nên phổ biến trong cơ sở mã của Công cụ cho nhà phát triển, nhưng điều này đã dẫn đến việc chạy trình biên dịch đóng ở chế độ hoạt động kém an toàn hơn đối với phần lớn cơ sở mã.

Một phân tích chéo về các lần hồi quy của chúng tôi với các lỗi loại mà TypeScript phát hiện được cũng cho thấy sự trùng lặp, khiến chúng tôi tin rằng TypeScript có thể đã ngăn chặn các vấn đề này (miễn là các loại đó chính xác).

Đang gọi điện cho any

Tại thời điểm này, chúng tôi phải quyết định giữa việc cải thiện việc sử dụng Trình biên dịch đóng hoặc chuyển sang TypeScript. (Vì Flow không được hỗ trợ tại Google hay trong Chromium, nên chúng tôi phải bỏ qua tuỳ chọn đó.) Dựa trên các cuộc thảo luận và đề xuất của các kỹ sư của Google đang làm việc về công cụ JavaScript/TypeScript, chúng tôi đã chọn chọn trình biên dịch TypeScript. (Gần đây, chúng tôi cũng đã xuất bản một bài đăng trên blog về di chuyển Puppeteer sang TypeScript).

Lý do chính khiến trình biên dịch TypeScript là cải thiện độ chính xác về kiểu, trong khi các ưu điểm khác là do các nhóm TypeScript hỗ trợ trong nội bộ Google và các tính năng của ngôn ngữ TypeScript, chẳng hạn như interfaces (trái ngược với typedefs trong JSDoc).

Việc chọn trình biên dịch TypeScript có nghĩa là chúng tôi đã phải đầu tư đáng kể vào cơ sở mã Công cụ cho nhà phát triển và kiến trúc nội bộ của công cụ này. Do đó, chúng tôi ước tính rằng chúng tôi cần ít nhất một năm để chuyển sang TypeScript (nhắm mục tiêu vào Quý 3 năm 2020).

Thực hiện quá trình di chuyển

Câu hỏi lớn nhất còn lại: chúng ta sẽ di chuyển sang TypeScript như thế nào? Chúng tôi có 150.000 dòng mã và chúng tôi không thể di chuyển mã đó trong một lần. Chúng tôi cũng biết rằng việc chạy TypeScript trên cơ sở mã của mình sẽ phát hiện ra hàng nghìn lỗi.

Chúng tôi đã đánh giá nhiều lựa chọn:

  1. Thu thập tất cả các lỗi TypeScript và so sánh với kết quả "vàng". Phương pháp này tương tự như phương pháp mà nhóm TypeScript đang áp dụng. Nhược điểm lớn nhất của phương pháp này là hay xảy ra xung đột hợp nhất, vì có nhiều kỹ sư làm việc trong cùng một cơ sở mã.
  2. Đặt mọi loại có vấn đề thành any. Điều này về cơ bản sẽ khiến TypeScript loại bỏ lỗi. Chúng tôi không chọn phương án này vì mục tiêu của chúng tôi khi di chuyển là tính đúng kiểu nên hoạt động chặn sẽ làm giảm hiệu suất.
  3. Sửa tất cả các lỗi TypeScript theo cách thủ công. Việc này sẽ liên quan đến việc sửa hàng nghìn lỗi, tốn nhiều thời gian.

Mặc dù dự kiến sẽ rất nỗ lực, nhưng chúng tôi đã chọn phương án 3. Có thêm lý do khiến chúng tôi chọn tùy chọn này: chẳng hạn như tùy chọn này cho phép chúng tôi kiểm tra tất cả mã và đánh giá tất cả chức năng mỗi thập kỷ một lần, bao gồm cả việc triển khai chức năng đó. Từ góc độ kinh doanh, chúng tôi không mang lại giá trị mới, mà thay vào đó là duy trì hiện trạng. Điều này gây khó khăn hơn để chứng minh phương án 3 là phương án chính xác.

Tuy nhiên, bằng cách sử dụng TypeScript, chúng tôi thực sự tin rằng mình có thể ngăn chặn các vấn đề trong tương lai, đặc biệt là liên quan đến sự hồi quy. Do đó, đối số ít đưa ra là "chúng tôi đang bổ sung giá trị kinh doanh mới" và đặt ra nhiều yếu tố hơn là "chúng tôi đảm bảo không mất đi giá trị kinh doanh thu được".

Hỗ trợ JavaScript của trình biên dịch TypeScript

Sau khi đảm bảo có được sự tham gia và phát triển kế hoạch chạy cả trình biên dịch đóng và TypeScript trên cùng một mã JavaScript, chúng tôi đã bắt đầu với một số tệp nhỏ. Phương pháp của chúng tôi chủ yếu là từ dưới lên: bắt đầu với mã cốt lõi và di chuyển theo từng phần cấu trúc cho đến khi tiếp cận được bảng điều khiển cấp cao.

Chúng tôi đã có thể tải song song công việc của mình bằng cách thêm trước @ts-nocheck vào từng tệp trong Công cụ cho nhà phát triển. Quá trình "sửa lỗi TypeScript" là xoá chú thích @ts-nocheck và giải quyết mọi lỗi mà TypeScript sẽ tìm thấy. Điều này có nghĩa là chúng tôi tự tin rằng mỗi tệp đều đã được kiểm tra và nhiều vấn đề về loại tệp nhất có thể đã được giải quyết.

Nhìn chung, phương pháp này đã khắc phục được một số vấn đề. Chúng tôi đã gặp một số lỗi trong trình biên dịch TypeScript, nhưng hầu hết các lỗi này đều không rõ ràng:

  1. Tham số không bắt buộc có loại hàm trả về any được coi là bắt buộc: #38551
  2. Việc chỉ định thuộc tính cho phương thức tĩnh của lớp khai báo phá vỡ nội dung khai báo: #38553
  3. Phần khai báo lớp con có hàm khởi tạo không có đối số và lớp cấp cao có hàm khởi tạo đối số sẽ bỏ qua hàm khởi tạo con: #41397

Những lỗi này nêu bật điều đó, trong trường hợp 99%, trình biên dịch TypeScript là nền tảng vững chắc để xây dựng. Có, những lỗi chưa rõ ràng này đôi khi sẽ gây ra sự cố cho Công cụ cho nhà phát triển, nhưng trong hầu hết trường hợp, chúng đều đủ che khuất để chúng tôi có thể dễ dàng giải quyết chúng.

Vấn đề duy nhất gây ra một số nhầm lẫn là kết quả không xác định của các tệp .tsbuildinfo: #37156. Tại Chromium, chúng tôi yêu cầu hai bản dựng bất kỳ của cùng một Chromium xác nhận đều phải có cùng một kết quả đầu ra. Rất tiếc, các kỹ sư xây dựng Chromium của chúng tôi phát hiện ra rằng kết quả .tsbuildinfo không xác định: crbug.com/1054494. Để giải quyết vấn đề này, chúng tôi đã phải vá tệp .tsbuildinfo (về cơ bản là chứa JSON) rồi xử lý hậu kỳ để trả về một kết quả xác định: https://crrev.com/c/2091448 Thật may là nhóm TypeScript đã giải quyết được vấn đề ngược dòng và chúng tôi đã sớm có thể gỡ bỏ giải pháp của mình. Cảm ơn nhóm TypeScript đã tiếp thu các báo cáo lỗi và nhanh chóng khắc phục các sự cố này!

Nhìn chung, chúng tôi hài lòng với tính chính xác (type) của trình biên dịch TypeScript. Chúng tôi hy vọng Devtools dưới dạng một dự án JavaScript nguồn mở lớn đã giúp củng cố khả năng hỗ trợ JavaScript trong TypeScript.

Phân tích hậu quả

Chúng tôi đã có tiến bộ tốt trong việc giải quyết các lỗi loại này và tăng dần số lượng mã được TypeScript kiểm tra. Tuy nhiên, vào tháng 8 năm 2020 (9 tháng sau khi di chuyển này), chúng tôi đã kiểm tra và phát hiện ra rằng chúng tôi sẽ không đạt đến thời hạn với tiến độ hiện tại. Một trong các kỹ sư của chúng tôi đã xây dựng biểu đồ phân tích để hiển thị tiến trình của "TypeScriptification" (tên mà chúng tôi đặt cho quá trình di chuyển này).

Tiến trình Di chuyển TypeScript

Tiến trình di chuyển TypeScript – Theo dõi các dòng mã còn lại cần di chuyển

Các ước tính khi chúng tôi đạt đến 0 dòng còn lại dao động từ tháng 7 năm 2021 đến tháng 12 năm 2021, gần một năm sau thời hạn của chúng tôi. Sau khi thảo luận với ban quản lý và các kỹ sư khác, chúng tôi đã đồng ý tăng số lượng kỹ sư phụ trách việc chuyển sang dùng tính năng hỗ trợ trình biên dịch TypeScript. Điều này có thể xảy ra vì chúng tôi thiết kế quá trình di chuyển có thể thực hiện song song để nhiều kỹ sư làm việc trên nhiều tệp khác nhau không xung đột với nhau.

Tại thời điểm này, quá trình TypeScriptification trở thành nỗ lực của toàn bộ nhóm. Với sự trợ giúp thêm, chúng tôi đã có thể hoàn tất quá trình di chuyển vào cuối tháng 11 năm 2020, 13 tháng sau khi bắt đầu và hơn một năm trước khi dự kiến ban đầu.

Tổng cộng có 771 danh sách thay đổi (tương tự như Yêu cầu lấy dữ liệu) do 18 kỹ sư gửi. Lỗi theo dõi của chúng tôi (https://crbug.com/1011811) có hơn 1200 bình luận (hầu hết tất cả đều là bài đăng tự động từ danh sách thay đổi). Trang tính theo dõi của chúng tôi có hơn 500 hàng cho tất cả các tệp cần nhập, người được giao và trong danh sách thay đổi chứa các tệp này.

Giảm thiểu tác động đến hiệu suất của trình biên dịch TypeScript

Vấn đề lớn nhất mà chúng tôi đang giải quyết hiện nay là hiệu suất chậm của trình biên dịch TypeScript. Dựa trên số lượng kỹ sư xây dựng Chromium và Công cụ cho nhà phát triển, điểm tắc nghẽn này rất tốn kém. Đáng buồn thay, chúng tôi không thể xác định rủi ro này trước quá trình di chuyển và chỉ tại thời điểm di chuyển phần lớn các tệp sang TypeScript, chúng tôi mới phát hiện ra sự gia tăng đáng kể về thời gian sử dụng trên các bản dựng Chromium: https://crbug.com/1139220

Chúng tôi đã báo cáo vấn đề này ngược dòng đến nhóm biên dịch Microsoft TypeScript, nhưng đáng tiếc là họ xác định hành vi này là có chủ đích. Chúng tôi hy vọng họ sẽ xem xét lại vấn đề này. Tuy nhiên, trong thời gian chờ đợi, chúng tôi đang nỗ lực giảm thiểu tác động nhiều nhất có thể đến hiệu suất chậm đối với Chromium.

Tiếc là các giải pháp mà chúng tôi cung cấp hiện nay không phải lúc nào cũng phù hợp với những cộng tác viên không phải nhân viên của Google. Vì đóng góp nguồn mở cho Chromium rất quan trọng (đặc biệt là những đóng góp của nhóm Microsoft Edge), chúng tôi đang tích cực tìm kiếm các giải pháp thay thế phù hợp với tất cả các cộng tác viên. Tuy nhiên, hiện tại chúng tôi chưa tìm ra giải pháp thay thế phù hợp.

Trạng thái hiện tại của TypeScript trong Công cụ cho nhà phát triển

Hiện tại, chúng tôi đã xoá trình kiểm tra loại trình biên dịch đóng khỏi cơ sở mã và chỉ dựa vào trình biên dịch TypeScript. Chúng tôi có thể viết các tệp do TypeScript tạo và sử dụng các tính năng dành riêng cho TypeScript (chẳng hạn như giao diện, tham số chung, v.v.) để hỗ trợ chúng tôi hằng ngày. Chúng tôi tin tưởng hơn rằng trình biên dịch TypeScript sẽ phát hiện lỗi và hồi quy kiểu. Đây là những gì chúng tôi hy vọng sẽ xảy ra khi lần đầu bắt đầu thực hiện quá trình di chuyển này. Quá trình di chuyển này, giống như rất nhiều người khác, diễn ra chậm, nhiều sắc thái và thường đầy thách thức, nhưng khi chúng tôi mang lại các lợi ích, chúng tôi tin rằng nó xứng đáng.

Tải kênh xem trước xuống

Hãy cân nhắc sử dụng Chrome Canary, Dev hoặc Beta làm trình duyệt phát triển mặc định. Các kênh xem trước này cung cấp cho bạn quyền truy cập vào các tính năng mới nhất của Công cụ cho nhà phát triển, thử nghiệm API nền tảng web tiên tiến và tìm ra sự cố trên trang web của bạn trước khi người dùng của bạn làm điều đó!

Liên hệ với nhóm Công cụ của Chrome cho nhà phát triển

Sử dụng các lựa chọn sau đây để thảo luận về các tính năng mới và thay đổi trong bài đăng hoặc bất cứ vấn đề nào khác liên quan đến Công cụ cho nhà phát triển.

  • Hãy gửi đề xuất hoặc phản hồi cho chúng tôi qua crbug.com.
  • Báo cáo sự cố của Công cụ cho nhà phát triển bằng cách sử dụng phần Tuỳ chọn khác   Thêm   > Trợ giúp > Báo cáo sự cố về Công cụ cho nhà phát triển trong Công cụ cho nhà phát triển.
  • Tweet tại @ChromeDevTools.
  • Hãy để lại bình luận về tính năng mới trong video trên YouTube của Công cụ cho nhà phát triển hoặc video trên YouTube.