هذه المشاركة هي جزء من سلسلة من مشاركات المدونة التي تصف التغييرات التي نُجريها على بنية "أدوات مطوّري البرامج في Chrome" وكيفية إنشائها.
في إطار متابعة نقل البيانات إلى وحدات JavaScript ونقل البيانات إلى Web Components، نواصل اليوم سلسلة مشاركات المدونة حول التغييرات التي نجريها على بنية Devtools وكيفية إنشائها. (إذا لم يسبق لك الاطّلاع عليه، لقد نشرنا فيديو عن عملنا على ترقية بنية "أدوات مطوّري البرامج" إلى الويب الحديث، مع 14 نصيحة حول كيفية إجراء تحسينات على مشاريع الويب.)
في هذه المشاركة، سنوضّح رحلتنا التي استمرّت 13 شهرًا للانتقال من أداة التحقّق من أنواع Closure Compiler إلى TypeScript.
مقدمة
نظرًا لحجم قاعدة بيانات DevTools والحاجة إلى منح المهندسين الذين يعملون عليها الثقة، يصبح استخدام مدقّق أنواع ضروريًا. لهذا الغرض، اعتمدت "أدوات مطوّري البرامج" أداة Closure Compiler في عام 2013. من خلال استخدام Closure، تمكّن مهندسو أدوات المطوّرين من إجراء التغييرات بثقة، إذ سيُجري مُجمِّع Closure عمليات التحقّق من النوع لضمان أنّ جميع عمليات دمج النظام ذات أنواع صحيحة.
ومع مرور الوقت، أصبحت أدوات التحقّق من الأنواع البديلة شائعة في تطوير الويب الحديث. ومن الأمثلة البارزة على ذلك TypeScript وFlow. بالإضافة إلى ذلك، أصبحت TypeScript لغة برمجة رسمية في Google. ومع زيادة رواج هذه الأدوات الجديدة للتحقّق من الأنواع، لاحظنا أيضًا أنّنا نُصدِر تراجعات كان من المفترض أن يرصدها أحد أدوات التحقّق من الأنواع. لذلك، قرّرنا إعادة تقييم اختيارنا لأداة التحقّق من النوع وتحديد الخطوات التالية لتطوير DevTools.
تقييم مدقّقي الأنواع
بما أنّ أدوات المطوّرين كانت تستخدم مدقّق أنواع، كان السؤال الذي علينا الإجابة عنه هو:
هل نواصل استخدام Closure Compiler أو ننتقل إلى مدقّق أنواع جديد؟
للإجابة عن هذا السؤال، كان علينا تقييم مدقّقي الأنواع استنادًا إلى عدة خصائص. بما أنّ استخدامنا لأداة التحقّق من النوع يركز على ثقة المهندس، فإنّ أهم جانب بالنسبة إلينا هو صحة النوع. بعبارة أخرى: ما مدى موثوقية مدقّق الأنواع في اكتشاف المشاكل الحقيقية؟
ركّز تقييمنا على حالات التراجع التي تم طرحها وتحديد الأسباب الأساسية لها. والافتراض هنا هو أنّه بسبب استخدامنا "مجمّع Closure" من قبل، لن يرصد Closure هذه المشاكل. وبالتالي، علينا تحديد ما إذا كان بإمكان أي مدقّق أنواع آخر اكتشاف ذلك.
صحة النوع في TypeScript
وبما أنّ TypeScript كانت لغة برمجة متوافقة رسميًا في Google وتزداد شعبيتها بسرعة، قرّرنا تقييم TypeScript أولاً. كان TypeScript خيارًا مثيرًا للاهتمام، لأنّ فريق TypeScript نفسه يستخدم DevTools كأحد مشاريع الاختبار لتتبُّع التوافق مع ميزة التحقّق من النوع في JavaScript. أظهرت نتائج الاختبار المرجعي الأساسي أنّ TypeScript كان يرصد عددًا كبيرًا من مشاكل النوع، وهي مشاكل لا يرصدها بالضرورة "محرِّر Closure". من المرجّح أنّ العديد من هذه المشاكل كانت السبب الأساسي في حدوث التراجعات التي كنا نطرحها، ما دفعنا إلى الاعتقاد بأنّ TypeScript قد يكون خيارًا مناسبًا لاستخدامه في "أدوات مطوّري البرامج".
أثناء نقل البيانات إلى وحدات JavaScript، اكتشفنا أنّ Closure Compiler كان يرصد مشاكل أكثر مما كان يرصده في السابق. أدى الانتقال إلى تنسيق وحدة عادي إلى زيادة قدرة Closure على فهم قاعدة بياناتنا، وبالتالي زيادة فعالية مدقّقي الأنواع. ومع ذلك، كان فريق TypeScript يستخدم إصدارًا أساسيًا من DevTools يعود إلى ما قبل نقل وحدات JavaScript. لذلك، كان علينا معرفة ما إذا كان نقل البيانات إلى وحدات JavaScript قد قلّل أيضًا من عدد الأخطاء التي سيرصدها محوّل TypeScript البرمجي.
تقييم TypeScript
تم طرح "أدوات مطوّري البرامج" منذ أكثر من عقد من الزمن، وتطوّرت لتصبح تطبيق ويب كبيرًا وغنيًا بالميزات. في وقت كتابة هذا المقال، تحتوي "أدوات المطوّر" على 150,000 سطر تقريبًا من رمز JavaScript التابع للطرف الأول. عندما أجرينا عملية تجميع TypeScript على رمز المصدر، كان حجم الأخطاء الهائل أمرًا لا يُطاق. تبيّن لنا أنّه على الرغم من أنّ برنامج تجميع TypeScript كان يُصدر عددًا أقل من الأخطاء المرتبطة بحلّ الرموز البرمجية (حوالي 2,000 خطأ)، لا يزال هناك 6,000 خطأ آخر في قاعدة الرموز البرمجية المرتبطة بتوافق الأنواع.
أظهر ذلك أنّه على الرغم من أنّ TypeScript تمكّنت من فهم كيفية حلّ أنواع الأخطاء، إلا أنّها عثرت على عدد كبير من حالات عدم توافق الأنواع في قاعدة الرموز البرمجية.
أظهر التحليل اليدوي لهذه الأخطاء أنّ TypeScript كان صحيحًا (في معظم الأحيان).
السبب في أنّ TypeScript تمكّنت من رصد هذه الأخطاء ولم يتمكّن Closure من ذلك هو أنّه غالبًا ما يستنتج مُجمِّع Closure أنّ النوع هو Any
، في حين أنّ TypeScript ستُجري استنتاجًا للنوع استنادًا إلى عمليات الربط وستستنتج نوعًا أكثر دقة.
ونتيجةً لذلك، تبيّن أنّ TypeScript كان أفضل في فهم بنية عناصرنا واكتشاف الاستخدامات التي تتضمن مشاكل.
من الجدير بالذكر أنّ استخدام "مجمّع Closure" في "أدوات المطوّر" يتضمّن الاستخدام المتكرّر @unrestricted
.
يؤدي التعليق التوضيحي لفئة باستخدام @unrestricted
إلى إيقاف عمليات التحقّق الصارمة من السمات في "مجمّع Closure" لهذه الفئة المحدّدة، ما يعني أنّه يمكن للمطوّر إضافة تعريف فئة حسب الرغبة بدون أمان النوع.
لم نتمكّن من العثور على أي سياق تاريخي يوضّح سبب انتشار استخدام @unrestricted
في قاعدة بيانات "أدوات مطوّري البرامج"، ولكنّ ذلك أدّى إلى تشغيل Closure compiler في وضع تشغيل أقل أمانًا لشرائح كبيرة من قاعدة البيانات.
أظهر التحليل المتعدّد لعمليات التراجع التي أجريناها مع أخطاء النوع التي اكتشفتها TypeScript أيضًا تداخلًا، ما دفعنا إلى الاعتقاد بأنّ TypeScript كان بإمكانه منع حدوث هذه المشاكل (بشرط أن تكون الأنواع نفسها صحيحة).
إجراء مكالمة على any
في هذه المرحلة، كان علينا الاختيار بين تحسين استخدامنا لـ Closure Compiler أو نقل البيانات إلى TypeScript. (بما أنّ تطبيق Flow لم يكن متوافقًا مع Google أو مع Chromium، اضطررنا إلى التخلي عن هذا الخيار). استنادًا إلى المناقشات التي أجريناها مع مهندسي Google الذين يعملون على أدوات JavaScript/TypeScript واقتراحاتهم، اخترنا استخدام مترجم TypeScript. (نشرنا أيضًا مؤخرًا مشاركة مدونة حول نقل Puppeteer إلى TypeScript).
كانت الأسباب الأساسية لاستخدامنا لمحرِّك TypeScript هي تحسين صحة النوع، في حين أنّ المزايا الأخرى تشمل الدعم من فِرق TypeScript داخل Google وميزات لغة TypeScript، مثل interfaces
(على عكس typedefs
في JSDoc).
وقد فرض علينا اختيار مترجم TypeScript بذل جهد كبير في تحسين قاعدة بيانات DevTools وبنيتها الداخلية. وبناءً على ذلك، توقّعنا أن نحتاج إلى عام واحد على الأقل لنقل البيانات إلى TypeScript (المستهدف في الربع الثالث من عام 2020).
تنفيذ عملية نقل البيانات
كان السؤال الأكبر الذي بقي هو: كيف سننقل البيانات إلى TypeScript؟ لدينا 150,000 سطر من الرموز البرمجية ولا يمكننا نقلها دفعة واحدة. كنا نعلم أيضًا أنّ تشغيل TypeScript على قاعدة الرموز البرمجية الخاصة بنا سيؤدي إلى رصد آلاف الأخطاء.
لقد قيّمنا خيارات متعدّدة:
- احصل على جميع أخطاء TypeScript وقارِنها بإخراج "مثالي". سيكون هذا النهج مشابهًا لما يستخدمه فريق TypeScript. أكبر عيب في هذا النهج هو حدوث تعارضات الدمج بشكل كبير، لأنّ عشرات المهندسين يعملون في قاعدة البيانات نفسها.
- اضبط جميع الأنواع التي تتضمّن مشاكل على
any
. سيؤدي ذلك إلى أن تمنع TypeScript الأخطاء بشكل أساسي. لم نختار هذا الخيار لأنّ هدفنا من عملية نقل البيانات هو تصحيح الأخطاء في أنواع البيانات، ما سيؤدي إلى تقويض عملية الإلغاء. - أصلِح جميع أخطاء TypeScript يدويًا. سيتضمّن ذلك تصحيح آلاف الأخطاء، ما يستغرق وقتًا طويلاً.
على الرغم من الجهد الكبير المتوقّع، اخترنا الخيار 3. كانت هناك أسباب إضافية لاختيارنا هذا الخيار: على سبيل المثال، سمح لنا هذا الخيار بتدقيق كل الرموز البرمجية وإجراء مراجعة لكل الوظائف مرة واحدة كل عشر سنوات، بما في ذلك تنفيذها. من منظور النشاط التجاري، لم نكن نقدّم قيمة جديدة، بل كنّا نحافظ على الوضع الراهن. وقد جعل ذلك من الصعب تبرير الخيار 3 على أنّه الخيار الصحيح.
ومع ذلك، من خلال استخدام TypeScript، اعتقدنا بشدة أنّه يمكننا تجنُّب المشاكل المستقبلية، خاصةً في ما يتعلّق بالتراجعات. وهكذا، لم تكن الحجة هي "نحن نضيف قيمة جديدة للنشاط التجاري"، بل كانت "نحن نضمن عدم فقدان قيمة النشاط التجاري التي تم الحصول عليها".
توافق JavaScript مع محوّل TypeScript البرمجي
بعد الحصول على الموافقة وتطوير خطة لتشغيل كلّ من مُجمِّع Closure ومُجمِّع TypeScript على رمز JavaScript نفسه، بدأنا ببعض الملفات الصغيرة. كان نهجنا في الغالب من الأسفل إلى الأعلى: نبدأ بالرمز البرمجي الأساسي وننتقل إلى التصميم حتى نصل إلى اللوحات العالية المستوى.
تمكّنا من إجراء عملنا بشكل موازٍ من خلال إضافة @ts-nocheck
بشكل استباقي إلى كل ملف في DevTools. تتمثل عملية "إصلاح TypeScript" في إزالة التعليق التوضيحي @ts-nocheck
وحلّ أي أخطاء يعثر عليها TypeScript. وهذا يعني أنّنا كنّا على ثقة من أنّه تم فحص كل ملف وأنّه تم حلّ أكبر عدد ممكن من مشاكل الأنواع.
بشكل عام، نجحت هذه الطريقة مع حدوث بعض المشاكل. واجهنا عدة أخطاء في مُجمِّع TypeScript، ولكن معظمها كان غامضًا:
- يتم التعامل مع المَعلمة الاختيارية التي لها نوع دالة يعرض
any
على أنّها مطلوبة: #38551 - يؤدي تعيين خاصية إلى طريقة ثابتة لفئة إلى إيقاف البيان: #38553
- إنّ تعريف فئة فرعية باستخدام دالة إنشاء لا تتضمّن وسيطات وفئة رئيسية باستخدام دالة إنشاء تتضمّن وسيطات يحذف الدالة الإنشائية للطفل: #41397
تُبرز هذه الأخطاء أنّ "مجمّع TypeScript" هو أساس متين يمكن الاعتماد عليه في 99% من الحالات. نعم، كانت هذه الأخطاء غير الواضحة تتسبب أحيانًا في حدوث مشاكل في DevTools، ولكن في معظم الأحيان كانت غير واضحة بما يكفي لكي نتمكّن من تجنّبها بسهولة.
كانت المشكلة الوحيدة التي سبّبت بعض الارتباك هي الإخراج غير الحاسم لملفات .tsbuildinfo
: #37156.
في Chromium، نطلب أن يؤدي أي إصدارَين من الإصدار نفسه من Chromium إلى الناتج نفسه بالضبط.
تبيّن لمهندسي الإصدار في Chromium أنّ ناتج .tsbuildinfo
غير محدّد: crbug.com/1054494.
لحلّ هذه المشكلة، اضطررنا إلى إجراء تصحيح برمجي لملف .tsbuildinfo
(الذي يحتوي بشكل أساسي على ملف JSON) ومعالجته بعد ذلك لعرض نتيجة محدّدة: https://crrev.com/c/2091448
لحسن الحظ، حلّ فريق TypeScript المشكلة في المصدر وتمكّنا قريبًا من إزالة الحلّ البديل. نشكر فريق TypeScript على قبول تقارير الأخطاء وإصلاح هذه المشاكل على الفور.
بشكل عام، نحن سعداء بدقة (النوع) لمجمّع TypeScript. نأمل أن يكون Devtools، وهو مشروع JavaScript كبير ومفتوح المصدر، قد ساعد في تعزيز دعم JavaScript في TypeScript.
تحليل ما بعد الحدث
لقد تمكّنا من تحقيق تقدّم جيد في حلّ هذه الأخطاء من النوع وزيادة كمية الرموز البرمجية التي يتحقّق منها TypeScript تدريجيًا. ومع ذلك، في آب (أغسطس) 2020 (بعد 9 أشهر من بدء عملية نقل البيانات هذه)، أجرينا عملية تحقّق وتبيّن لنا أنّنا لن نلتزم بالموعد النهائي بالوتيرة الحالية. أنشأ أحد مهندسينا رسمًا بيانيًا للتحليل لعرض مستوى تقدّم عملية "TypeScriptification" (الاسم الذي أطلقناه على عملية نقل البيانات هذه).
تقدّم نقل TypeScript - تتبُّع أسطر الرموز البرمجية المتبقية التي تحتاج إلى نقلها
كانت التقديرات التي وضعناها بشأن موعد انتهاء عملية نقل البيانات تتراوح بين تموز (يوليو) 2021 وكانون الأول (ديسمبر) 2021، أي بعد مرور عام تقريبًا على الموعد النهائي. بعد إجراء مناقشات مع الإدارة والمهندسين الآخرين، اتفقنا على زيادة عدد المهندسين الذين يعملون على نقل البيانات إلى متوافق مع TypeScript compiler. وقد تمكّنا من إجراء ذلك لأنّنا صمّمنا عملية نقل البيانات بحيث يمكن تنفيذها بالتوازي، ما يسمح لعدد من المهندسين بالعمل على ملفات متعددة مختلفة بدون تعارض مع بعضهم.
في هذه المرحلة، أصبحت عملية استخدام TypeScript جهدًا جماعيًا. وبفضل المساعدة الإضافية، تمكّنا من إنهاء عملية نقل البيانات في نهاية تشرين الثاني (نوفمبر) 2020، أي بعد 13 شهرًا من بدء العملية، وقبل أكثر من عام من الموعد المقدَّر في التقدير الأولي.
في المجمل، تم إرسال 771 قائمة تغييرات (تشبه طلب سحب) من قِبل 18 مهندسًا. يحتوي خطأ التتبّع (https://crbug.com/1011811) على أكثر من 1200 تعليق (جميعها تقريبًا مشاركات مبرمَجة من قوائم التغييرات). احتوت ورقة التتبّع على أكثر من 500 صفّ لجميع الملفات التي سيتم تحويلها إلى TypeScript، والمخصّص لها، وقائمة التغييرات التي تم فيها إجراء عملية التحويل.
الحد من تأثير أداء مترجم TypeScript
إنّ أكبر مشكلة نواجهها حاليًا هي الأداء البطيء لمحرر TypeScript. ونظرًا لعدد المهندسين الذين يطوّرون Chromium وDevTools، فإنّ هذه المشكلة تتسبب في تكاليف باهظة. لم نتمكّن من تحديد هذا الخطر قبل نقل البيانات، ولم نكتشف زيادة ملحوظة في الوقت المستغرَق في جميع إصدارات Chromium إلا بعد نقل معظم الملفات إلى TypeScript: https://crbug.com/1139220
لقد أبلغنا عن هذه المشكلة إلى فريق مترجم Microsoft TypeScript، ولكن تبيّن لهم أنّ هذا السلوك مقصود. نأمل أن يعيد الفريق النظر في هذه المشكلة، ولكن في الوقت الحالي، نعمل على الحدّ من تأثير الأداء البطيء من جانب Chromium قدر الإمكان.
للأسف، إنّ الحلول المتاحة لنا اليوم ليست مناسبة دائمًا للمساهمين من غير موظفي Google. بما أنّ المساهمات في Chromium ذات مصدر مفتوح مهمة جدًا (خاصةً تلك التي يقدّمها فريق Microsoft Edge)، نبحث بنشاط عن بدائل تناسب جميع المساهمين. في الوقت الحالي، لم نتوصّل إلى حل بديل مناسب.
الحالة الحالية لـ TypeScript في "أدوات مطوّري البرامج"
في الوقت الحالي، أزلنا مدقّق أنواع مُجمِّع Closure من قاعدة الرموز البرمجية لدينا واعتمدنا فقط على مُجمِّع TypeScript. يمكننا كتابة ملفات مكتوب بها TypeScript والاستفادة من الميزات الخاصة بـ TypeScript (مثل الواجهات والأنواع العامة وما إلى ذلك)، ما يساعدنا بشكل يومي. لقد ازداد لدينا الثقة بأنّ مُجمِّع TypeScript سيرصد أخطاء النوع والتراجعات، وهو ما كنّا نأمل حدوثه عندما بدأنا العمل على نقل البيانات هذه لأول مرة. كانت عملية نقل البيانات هذه، مثل العديد من عمليات النقل الأخرى، بطيئة ودقيقة وصعبة في بعض الأحيان، ولكن مع تحقيق المزايا، نعتقد أنّها كانت جديرة بالاهتمام.
تنزيل قنوات المعاينة
ننصحك باستخدام إصدار Canary أو Dev أو الإصدار التجريبي من Chrome كمتصفّح التطوير التلقائي. تتيح لك قنوات المعاينة هذه الوصول إلى أحدث ميزات DevTools، وتتيح لك اختبار واجهات برمجة تطبيقات منصات الويب المتطوّرة، وتساعدك في العثور على المشاكل في موقعك الإلكتروني قبل أن يعثر عليها المستخدمون.
التواصل مع فريق "أدوات مطوّري البرامج في Chrome"
استخدِم الخيارات التالية لمناقشة الميزات الجديدة أو التحديثات أو أي شيء آخر مرتبط بـ "أدوات مطوّري البرامج".
- يمكنك إرسال الملاحظات وطلبات الميزات إلينا على crbug.com.
- يمكنك الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" باستخدام رمز خيارات إضافية > مساعدة > الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" في "أدوات مطوّري البرامج".
- يمكنك نشر تغريدة على Twitter على @ChromeDevTools.
- يمكنك إضافة تعليقات على فيديوهات YouTube حول الميزات الجديدة في "أدوات مطوّري البرامج" أو فيديوهات YouTube حول نصائح حول "أدوات مطوّري البرامج".