الحياة قبل وقت التشغيل. تقرير ياندكس

في مشروع كبير ، قد تنشأ مهمة تحديد التغييرات للمستخدم النهائي من خلال الاختلافات في رمز الواجهة الأمامية للتطبيق. أخبر المطور من Yandex.Market Nikita Sidorovnickshevr كيف تم حل هذه المشكلة باستخدام مكتبة Diffector ، حول بناء وتحليل الرسم البياني للوحدة النمطية في تطبيقات Node.js وحول إيجاد عيوب في الكود قبل إطلاقه.



- اليوم سأحاول أن أكون صريحًا معك قدر الإمكان. لقد كنت أعمل في Yandex.Market منذ أكثر من عام ونصف. أفعل نفس المقدار من الويب ، وبدأت في ملاحظة التغييرات في نفسي ، يمكنك أيضًا ملاحظة ذلك. زاد متوسط ​​طول شعري وبدأت اللحية بالظهور. كما تعلمون ، لقد نظرت اليوم إلى زملائي: في سيرجي بيريزنيي ، في فوفا غريننكو تاداتوتا ، وأدركت - هذا معيار جيد لما نضجت تقريبًا كمطور حقيقي للواجهة الأمامية.

وعندما وصلت إلى هذا القسم ، قررت أن أتحدث معك عن الحياة ، وعن الحياة التي نشارك فيها جميعًا. في الغالب عن الحياة قبل وقت التشغيل. الآن سأشرح ما سيكون عليه كل هذا.



ماذا عن الحياة؟ عن حياة الكود ، بالطبع. الرمز هو ما نفعله. دعني أذكرك ، قررت أن أكون صادقا معك هنا ، لذلك كانت الشريحة الأولى بسيطة قدر الإمكان. أخذت الحقيقة ، المرحلة الأولى - التبني ، كما ترى ، لن يجادل أحد مع هذه البديهية.



ثم أدركت أنه سيتعين علي تعديله ، لكن ذلك كان واضحًا. فليكن نوعا من قبول المتطلبات. أي رمز يبدأ بحقيقة أنك تنظر إلى المهمة وتحاول قبول المتطلبات التي تحددك.



بعد ذلك ، بالطبع ، نبدأ مرحلة الكتابة - نكتب الشفرة الخاصة بنا. ثم نغطيها بالاختبارات ونفحص فعاليتها بأنفسنا. بعد ذلك ، نتحقق بالفعل من أن تطبيقنا يعمل مع الكود الخاص بنا ككل. بعد ذلك ، نعطيها للاختبار - دعه يتحقق. ما رأيك بعد ذلك؟ أنا أذكرك ، الحياة قبل وقت التشغيل. هل تعتقد أن وقت التشغيل يتبع هذا؟ في الواقع ، اتضح مثل هذا. وهذا ليس خطأ في العرض. في كثير من الأحيان في أي مرحلة من مراحل الشيكات - وقد يكون هناك الكثير مما أشرت إليه - قد يكون لديك بعض المكالمات الهاتفية للكتابة مرة أخرى. موافق ، هذا يمكن أن يكون مشكلة كبيرة جدا. قد يؤدي ذلك إلى إبطاء تسليم بعض الميزات في الإنتاج ، ومن حيث المبدأ ، يبطئ من قدرتك كمطور ، لأن البطاقة ستعلق عليك. وهنا كل شيء يمر ، يمر. هناك بعض المرات M للشيكات N ، وعندها فقط يحصل الكود على المستخدم في المتصفح. ولكن هذا هو هدفنا. هدفنا هو كتابة التعليمات البرمجية التي ستكون متاحة للمستخدم وستعمل حقًا لصالحه.

اليوم سنتحدث عن الجزء الأول. حول ما يحدث من قبل ، ولكن ليس حول الاختبارات.



بالمناسبة ، يبدو شيء من هذا القبيل. أخذت دورنا في تعقب ، وجمع بلدي ، تحسب الوسيط. اتضح أن تذاكري أقل تطوراً من التحقق. وكما تعلم ، كلما طال الفحص ، زادت فرصة ظهور إما goto في البداية أو ستظهر goto في النهاية - ولا أريد القيام بذلك على الإطلاق.

وأيضًا ، إذا كنت تهتم ، فهناك كلمتين على الشريحة هنا - التطوير (هذا ما نقوم به ، نحن المطورين) والتحقق (هذا ما نقوم به ، ولكن أيضًا المختبرين). لذلك ، فإن المشكلة ذات صلة ، في الواقع ، بالمختبرين.



الهدف هو جميل جدا. بشكل عام ، أود أن أقول إن الحياة تحتاج إلى التبسيط: نحن بالفعل نعمل معك كثيرًا. يبدو الهدف من هذا القبيل ، لكن يجب الاعتراف بأنه سريع الزوال ، لذلك دعونا نلقي الضوء على بعض المعايير الأساسية التي قد يعتمد عليها الهدف.

بالطبع ، كلما كان الرمز أقل ، أصبح الأمر أسهل بالنسبة لنا. كلما تحقق لدينا فحص CI أسرع ، سرعان ما سنعرف ما إذا كنا على صواب أم لا. وهذا هو ، محليا ، يمكن أن تبدأ عموما إلى الأبد. سرعة التحقق - وهذا ينطبق مباشرة على اختبار. إذا كان طلبنا كبيرًا ويجب التحقق منه بالكامل ، فهذا وقت كبير. سرعة الافراج تعتمد على كل هذا. بما في ذلك أنه من المستحيل إصداره حتى يتم اجتياز جميع الاختبارات ، وإلى أن نفهم أن الكود هو بالضبط ما نريده.

لحل بعض المشكلات التي نتحدث عنها ، دعونا نحلل الرسم البياني للاعتماد على الوحدات النمطية في لغة البرمجة الخاصة بنا. وفي الواقع ، دعنا نصف ذلك.



الرسم البياني موجه: يحتوي على حواف مع الاتجاهات. في العقد من الرسم البياني ، سيكون لدينا فقط وحدات اللغة التي نتحدث عنها. الأضلاع هي نوع معين من السندات. هناك عدة أنواع من الاتصالات.



دعونا نلقي نظرة على مثال شائع. يوجد ملف A. هنا ، يتم استيراد شيء ما من الملف B فيه ، وهذه علاقة بين العقد.



سوف يحدث نفس الشيء إذا استبدلت الاستيراد بـ "تتطلب". في الواقع ، كل شيء ليس بهذه البساطة هنا.



أقترح ، نظرًا لأننا نتحدث عن نوع التبعية ، فكر في نوعين على الأقل - تسريع خط الأنابيب الخاص بك ، لتسريع اجتياز الرسم البياني. من الضروري مشاهدة الوحدة التابعة فقط ، ولكن أيضًا مراقبة الوحدة التابعة. أقترح الاتصال بالوحدة A - الوالد ، B - الطفل ، وأنصحك بالاحتفاظ بالروابط دائمًا كقائمة مزدوجة مرتبطة. هذا سوف يبسط حياتك ، وأنا أبلغكم مقدما.

بمجرد وصفنا للرسم البياني بطريقة أو بأخرى ، دعونا نتفق على كيفية بنائه.



هناك طريقتان. إما أداتك المفضلة في لغة البرمجة المفضلة لديك باستخدام نفس AST (أشجار بناء الجملة المجردة) أو النظامي. ما هو الربح هنا؟ حقيقة أنك لست مرتبطًا بأي شخص ، ولكن في نفس الوقت عليك تنفيذ كل شيء بنفسك. سيتعين عليك وصف جميع أنواع الوصلات لكل تلك الأشياء والتقنيات التي تستخدمها ، سواء أكانت أداة تجميع CSS منفصلة أم لا ، شيء آخر كهذا. لكن لديك حرية طيران كاملة ، إذا جاز التعبير.

بالإضافة إلى ذلك ، الخيار الثاني ، سأقوم أيضًا بالترويج له قليلاً ، وهذا خيار فقط لمعظم الأشخاص الذين لديهم بالفعل نظام بناء تم تكوينه. الحقيقة هي أن نظام التجميع يجمع الرسم البياني حسب التصميم ، بشكل افتراضي.



دعونا نلقي نظرة على واحدة من أنظمة التجميع الأكثر شعبية في ياندكس ، وهذا هو webpack. قدمت هنا مثالًا على كيفية جمع النتيجة الكاملة لحزمة الويب في ملف منفصل ، والذي يمكن بعد ذلك إطعامه إلى محللنا أو بعض المحللين الآخرين. انه يجمعها بمساعدة AST ، يتم استخدام مكتبة البلوط. ربما لاحظتها عندما سقط شيء ما. لقد لاحظت.

وما هي المزايا. الحقيقة هي أنه عندما وصفت نظام البناء الخاص بك ، فأنت تطلب بصراحة الدخول. هذه هي الملفات التي لا تبعية لها من التبعيات ، ونقاط الالتفافية الأولية. هذا جيد ، لأنه ليس عليك تسجيلهم مرة أخرى. بالإضافة إلى ذلك ، webpack و بابل ، وكل هذا ، والجوز ، بما في ذلك ، ما زال الأمر غير صعب. وبالتالي ، يتم تصحيح جميع أنواع الميزات الجديدة للغة ، وجميع أنواع الأخطاء وكل شيء آخر ، بشكل أسرع مما لو كنت فعلت ذلك ، خاصةً إذا لم يكن لديك فريق كبير جدًا. نعم ، حتى لو كانت كبيرة ، فهي ليست كبيرة مثل المصدر المفتوح.

هذا هو زائد وناقص ، في الواقع. يشبه الحصول على حافة مزدوجة (سيف ذو حدين). الحقيقة هي أن هذا الرسم البياني مبني أثناء التجميع. إنه نوع من الخير ، أي أنه يمكننا تجميع المشروع وإعادة استخدام نتيجة التجميع على الفور. ولكن ماذا لو كنا لا نريد تجميع مشروع ، ولكن نريد فقط الحصول على هذا الرسم البياني؟

ومثل هذا ناقص كبير ، في الواقع. إذا كان لديك أي أشياء مخصصة متصلة ، سنتحدث لاحقًا عن الاتصالات ، فلن يسمح لك نظام الإنشاء بالقيام بذلك. أو ، يجب عليك دمج هذا ، مثل البرنامج المساعد webpack الخاص بك.



النظر في مثال محدد. قمت بتشغيل أمر على الإسقاط الخاص بي ، حيث يوجد ثلاثة ملفات فقط ، وحصلت على هذا الإخراج. وهذا أنا فقط إظهار مفتاح واحد ، وهو ما يسمى الوحدات. نحن نتحدث معك فقط حول الرسم البياني للاعتماد على الوحدات ، لذلك ننظر إلى الوحدات ، كل شيء منطقي.



الكثير من المعلومات ، لكننا لسنا بحاجة إلى كل شيء. اترك بعض النقاط ودعنا نتحدث عنها. لنفترض أننا نعتبر الوحدة الأولى. لديه اسم ، هناك أسباب. الأسباب هي مجرد الارتباط ، في الواقع ، مع وحدات "تابعة" ، اتضح لأولئك الذين يستوردون هذه الوحدة لأنفسهم. هذه هي البيانات الأساسية لبناء رسم بياني عليها.



بالإضافة إلى ذلك ، يرجى الانتباه إلى usedExports و provideExports. سنتحدث عنها بعد قليل. ولكن هذه هي أيضا أشياء مهمة للغاية.





وإذا وصفت قرارك ، فأنت بحاجة إلى الحديث عن أنواع الاتصالات التي تحدث بين الوحدات النمطية. هذا هو ، بالطبع ، لدينا نظام من الوحدات داخل لغتنا: سواء كانت وحدات cjs ، أو وحدات esm. بالإضافة إلى ذلك ، يجب أن توافق على أنه قد يكون لدينا اتصال بين الملفات في نظام الملفات على مستوى نظام الملفات نفسه. هذه هي نوع من الأطر: نوع من الإطار سوف يتم ذلك ، وهذا يتوقف على كيفية الآباء.



ومثالًا عاديًا - إذا كتبت جانب خادم Node ، فغالبًا ما تشاهد حزمة npm الشائعة مثل Config. انها تسمح لك لتحديد مريح تماما التكوينات الخاصة بك.



لاستخدامها ، تحتاج إلى الحصول على مجلد التكوين ، حيث لديك NODE_PATH ، وتحديد العديد من ملفات JavaScript - فقط لتقديم التهيئة هناك لبيئات مختلفة. على سبيل المثال ، لقد قمتُ بإعداد افتراضي وتطوير وإنتاج وإنتاج.



وفي الواقع ، التكوين بأكمله يعمل شيء من هذا القبيل. وهذا هو ، عندما تكتب تتطلب ('config') ، فإنه فقط يقرأ الوحدة النمطية داخل نفسه ويأخذ اسم الوحدة النمطية من متغير البيئة. كما فهمت ، لم يكن واضحًا أن هذه الملفات تُستخدم بطريقة أو بأخرى ، لأنه لا يوجد استيراد / طلب مباشر ، حتى أن حزمة الويب لن تتعرف عليها.


رابط من الشريحة

اليوم تحدثنا أيضا عن Dependency Injection. لم أكن مصدر إلهام ، لكنني نظرت لدعم إحدى المكتبات هنا. ويسمى ينعكس JS. كما ترون ، فإنه يوفر بناء جملة مخصص إلى حد ما: lazyInject ، nameProvider ، ومن هنا. ويجب أن نعترف أنه ليس من الواضح ما هو نوع الموفر ، ونوع الوحدة التي حقنها حقًا هنا. ونحن في حاجة إليها ، وعلينا أن نفهمها. هذا ، مرة أخرى ، لن يتم حل نظام الإنشاء ، وسيتعين علينا القيام بذلك بأنفسنا.

لنفترض أننا صممنا رسمًا بيانيًا ، وأقترح عليك البدء بتخزينه في مكان ما. ماذا سيسمح لنا بذلك؟ سيسمح لنا ذلك بإجراء تحليل إرشادي ، ولعب القليل من علوم البيانات ، والقيام بذلك ، مع التركيز على شريحة زمنية.



ما هي الفكرة؟ هنا ، في الواقع ، هو مباشرة بياناتنا. لقد قمنا مؤخرًا بتطبيق نظام التصميم الخاص بنا في Yandex.Market ، وعلى وجه الخصوص ، قمنا بتطبيق مكتبة مكونات كجزء من نظام التصميم هذا. وهنا يمكنك أن ترى: نأخذ في الاعتبار عدد الواردات ، ومكون التفاعل من مكتبتنا ، والمكون المشترك. ويمكنك التوزيع في الدلائل. في هذه الحالة ، لدينا مستودع غير أحادي ، وبالتالي لدينا platform.desktop و platform.touch و src.

ماذا يمكن أن نفكر عندما نرى هذه الأرقام؟ يمكننا افتراض أن الأمر touch لا يبدو أنه يزيد من استخدام المكونات الشائعة. هذا يعني إما أن المكونات سيئة للجوال - سيئة الصنع ، أو أن الأمر touch كسول. ولكن هل هذا حقا هكذا؟



إذا نظرنا إلى فترة أطول ، في فترة زمنية أطول ، فإن هذا يسمح لنا بالقيام فقط بتخزين الرسوم البيانية بعد كل إصدار ، ثم سوف نفهم أنه في الواقع ، كل شيء على ما يرام ، فإن المؤشر ينمو فيه. بالنسبة إلى src ، إنه أفضل من ذلك ، بالنسبة لسطح المكتب ، لم يتم ذلك.



لا يزال هناك سؤال من الجمهور حول كيفية شرح الأهمية للمديرين. هنا هو إجمالي عدد واردات المكتبة ، وكذلك حسب الوقت. أي مديرين لا يحبون الرسومات؟ يمكنك إنشاء مثل هذا الجدول الزمني وترى أن استخدام المكتبة ينمو ، مما يعني أن هذا شيء مفيد على الأقل.

واحدة من أجزامي المفضلة. سأغطيها لفترة وجيزة. هذا هو البحث عن العيوب في الرسم البياني. أردت اليوم أن أتحدث إليكم عن نوعين من العيوب: هذا اعتماد دوري على الوحدات وبعض الوحدات غير المستخدمة ، أي مشكلة إزالة الكود الميت.



لنبدأ بالتبعية الدائرية.



يبدو أن كل شيء بسيط للغاية هنا. لديك بالفعل رسم بياني موجه ، تحتاج فقط إلى العثور على حلقة هناك. سأشرح لماذا أتحدث عن هذا. والحقيقة هي أنه قبل أن أكتب ، بشكل أساسي ، جانب الخادم على Node.js ، ولم نستخدم ، من حيث المبدأ ، أي webpack / babel ، لا شيء. وهذا هو ، أطلقوا كما هي. وكان هناك حاجة. من يتذكر كيف يختلف الاستيراد عن الطلب؟ كل شيء صحيح. إذا كتبت الكود بطريقة سيئة ، لكنني فعلت ذلك بالفعل ، يمكنك أن تعرف على الخادم الخاص بك أن وحدتك في نوع من التبعية الدورية فقط عندما يأتي بعض الطلب من المستخدمين ، أو إذا كان هناك حدث آخر سيعمل. هذه مشكلة عالمية إلى حد ما. حتى وقت التشغيل لا يفهم. وهذا هو ، الاستيراد هو أفضل بكثير ، لن يكون هناك مثل هذه المشكلة.



ثم فقط تأخذ أي خوارزمية تريد. هنا أخذت خوارزمية بسيطة إلى حد ما. نحتاج إلى العثور على قمة تحتوي على نوع واحد فقط من الحواف - إما للداخل أو للخارج. إذا كان هناك قمة من هذا القبيل ، فسنحذفها ونزيل الحواف ، وفي الواقع نواصل هذه العملية ، سنجد ونثبت أن هناك دورة من خمس دورات في هذا الرسم البياني.

توافق ، إذا نظرت إليها من خلال الكود ، فهناك لا يزال بإمكانك العثور على دورة بطول أو ثلاثة أطوال ، لكنها أكثر واقعية ، وكان لدينا بالفعل دورة من سبعة في المشروع ، ولكن ليس في الإنتاج.



حول الوحدات غير المستخدمة. هناك أيضا خوارزمية تافهة إلى حد ما. نحن بحاجة إلى تسليط الضوء على المكونات المتصلة في الرسم البياني لدينا ، وإلقاء نظرة فقط ، والعثور على تلك المكونات ، التي لا تشمل أي من العقد الإدخال. في هذه الحالة ، هذا هو مكون التوصيل ، كلا الرأسين ، كما يتضح ، كلا العقدتين. ثم دعا الدخول. في الواقع ، بغض النظر عن ما يطلق عليه ، وهذا هو ما وصفته في التكوين التجميع إدخال يعني.



ولكن هناك نهج آخر. إذا لم تكن قد جمعت الرسم البياني ، وكان لديك فقط نظام بناء ، فكيف هي أرخص وسيلة للقيام بذلك؟ دعونا فقط وضع علامة على جميع الملفات التي حصلت على التجمع أثناء التجميع. وضع علامة عليها وإنشاء الكثير. بعد ذلك ، يجب أن نحصل على الكثير من الملفات الموجودة لديك في المشروع ، ونطرحها ببساطة. هذه عملية بسيطة للغاية.



والآن ، أنا لا أقول لك شيئًا نظريًا فحسب ، بل كنت مصدر إلهام ، وقد أتيت إلى مشروعي وفعلت ذلك. والانتباه! لم أحذف حتى node_modules. هذا غادرت كنقطة نمو للمراجعة القادمة. وباختصار ، لقد استلهمت من نفسي لدرجة أنني قررت إنشاء هذه الشريحة بطريقة أو بأخرى ، وإعادة ترتيبها. دعها تبدو هكذا ، لأنها رائعة حقًا!

أرقام جيدة ، هل يمكنك أن تتخيل كيف أصبح كل شيء جيدًا؟ وبعد ذلك تم اقتيادي إلى سهوب شعرت به كأنني مصمم ، وأعتقد أن هذا إنجاز أود إضافته إلى الإطار. وكما تعلم ، استيقظت ونظرت وأدركت أنني على الأرجح لا أكون مصممًا ، بل بالفعل مطور ويب. لكنني لست أحمق. أخذت هذا الإطار ، وأضاف إلى موقعي لتمائم كبار المسئولين الاقتصاديين.



يمكنك استخدام ، حتى الرابط هو. وبهذا لا تعتقد أنني أخدعك - نحن صريحون اليوم - لقد نظرت حقًا إلى المراجعات. أعتقد أنك يمكن أن تصدقهم.



حسنًا ، بصراحة ، بدا الأمر كهذا. رأيت مكتبة جديدة hypos-js ، أخذت ، إنشاء طلب تجمع. في السر ، لديّ حقوق المسؤول في مستودعنا. وأخذت وأربك السيد. كيف تحب ذلك؟ حسنًا ، أنت وأنا صريحان ، وفي الواقع ، بدا كل شيء هكذا. إذا لم يكن أحد يعرف ذلك ، فإن thanos-js هي مكتبة تزيل ببساطة 50٪ من الشفرة بشكل عشوائي.



في الواقع ، استخدمت المكتبة هناك على أي حال ، لكن المكتبة تسمى بشكل مختلف. يطلق عليه اسم ناشر ، والآن سنتحدث عن ذلك معك. وهنا أود أن أشير إلى أن طلب التجميع كبير للغاية ، ناقص 44 ألف سطر من الكود ، ويمكنك أن تتخيله - لقد اجتاز الاختبار في المرة الأولى. وهذا هو ، ما أتحدث عنه يمكن أن تعمل حقا.



Diffector. في الواقع ، لا يشارك فقط في مهمة إزالة الوحدات غير المستخدمة ، ويبحث عن عيوب في الرسم البياني ، ولكن أيضًا في مهمة أكثر أهمية. ما أعلنته في البداية هو مساعدة المطور والاختبار ، والآن سنتحدث عن ذلك. ويعمل شيء من هذا القبيل.

نحصل على قائمة الملفات المعدلة باستخدام نظام التحكم في الإصدار. لقد بنينا بالفعل رسم بياني - يبنيها المنشور. وبالنسبة لكل ملف تم تعديله ، فإننا نبحث عن مسار الإدخال ونضع علامة على الإدخال المعدل. سيتوافق الإدخال مع صفحات التطبيق التي سيشاهدها المستخدم. لكن هذا منطقي جدا.

وماذا يعطينا هذا؟ للاختبار - نحن نعرف الصفحات التي تغيرت في التطبيق. يمكننا أن نقول للاختبار أن فقط منهم يستحق الاختبار. يمكننا أيضًا أن نخبر ci-job ، التي تدير الاختبارات الذاتية ، أن هذه الصفحات فقط تستحق الاختبار. وبالنسبة للمطورين ، كل شيء أبسط بكثير ، لأن الآن المختبرين لا يكتبون إليك ولا يسألون: "لماذا تحتاج إلى الاختبار؟"



دعونا نلقي نظرة على مثال لكيفية عمل الناشر. هنا لدينا دليل معين ، pages.desktop / *. أنه يحتوي فقط على قائمة الصفحات نفسها. وصفت الصفحات أيضا من قبل العديد من الملفات. وحدة التحكم هي جانب خادم الصفحة. الرأي هو نوع من رد فعل جزء. و deps ، وهذا من نظام بناء آخر. ليس لدينا فقط webpack ، ولكن أيضًا ENB.



وقمت ببعض التغييرات على المشروع ، إلى ملف فارغ ، هيكل الذي رأيته. هذا هو ما يعطيني diffector. لقد بدأت للتو ، diffector هو تطبيق سطر الأوامر. لقد أطلقتها ، أخبرني أنني قد قمت بتغيير صفحة واحدة ، تسمى BindBonusPage.



يمكنني أيضًا تشغيله في وضع مطوّل ، ورؤية تقرير أكثر تفصيلًا ، وأرى أنه يعمل على الأقل في مثل هذه الحالة البسيطة. كما نرى ، في BindBonusPage لدينا ملف الفهرس والتحكم قد تغيرت.

ولكن دعونا نرى ما يحدث إذا قمنا بتغيير شيء آخر.



لقد غيرت شيئا آخر. وأخبرني المنشق أنني غيرت تسع صفحات. وهذا لا يجعلني سعيدًا بعد الآن ، كما لو أنه لن يساعدني حقًا.



دعونا نرى لماذا؟ يعرض الآن أسباب اعتبار هذه الصفحة معدلة. , . - uikit.



diff. . , diffector . , - , .



, , . , , entry, , , test-scope, . .

. , , , , .



. - , . — i18n, , . , , , . , , - .

? , , , , , .



- . , B , , -2 . . , esm.



.



.



, value, . , , . , .

, AST, , 250 , , . , , - , , .



, - GlobalContext - , . , modify, , ? , - GlobalContext. . . , side effects. , , webpack, , . , webpack sideEffects: true, . .



, - - . , . diffector, . , , . — , . , .

, , diff, expand, log, , , , .



, . D, diff. , , , . , , . , , . .

, , . . . , — , . . . , , , , , . . , .

— diffector ? , , , .



- , , .



, , entry. entry. diffector.



. -. , .



, entry -, .



. diffector. . , , - , -. , . : , BindBonusPage, -, . . , , - . .





— CI. . : , , .



, . 43 — testing, , .



. , , .



, . : , , . , , , , , . , , - .



. , , , . - , - , , , . .

, , , output . , . — . — . شكرا لك

Source: https://habr.com/ru/post/ar454470/


All Articles