רענון הארכיטקטורה של כלי הפיתוח: העברת כלי הפיתוח ל-TypeScript

Tim van der Lippe
Tim van der Lippe

פוסט זה הוא חלק מסדרת פוסטים בבלוג המתארים את השינויים שאנחנו מבצעים בארכיטקטורה של כלי הפיתוח ואת אופן בנייתם.

בהמשך למעבר למודולים של JavaScript ולמעבר לרכיבי אינטרנט, היום אנחנו ממשיכים בסדרת הפוסטים בבלוג שלנו בנושא השינויים שאנחנו מבצעים בארכיטקטורה של Devtools ואופן הבנייה שלה. (אם עדיין לא ראיתם אותו, פרסמנו סרטון על שדרוג הארכיטקטורה של כלי הפיתוח לאינטרנט המודרני והכולל 14 טיפים לשיפור פרויקטים באינטרנט).

בפוסט הזה נתאר את המסע שלנו בן 13 החודשים, שבו נעבור מכלי לבדיקת סוג מהדר (Closure Compiler) ל-TypeScript.

מבוא

בגלל הגודל של בסיס הקוד של כלי הפיתוח והצורך לספק ביטחון למהנדסים שעובדים עליו, צריך להשתמש בבודק סוגים. לשם כך, DevTools אימץ את הכלי מהדרים של חסימות בשנת 2013. השימוש בסגירה אפשר למהנדסי DevTools לבצע שינויים בביטחון מלא. מהדר החסימות יבצע בדיקות סוגים כדי לוודא שכל שילובי המערכת הוזנו כראוי.

עם זאת, עם הזמן, הכלים לבדיקת סוגים אלטרנטיביים הפכו לפופולריים בפיתוח אתרים מודרניים. שתי דוגמאות בולטות הן TypeScript ו-Flow. בנוסף, TypeScript הפכה לשפת תכנות רשמית ב-Google. אמנם הפופולריות של בודקים חדשים אלה עלתה, אבל שמנו לב גם שיש רגרסיות של משלוחים שהיו אמורות להיתפס על ידי בודק טיפוסים. לכן, החלטנו לבחון מחדש את הכלי לבדיקת הסוגים ולחשוב על השלבים הבאים בפיתוח כלי הפיתוח.

הערכת בודקי סוגים

מכיוון שבכלי הפיתוח כבר נעשה שימוש בבודק סוגים, השאלה שעליה ענו לנו הייתה:

האם אנחנו ממשיכים להשתמש בכלי החסימות או עוברים לבודק סוגים חדש?

כדי לענות על שאלה זו, היה עלינו להעריך את סוג בודקי הסוגים על כמה מאפיינים. מכיוון שהשימוש שלנו בבודק סוגים מתמקד בביטחון של מהנדסים, ההיבט החשוב ביותר עבורנו הוא נכונות הסוג. במילים אחרות: מהי מידת האמינות של בודק סוגים על מנת לגלות בעיות אמיתיות?

ההערכה שלנו התמקדה ברגרסיות ששלחנו ובקביעת הגורמים שהובילו לכך. ההנחה כאן היא שמכיוון שהשתמשנו כבר במהדר החסימות, האפליקציה Closure לא תאתר את הבעיות האלה. לפיכך, נצטרך לקבוע אם כל בודק מסוג אחר היה יכול לעשות זאת.

נכונות ההקלדה ב-TypeScript

מאחר ש-TypeScript הייתה שפת תכנות שנתמכת באופן רשמי ב-Google והפופולריות שלה גדלה במהירות, החלטנו קודם להעריך את TypeScript. הבחירה ב-TypeScript הייתה מעניינת, כי הצוות של TypeScript משתמש ב-DevTools בתור אחד מפרויקטי הבדיקה שלהם כדי לעקוב אחר התאימות שלהם לתכונת בדיקת סוגי ה-JavaScript. פלט הבדיקה של קובץ העזר הבסיסי הראה ש-TypeScript זיהה כמות גדולה של בעיות מסוג מסוים – בעיות שמהדר של החסימות לא בהכרח זיהה. סביר להניח שהרבה מהבעיות האלה הן שורש הבעיה של הרגרסיה ששלחנו. כתוצאה מכך, יכולנו להאמין ש-TypeScript יכול להיות אפשרות מעשית עבור כלי הפיתוח.

במהלך המעבר למודולים של JavaScript, כבר גילינו ש'מהדר החסימות' חשף יותר בעיות מבעבר. המעבר לפורמט מודול סטנדרטי שיפר את היכולת של Closure להבין את בסיס הקוד שלנו, ולכן שיפרה את היעילות של בודקי סוגי הפריטים. עם זאת, צוות TypeScript השתמש בגרסת בסיס של DevTools שקדמה להעברת המודולים של JavaScript. לכן, היה עלינו להבין אם ההעברה למודולים של JavaScript הפחיתה גם את כמות השגיאות שמהדר של TypeScript יאתר.

הערכת TypeScript

כלי הפיתוח קיימים במשך יותר מעשור, ובמהלך עשורם הם התרחבו לאפליקציית אינטרנט גדולה במיוחד ועתירת תכונות. נכון למועד כתיבת הפוסט הזה בבלוג, כלי הפיתוח מכילים כ-150,000 שורות של קוד JavaScript של צד ראשון. כשהפעלנו את המהדר של TypeScript בקוד המקור שלנו, נפח השגיאות הגדול היה עצום. הצלחנו להבין שלמרות שמהדר של TypeScript פולט פחות שגיאות שקשורות לרזולוציית הקוד (כ-2,000 שגיאות), עדיין היו 6,000 שגיאות נוספות ב-codebase שלנו הקשורות לתאימות סוגים.

זה הראה שלמרות ש-TypeScript הצליח להבין כיצד לפתור סוגים, הוא מצא כמות משמעותית של אי התאמות של סוגים ב-codebase שלנו. ניתוח ידני של השגיאות האלה הראה ש-TypeScript (רוב הזמן) היה נכון. הסיבה לכך ש-TypeScript הצליח לזהות אותם, ו-Closure לא הייתה מבוססת על מקרים רבים, מהדר החסימות הסיק שסוג הוא Any, ואילו TypeScript היה מבצע הסקת סוג על סמך הקצאות ומסיק סוג מדויק יותר. לכן, TypeScript הצליח להבין טוב יותר את מבנה האובייקטים שלנו וגילה שימושים בעייתיים.

חשוב להבין זאת הוא שהשימוש של מהדר (compiler) בכלי הפיתוח נעשה שימוש תכוף ב-@unrestricted. הוספת הערות לכיתה עם @unrestricted משביתה בפועל את בדיקות המאפיינים המחמירות של מהדר החסימות עבור המחלקה הספציפית הזו. פירוש הדבר הוא שהמפתח יכול להרחיב הגדרת מחלקה לפי רצון ללא בטיחות סוג. לא הצלחנו למצוא הקשר היסטורי לגבי הסיבה לשימוש ב-@unrestricted ב-codebase של DevTools, אבל בעקבות זאת הרצת המהדר (compiler) לסגירה במצב פחות בטוח בחלקים גדולים מ-codebase.

ניתוח צולב של הרגרסיות שלנו עם שגיאות מסוג TypeScript שגילה גם הראה חפיפה, שגרמה לנו להאמין ש-TypeScript יכול למנוע את הבעיות האלה (בתנאי שהסוגים עצמם היו נכונים).

מתבצע התקשרות אל any

בשלב הזה היינו צריכים להחליט אם לשפר את השימוש שלנו ב-Close Compiler או לעבור ל-TypeScript. (מכיוון ש-Fflow לא נתמך ב-Google או ב-Chromium, נאלצנו לוותר על האפשרות הזו). על סמך דיונים עם מהנדסי Google שעבדו על הכלים של JavaScript/TypeScript, המלצות מאת מהנדסי Google, בחרנו בהדר של TypeScript. (כמו כן, לאחרונה פרסמנו פוסט בבלוג בנושא העברת Puppeteer ל-TypeScript.)

הסיבות העיקריות להדר את TypeScript היו תיקון ההקלדה המשופרת, ואילו יתרונות אחרים כללו תמיכה מצוותי TypeScript באופן פנימי ב-Google ותכונות של שפת TypeScript, כגון interfaces (בניגוד ל-typedefs ב-JSDoc).

בעקבות בחירת המהדר של TypeScript, היינו צריכים להשקיע באופן משמעותי ב-codebase של DevTools ובארכיטקטורה הפנימית שלו. לכן, ההערכה שלנו היא שדרושה לפחות שנה אחת לצורך המעבר ל-TypeScript (ברבעון השלישי של 2020).

ביצוע ההעברה

השאלה הגדולה ביותר שנשארה: איך נעביר ל-TypeScript? יש לנו 150,000 שורות קוד ואנחנו לא יכולים להעביר את זה בבת אחת. ידענו גם שהרצת TypeScript ב-codebase שלנו תחשוף אלפי שגיאות.

בדקנו את האפשרויות הבאות:

  1. לקבל את כל השגיאות ב-TypeScript ולהשוות אותן לפלט "זהב". גישה זו דומה לגישה של צוות TypeScript. החיסרון הגדול ביותר של גישה זו הוא הכמות הגדולה של התנגשויות מיזוג, מאחר שעשרות מהנדסים עובדים על אותו בסיס קוד.
  2. הגדר את כל הסוגים הבעייתיים כ-any. פעולה זו תגרום למעשה לעקיפת שגיאות TypeScript. לא בחרנו באפשרות הזו, כי המטרה שלנו הייתה להעביר את התוכן בצורה נכונה, מה שיגרום להשחתת התוכן.
  3. תיקון כל השגיאות ב-TypeScript באופן ידני. תיקון אלפי שגיאות ותיקון שלו גוזלים זמן רב.

למרות המאמץ הצפוי, בחרנו באפשרות 3. היו סיבות נוספות לכך שבחרנו באפשרות הזו: למשל, היא אפשרה לנו לבדוק את כל הקוד ולבצע בדיקה פעם בעשור של כל הפונקציונליות, כולל היישום שלה. מנקודת מבט עסקית, לא סיפקנו ערך חדש, אלא שמירה על הסטטוס קוו. לכן היה קשה יותר לקבוע את אפשרות 3 כאפשרות הנכונה.

עם זאת, על ידי שימוש ב-TypeScript, היה לנו ברור שנוכל למנוע בעיות עתידיות, במיוחד סביב רגרסיות. לכן, הטיעון היה פחות "אנחנו מוסיפים ערך עסקי חדש", ויותר "אנחנו מבטיחים שלא נאבד את הערך העסקי שהושג".

תמיכת JavaScript בהדר של TypeScript

אחרי שהשגנו רכישה ופיתחנו תוכנית להפעלה של המהדר (compiler) מסוג Closure ו-TypeScript באותו קוד JavaScript, התחלנו עם קבצים קטנים. הגישה שלנו הייתה בעיקר מלמטה: להתחיל עם הקוד המרכזי ולהתקדם בארכיטקטורה עד שמגיעים לחלוניות ברמה הגבוהה.

הצלחנו לבצע במקביל את העבודה שלנו על ידי הוספה מראש של @ts-nocheck לכל קובץ יחיד בכלי הפיתוח. התהליך של "תיקון TypeScript" יהיה להסיר את ההערה @ts-nocheck ולפתור את כל השגיאות שיימצאו ב-TypeScript. לכן אנחנו בטוחים שכל קובץ נבדק ופתרנו כמה שיותר בעיות.

באופן כללי, הגישה הזו עבדה עם מעט בעיות. נתקלנו במספר באגים במהדר של TypeScript, אבל רובם היו מעורפלים:

  1. פרמטר אופציונלי עם סוג פונקציה שמחזיר any מטופל כנדרש: #38551
  2. הקצאה של מאפיין לשיטה סטטית של הצהרה על מעברי מחלקה: #38553
  3. בהצהרה של מחלקת משנה עם בנאי ללא ארגומנטים ומחלקת-על עם בנאי ארגומנטים, השמטת הבנאי המשני: #41397

הבאגים האלה מדגישים שעבור 99% מהמקרים, המהדר של TypeScript הוא בסיס איתן שאפשר לבנות עליו. כן, הבאגים המטושטשים האלה יכולים לפעמים לגרום לבעיות בכלי הפיתוח, אבל ברוב המקרים הם היו מעורפלים מספיק כדי שנוכל לעקוף אותם בקלות.

הבעיה היחידה שגרמה לבלבול מסוים הייתה הפלט הלא-דטרמיניסטי של קובצי .tsbuildinfo: #37156. ב-Chromium, אנחנו דורשים שכל שתי גרסאות build של אותה שמירה ב-Chromium יניבו את אותו הפלט. לצערנו, מהנדסי ה-build של Chromium גילו שהפלט .tsbuildinfo לא היה חד-משמעי: crbug.com/1054494. כדי לעקוף את הבעיה, נאלצנו לבצע תיקון קוף בקובץ .tsbuildinfo (שלמעשה מכיל JSON) ולאחר מכן להחזיר פלט דטרמיניסטי: https://crrev.com/c/2091448 למרבה המזל, צוות TypeScript פתר את הבעיה ב-upstream, וזמן קצר אחר כך הצלחנו גם להסיר את הפתרון החלופי. תודה לצוות TypeScript לקבל דוחות על באגים ולתקן את הבעיות האלה במהירות!

באופן כללי, אנחנו מרוצים מהתיקון (הסוג) הנכון של מהדר TypeScript. אנחנו מקווים שהכלים של Devtools, כפרויקט גדול של קוד פתוח, עזרו לגבש את התמיכה ב-JavaScript ב-TypeScript.

ניתוח התוצאות

הצלחנו להתקדם בצורה טובה בפתרון שגיאות מסוג זה ולהגדיל בהדרגה את כמות הקוד שנבדקה על ידי TypeScript. עם זאת, באוגוסט 2020 (9 חודשים לאחר תחילת המיגרציה) עשינו בדיקה וגילינו שלא נגיע ליעד שלנו בקצב הנוכחי. אחד מהמהנדסים שלנו בנה תרשים ניתוח כדי להציג את ההתקדמות של "TypeScriptification" (השם שנתנו להעברה הזו).

התקדמות ההעברה של TypeScript

התקדמות של העברת TypeScript – שורות הקוד שנותרו למעקב שצריך להעביר

ההערכות למקרים שבהם נהיה אפס שורות נעו בין יולי 2021 לדצמבר 2021, כמעט שנה אחרי המועד האחרון שלנו. לאחר דיונים עם ההנהלה ומהנדסי התוכנה האחרים, הסכמנו להגדיל את מספר המהנדסים שעובדים על המעבר לתמיכה בהידור TypeScript. זה התאפשר כי תכננו את ההעברה כך שתהיה מקבילה, כך שמספר מהנדסים שעובדים על מספר קבצים שונים לא יתנגשו זה עם זה.

בשלב זה, תהליך TypeScriptification הפך למאמץ של כל הצוות. בעזרת העזרה הנוספת, הצלחנו לסיים את המעבר שלנו בסוף נובמבר 2020, 13 חודשים אחרי שהתחילו, ויותר משנה לפני האומדן הראשוני שלנו.

בסך הכל, היו 771 רשימות שינויים (בדומה לבקשת Pull) שנשלחו על ידי 18 מהנדסים. באג המעקב שלנו (https://crbug.com/1011811) כולל יותר מ-1,200 תגובות (כמעט כולן פוסטים אוטומטיים מרשימות שינויים). גיליון המעקב שלנו הכיל יותר מ-500 שורות לכל הקבצים להקלדה, למי שהקצה אותו לרשימת השינויים ולרשימת השינויים שבה הם היו בסטטוס 'Typescriptified'.

הפחתת ההשפעה של ביצועי המהדר של TypeScript

הבעיה הגדולה ביותר שאנחנו מתמודדים איתה כיום היא הביצועים האיטיים של מהדר TypeScript. בגלל מספר המהנדסים שבונים את Chromium ואת כלי הפיתוח, צוואר הבקבוק הזה יקר. לצערנו, לא הצלחנו לזהות את הסיכון הזה לפני ההעברה. רק בשלב שבו העברנו את רוב הקבצים ל-TypeScript, גילינו עלייה משמעותית בזמן הפיתוח של גרסאות build של Chromium: https://crbug.com/1139220

דיווחנו על הבעיה הזו לצוות המהדר של Microsoft TypeScript, אבל לצערנו הם קבעו שההתנהגות הזו היא מכוונת. אנחנו מקווים שהם יבדקו מחדש את הבעיה הזו, אבל בינתיים אנחנו פועלים כדי לצמצם ככל האפשר את ההשפעה האיטית על הביצועים בצד Chromium.

לצערנו, הפתרונות שזמינים לנו כיום לא תמיד מתאימים למי שתורם תוכן לגוגלרים. מכיוון שהתרומות של הקוד הפתוח ל-Chromium הן חשובות מאוד (במיוחד אלה של צוות Microsoft Edge), אנחנו מחפשים באופן פעיל חלופות שיתאימו לכל השותפים ביצירת התוכן. עם זאת, בשלב זה לא מצאנו פתרון חלופי מתאים.

המצב הנוכחי של TypeScript בכלי פיתוח

בשלב זה, הסרנו את בודק סוג המהדר (compiler) של Closure מ-codebase שלנו ואנחנו מסתמכים אך ורק על המהדר מסוג TypeScript. אנחנו מסוגלים לכתוב קבצים שנכתבו על ידי TypeScript ולהשתמש בתכונות ספציפיות ל-TypeScript (כגון ממשקים, כללים כלליים וכו'...), דבר שעוזר לנו על בסיס יומיומי. הגברנו את הביטחון שהמהדר של TypeScript יזהה שגיאות סוג ורגרסיות, וזה מה שקיווינו שיקרה כשהתחלנו לעבוד על ההעברה הזו לראשונה. המעבר הזה, כמו כל כך הרבה פעמים, היה איטי, מאתגר ולרוב מאתגר, אבל מכיוון שאנחנו מספקים את היתרונות, אנחנו מאמינים שהוא היה שווה את זה.

הורדת הערוצים של התצוגה המקדימה

כדאי להשתמש ב-Chrome Canary, Dev או Beta כדפדפן הפיתוח בברירת מחדל. ערוצי התצוגה המקדימה האלה נותנים לך גישה לתכונות החדשות של כלי הפיתוח, בודקים ממשקי API מתקדמים של פלטפורמת האינטרנט ומוצאים בעיות באתר לפני שהמשתמשים יגלו אותן!

יצירת קשר עם צוות כלי הפיתוח ל-Chrome

אפשר להשתמש באפשרויות הבאות כדי לדון בתכונות החדשות ובשינויים בפוסט, או בכל דבר אחר שקשור לכלי הפיתוח.

  • שלחו לנו הצעה או משוב דרך crbug.com.
  • כדי לדווח על בעיה בכלי הפיתוח, לוחצים על אפשרויות נוספות   עוד   > עזרה > דיווח על בעיות בכלי הפיתוח בכלי הפיתוח.
  • אפשר לשלוח ציוץ אל @ChromeDevTools.
  • אפשר לכתוב תגובות לגבי 'מה חדש' בסרטוני YouTube בכלי הפיתוח או בסרטונים ב-YouTube בקטע 'טיפים לשימוש בכלי הפיתוח'.