منذ وقت ليس ببعيد ،
نظرنا في كيفية ترتيب تجارب A / B في البحث. تحدث رئيس فريق التطوير لإصدار iOS من Yandex.Browser ، Andrei Sikerin
sav42 ، في الاجتماع الأخير لـ CocoaHeads Russia ، أيضًا عن البنية التحتية لاختبار A / B ، فقط في مشروعه.

- مرحبًا ، اسمي Andrey Sikerin ، أقوم بتطوير Yandex.Browser لنظام التشغيل iOS. أريد أن أخبرك ما هي منصة تجربة المتصفح لنظام التشغيل iOS ، وكيف تعلمنا استخدامها ، ودعمت ميزاتها الأكثر تقدماً ، وكيفية تشخيص وتصحيح الميزات التي تم طرحها باستخدام نظام التجربة ، وأيضًا مصدر الانتروبيا وأين يتم تخزين العملة.
لذلك دعونا نبدأ. نحن في متصفح iOS لن نعرض ميزة للمستخدمين مطلقًا مرة واحدة. أولاً ، نجري اختبار A / B ، ونحلل المنتج والمقاييس التقنية من أجل فهم كيف تؤثر الميزة المتدحرجة على المستخدم ، سواء أعجبه أم لا ، سواء أهدرت بعض المقاييس التقنية. لهذا نستخدم التحليلات. تحليلاتنا تبدو مثل هذا:

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

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

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

دعني ، في الحقيقة ، أريكم ملفًا يتضمن وصفًا للتجارب. المتصفح لديه مثل هذه الميزة - المترجم. انها خرجت في تجربة. يبدأ الملف بلوك الدراسة. يبدأ تكوين أي تجربة بهذا الحظر. التجربة تسمى مترجم. يمكن أن يكون هناك العديد من كتل الدراسة بهذا الاسم. وداخل كتلة الدراسة ، هناك العديد من مجموعات التجارب التي تم تعيين أسماء مختلفة. في هذه الحالة ، نرى تمكين مجموعة التجربة. وهناك كتلة مرشح ، والتي ، في الواقع ، تصف في ظل أي ظروف يمكن أن يصبح هذا التكوين نشطًا ، أي معاييره.
هناك نوعان من العلامات هنا - قناة و ya_min_version. القناة تعني عرض التجميع. يشار إلى الإصدار التجريبي هنا ، مما يعني أن هذا التكوين في الملف يمكن أن يصبح نشطًا فقط للتجميعات التي نرسلها إلى TestFlight. بالنسبة إلى App Store ، لا يمكن أن يصبح هذا التكوين وفقًا لمعايير القناة نشطًا.
يعني ya_min_version أنه مع الحد الأدنى من إصدار التطبيق 19.3.4.43 ، يمكن أن يصبح هذا التكوين نشطًا. في الواقع ، في هذا الإصدار من التطبيق ، اكتسبت الميزة بالفعل مثل هذا النموذج الذي يمكنك تمكينه.
هذا هو أبسط وصف لمجموعة تكوين تجربة المترجم. يمكن أن يكون هناك العديد من كتل الدراسة في ملف. باستخدام العلامات في كتلة المرشح ، قمنا بتعيينها لقنوات مختلفة ، للتجميعات الداخلية ، لتجميعات BETA ، لمعايير مختلفة.
في ما يلي مجموعة تجارب واحدة تسمى ممكّنة ، ولها علامة وزن مرجحة ، وهي وزن مجموعة التجربة. هذا عدد صحيح غير سالب يُستخدم لتحديد المجموعة النشطة في الوقت الذي تنبثق فيه العملة المعدنية.
دعنا نتخيل أن هذا التكوين على الشريحة أصبح نشطًا. هذا هو ، لقد قمنا بالفعل بتثبيت التطبيق مع النسخة التجريبية العامة ، ولدينا بالفعل إصدار 19.3.4.43 وما بعده. كيف يتم قذف العملة المعدنية؟ العملة المعدنية هي رقم عشوائي يتم إنشاؤه محليًا من صفر إلى واحد.
حتى نقع في نفس المجموعة خلال الإطلاق التالي ، يتم تخزينه على القرص. بينما سننظر في ذلك. بعد ذلك ، سوف أخبرك بكيفية التأكد من عدم تخزينه. يتم طرح العملة. افترض أن 0.5 تم إلقاؤه. يتم تحجيم هذه العملة في قطعة من الصفر إلى مجموع مجموعات من التجارب. في هذه الحالة ، لدينا مجموعة واحدة ممكّنة ، وزنها 1000 ، أي أن مجموع جميع المجموعات سيكون 1000. المقاييس "0.5" إلى 500. وفقًا لذلك ، تقسم جميع مجموعات التجارب الفاصل الزمني من صفر إلى مقدار التجارب والثغرات. وتصبح المجموعة نشطة في الفترة التي ستشير فيها القيمة المقاسة للعملة.
يمكننا أن نطلب اسم مجموعة التجارب النشطة في الشفرة وبالتالي تحديد إمكانية الوصول - هل نحن بحاجة إلى تمكين الميزة أم لا.

علاوة على ذلك ، سننظر في التكوينات التجريبية الأكثر تعقيدًا التي نستخدمها في الإنتاج. أولاً ، من الواضح أنه من الغباء طرح ميزة بنسبة 100٪ ، فنحن نستخدمها فقط للنسخة التجريبية أو للتجميع الداخلي. للإنتاج ، نستخدم الميكانيكا التالية.
نقسم المستخدمين إلى ثلاث مجموعات - مستخدمون قديمون ، مستخدمون بدون تجربة ومستخدمون جدد. في المعنى ، وهذا يعني ما يلي. المستخدمون القدامى هم أولئك الذين استخدموا تطبيقنا بالفعل وقاموا بتثبيت التطبيق مع ميزات أعلى الإصدار القديم. أي أنهم استخدموه بالفعل ، ولم يكن لديهم ميزات ، اعتادوا على كل شيء وتحديث التطبيق فجأة ، حيث يوجد نوع من التجربة ، وظائف جديدة. ثم - المستخدمين دون تجربة والمستخدمين الجدد. جديدة هي تلك التي وضعت التطبيق نظيفة. أي أنهم لم يستخدموا Yandex.Browser ، قرروا فجأة استخدامه وتثبيت التطبيق.
كيف نحقق هذا القسم؟ في كتلة المرشح ، وضعنا شروط علامات min_install_date و max_install_date. افترض أن X هي 14 مارس 2019 - هذا هو تاريخ الإصدار الخاص ببناء الميزة. ثم سيكون max_install_date للمستخدمين القدامى هو X ناقص 21 يومًا ، قبل إصدار التجميع مع الميزات. إذا كان للتطبيق تاريخ تثبيت كهذا ، فمن المحتمل جدًا أن يكون الإطلاق الأول له قبل الإصدار. وقبل الإصدار كان هناك إصدار بدون ميزات. وإذا كان لديه الآن ، بشكل مشروط ، إصدار به ميزات ، فهذا يعني أنه تلقى التطبيق بمساعدة تحديث.
وبالنسبة للمستخدمين الجدد ، قمنا بتعيين min_install_date. نحن فضح أنها X زائد بضعة أيام. هذا يعني: إذا كان لديه تاريخ تثبيت ، أي أنه قام بالإطلاق الأول بعد تاريخ إصدار الإصدار مع ميزات ، ثم كان لديه تثبيت نظيف. لديه الآن إصدار به ميزات ، ولكن تاريخ التثبيت كان أحدث من هذا الإصدار بالميزات.
وبالتالي ، فإننا نقسم المستخدمين إلى مستخدمين قديمين ، دون تجربة وأخرى جديدة. نفعل ذلك لأننا نرى: سلوك المستخدمين القدامى يختلف عن سلوك المستخدمين الجدد. وفقًا لذلك ، يمكننا ، على سبيل المثال ، عدم الطلاء في مجموعة مع مستخدمين قديمين ، ولكن الطلاء في مجموعة مع مستخدمين جدد ، أو العكس. إذا أجرينا تجربة على الكتلة بأكملها ، فقد لا نرى ذلك.

لنلقِ نظرة على هذه التجربة. نرى تكوين التجربة التالي - مترجم لمتجر التطبيقات ، مستخدمون جدد. دراسة كتلة ، اسم مترجم ، مجموعة ممكّنة. تعني البادئة الجديدة أننا نصف التكوين للعديد من المستخدمين الجدد. الوزن 500 (إذا كان مجموع كل الأوزان هو 1000 ، فإن قوة هذه المجموعة هي 50 ٪). Control_new ، الوزن 500 ، هذه هي المجموعة الثانية. والشيء الأكثر إثارة للاهتمام هو المرشحات للقناة STABLE ، أي للتجمعات التي يتم تجميعها للإنتاج. الإصدار الذي ظهرت فيه الميزة: 19.4.1. وهنا علامة min_install_date. هنا ، بتنسيق وقت يونكس ، يتم تشفيره في 18 أبريل 2019. هذا بعد أيام قليلة من إصدار الإصدار 19.4.1.
هناك جزء آخر هنا إلى جانب البادئة الجديدة ، يتم تمكينه والتحكم فيه. هذه هي بادئة التحكم ؛ إنها ليست عرضية. وإلى جانب حقيقة قيامنا بتقسيم المستخدمين إلى مستخدمين جدد وقديمة ، فإننا نقسمهم إلى مجموعات داخل التجربة إلى عدة أجزاء.

الجزء الأول من المستخدمين هو مجموعة تحكم ، واحدة لها بادئة التحكم. لا توجد ميزات في ذلك. لديها وزن X ، كما أن لديها مجموعة ميزات تسمى عادةً ممكّنة. كما أن لها وزن X ، وهذا أمر مهم: هناك يجب تشغيل الميزة. وهناك مجموعة افتراضية لها وزن 1 ناقص 2X (1000 ناقص 2X ، حيث 1000 هي قيمة الوزن الكلي لجميع المجموعات ضمن نفس التكوين ، والتي يتم قبولها بشكل افتراضي). المجموعة الافتراضية أيضا لا تتضمن أي ميزة. إنه ببساطة يخزن المستخدمين الذين بقوا بعد تقسيمهم إلى عنصر تحكم وميزة. يمكنك أيضًا إعادة تشغيل التجربة منه ، إذا لزم الأمر.

لنرى ، على سبيل المثال ، التكوين للمستخدمين القدامى. سنرى ميزة ومراقبة المجموعة هنا. enable_old - المميز. control_old ، - التحكم ، 10 ٪. default_old - افتراضي ، 80٪.
ملاحظة عامل التصفية ، ya_min_version 19.4.1 ، max_install_date 28 مارس 2019. هذا تاريخ أقدم من تاريخ الإصدار. وفقًا لذلك ، هذا تكوين مع قائمة المستخدمين الذين تلقوا الإصدار 19.4.1 بعد التحديث. استخدموا التطبيق والآن يستخدمون الإصدار الجديد.
لماذا هناك حاجة إلى مجموعات الميزات والتحكم؟ في التحليلات التي عرضتها على الشريحة الأولى ، قارنا مجموعة التحكم ومجموعة الميزات. يجب أن تكون متساوية في القوة بحيث يمكن مقارنة مقاييس منتجاتها.
وبالتالي ، نقوم بمقارنة مجموعات التحكم والميزات في التحليلات لمجموعات المستخدمين المختلفة ، القديمة والجديدة. إذا قمنا برسم كل شيء ، فسنقوم بلفة الميزة بنسبة 100٪.

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

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

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

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

كيف تبدو هذه من الكود؟ يعلن المبرمج عن كيانات فئتي Feature و FeatureParam. ويكتب قيمًا من هذه العناصر الأولية إلى طبقة الوصول إلى الميزة. بعد ذلك ، يقوم بتمرير هذا المعرّف إلى المحلل ، ويقوم بالفعل في ملف التكوين بتعيين المعرفات في كتلة مجموعة التجربة باستخدام علامة feature_association. بمجرد أن تصبح مجموعة التجربة نشطة ، يتم تعيين قيم الميزات والمعلمات مع هذه المعرفات في التعليمات البرمجية من الملف. في حالة عدم وجود معلمات وميزات في المجموعة ، يتم استخدام القيم الافتراضية ، والتي تتم الإشارة إليها من الرمز.
يبدو أن هذا أعطانا؟ أولاً ، عند إضافة مجموعة جديدة ، لا يحتاج المحلل إلى مطالبة المبرمج بإضافة مجموعة ميزات جديدة إلى التعليمات البرمجية ، لأن طبقة الوصول إلى البيانات تعمل مع معرّفات لا تتغير عند إضافة مجموعة جديدة إلى نظام التجربة.
ثانياً ، لقد نشرنا الوقت الذي يتوصل فيه المبرمجون إلى هذه المعرفات للميزات ومعلمات المعالم ، مع الوقت الذي يطور فيه المحلل آليات التجربة. يتطور المحلل عندما تكون الميزة جاهزة ، ويأتي المبرمج مع هذه المعرفات عندما يكتب الرمز ، في البداية.
كما يسمح لك بتقسيم الميزة إلى أجزاء. افترض أن هناك ميزة تسمى Translator ، والتي تتضمن ، في الواقع ، المترجم. وهناك ميزة TranslateServiceAPITimeout ، وهي تشمل وظائف إضافية يمكن أن تحدد مهلة مخصصة لواجهة برمجة تطبيقات Translator. وبالتالي ، يمكننا القيام بمجموعتين من التجارب ، يتم تشغيل المترجم في كليهما ، لكن في نفس الوقت ، نقارن القيمة الأفضل: 300 مللي ثانية أو 600.
. . (FeatureParam).
, , , . , , . , , . . ?

, : Feature FeatureParam. Feature FeatureParam . , Feature FeatureParam, , . . - , , .
-, Feature&FeatureParam. , «», , , . FeatureParam , , API — 300 600 ?
. . - , . public beta, . , .
, : .

? , , .
: . URL, , .
: browser://version — show-variations-cmd. : cheat-, . : .

. , . proto- - , study-, . , . Feature&FeatureParam, . , , . , , Feature&FeatureParam .
. proto- . . , . , , .

والثاني. Feature&FeatureParam? Chromium, . Chromium browser://version, show-variations-cmd.
: enabled-features, force-fieldtrials force-fieldtrials-params, . , . ? , . , Feature1 trial1. Feature2 trial2. Feature3 .
trial1 group1. trial2 group2. force-fieldtrials-params, , trial1 group1, p1 v1, p2 v2. trial2 group2, p3 v3, p4 v4.
, . Chromium, iOS. , .
. --force-fieldtrials=translator/enabled_new/ enabled_new translator.
--force-fieldtrial-params==translator.enablew_new:timeout/5000, translator enabled_new, , , translator, enabled_new timeout, 5 000 .
--enabled-features=TranslateServiceAPITimeout<translator , - translator translator , , , TranslateServiceAPITimeout. , , , , .

(cheat urls). , , , , , , . . . .
yandexbrowser:// (.), , . . my_pet_experiment=group_name. , enable-features=, , disable-features=, . , &.
(cheat url), . , , , . , , . filter, my_pet_experiment , . 1000, feature_association, , .
, . , , . , — my_pet_experiment — , , . , , study .

, . — , . . , .
, .

, , , , . , . , .
. . , , ? , . , ?
. .

, . , , UUID, application Identifier, . . hash .
? ? , , UUID, . ? , , . . ? hash hash , . ,
Google, An Efficient Low-Entropy Provider.
, — UUID, , , . , , Chromium.
, , ? ? :
- : , -. .
- . , , , .
- الاختبار. يجب أن يكون لديك آليات تسمح لك بإعادة تحديد قيم المجموعات أو الميزات أو المعلمات أو الكيانات الأخرى التي تحتاج إليها.
- واحد حيث يتم استخدام بدائل مختلفة للتحليلات والبرمجة.
- توسيع. يجب أن تكون قادرًا على رؤية كيفية عمله وتكييفه وفقًا لاحتياجاتك (راجع خدمة أشكال Chromium ).
يحتوي نظام Chromium ، الذي نقوم بتوسيعه في Yandex.Browser لنظام iOS ، على مثل هذه المعايير. قم بإجراء تجاربك وتحليلها وجعل التطبيقات أفضل. شكرا لك