সাইটের রুট লেআউটে থাকা একটি শেয়ারড ফাংশন একটি A/B-টেস্ট এক্সপোজার কাউন্টারকে পেজ-ভিউ কাউন্টারে রূপান্তরিত করে ফেলেছিল, যার ফলে স্যাম্পল সাইজ বেড়ে যায় এবং কনভার্সন রেটগুলো অর্থহীন হয়ে পড়ে। হোমপেজের একটি ৫০/৫০ স্প্লিট করা এই টেস্টে, একটি ভ্যারিয়েন্টের জন্য ১৭৮টি হিট এবং অন্যটির জন্য ৫৭টি হিট রিপোর্ট করা হয়েছিল—যা প্রত্যাশিত সমান বিভাজনের চেয়ে অনেক দূরে ছিল।
কীভাবে এই ত্রুটি র্যান্ডমাইজারের নজর এড়িয়ে গেল
ডেভেলপার প্রথমে সেই র্যান্ডমাইজারটি যাচাই করেছিলেন যা ভিজিটরদের একটি ভ্যারিয়েন্টে নিযুক্ত করে। তিনি মিডলওয়্যারটি পড়েন, কুকি লজিকটি পরীক্ষা করেন এবং একটি স্ক্রিপ্ট চালান যা অ্যাসাইনমেন্ট ফাংশনটিকে ১০,০০০ বার কল করেছিল; এটি একটি নিখুঁত ৫০/৫০ স্প্লিট তৈরি করেছিল। র্যান্ডমাইজারটি নিজে ঠিকঠাক কাজ করছিল; সমস্যাটি ছিল এক্সপোজার কীভাবে রেকর্ড করা হচ্ছিল তা নিয়ে।
একটি মাত্র ফাংশন দুটি কাজ করছিল:
১. ভ্যারিয়েন্ট সেট করা – ভিজিটরের অভিজ্ঞতা সামঞ্জস্যপূর্ণ রাখতে এটি প্রতিটি পেজ লোডের সময় চলে। ২. এক্সপোজার ইভেন্ট রেকর্ড করা – এটি প্রতি ভিজিটরের জন্য কেবল একবারই ফায়ার হওয়া উচিত, ঠিক যখন ভ্যারিয়েন্টটি প্রথমবার প্রদর্শিত হয়।
ফাংশনটি রুট লেআউটে ছিল, যা প্রতিটি নেভিগেশনের সময় রেন্ডার হওয়া একটি কম্পোনেন্ট। যেহেতু এক্সপোজার-রেকর্ডিং কোডটি প্রতিবার লেআউট রেন্ডার হওয়ার সময় কার্যকর হচ্ছিল, তাই প্রতিটি পেজ ভিউ একটি নতুন এক্সপোজার হিসেবে গণ্য হচ্ছিল। হোমপেজের দুটি ভ্যারিয়েন্ট সামান্য ভিন্ন লেআউট ট্রি ব্যবহার করছিল, তাই তাদের পেজ-ভিউ রেট ভিন্ন হয়ে যায়, যা একটি ত্রুটিপূর্ণ র্যান্ডমাইজারের বিভ্রম তৈরি করেছিল।
কেন এই ভুল গণনা গুরুত্বপূর্ণ ছিল
কনভার্সন সংখ্যা—ক্লিক-থ্রু, সাইন-আপ, পারচেজ—সঠিকভাবে লগ করা হয়েছিল। কিন্তু হর বা ডিনোমিনেটর (এক্সপোজারের সংখ্যা) ভুল ছিল। গণনাকৃত কনভার্সন রেটগুলো বাস্তবতার তুলনায় অনেক কম দেখাচ্ছিল এবং সেই রেটগুলোর ওপর ভিত্তি করে নেওয়া যেকোনো সিদ্ধান্ত ছিল অনির্ভরযোগ্য।
অসংগতিটি সামনে আসার আগে টেস্টটি দুই সপ্তাহ ধরে চলেছিল, যার ফলে টিমকে পুরো ডেটা সেটটি বাতিল করতে বাধ্য হতে হয়।
সমাধান
সমাধানটি ছিল সহজ: দায়িত্বগুলোকে আলাদা আলাদা ফাংশনে ভাগ করা। এক্সপোজার-রেকর্ডিং কোডটি এখন পরীক্ষা করে দেখে যে ভিজিটরকে ইতিমধ্যে গণনা করা হয়েছে কিনা, এবং প্রতি ব্যবহারকারীর জন্য কেবল একবারই ফায়ার হয়। ভ্যারিয়েন্ট-সেটিং কোডটি যেখানে ছিল সেখানেই থাকে এবং প্রতিটি নেভিগেশনে চলতে থাকে।
পরীক্ষা পরিচালনাকারীদের জন্য তিনটি শিক্ষা
- স্টেট-সেটিং এবং ওয়ান-অফ ইভেন্টগুলোকে আলাদা রাখুন। যে ফাংশনটি একই সাথে একটি ভ্যারিয়েন্ট নিযুক্ত করে এবং একটি এক্সপোজার লগ করে, সেটি সংঘর্ষ তৈরি করবে কারণ প্রথমটি বারবার ঘটে কিন্তু দ্বিতীয়টি করা উচিত নয়।
- রুট লেআউটে ওয়ান-শট লজিক এড়িয়ে চলুন। এমন কোনো কম্পোনেন্টে কিছু রাখলে যা প্রতিটি পেজ লোডের সময় রেন্ডার হয়, তা বারবার কার্যকর হবে এবং "প্রতি ভিজিটরের জন্য একবার" বিষয়টি "প্রতি পেজ ভিউতে একবার" হয়ে যাবে।
- যখন পর্যবেক্ষিত স্প্লিট র্যান্ডমাইজারের সাথে সাংঘর্ষিক হয়, তখন প্রথমে কাউন্টারটি অডিট করুন। ডেভেলপাররা প্রায়ই র্যান্ডমাইজারের নিরপেক্ষতা পরীক্ষা করেন কিন্তু গণনা করার পদ্ধতিটি সঠিক কিনা তা খুব কমই যাচাই করেন।
পরবর্তীতে যা খেয়াল রাখতে হবে
এক্সপোজারের জন্য যে কোনো পরীক্ষা যা একটি একক কাউন্টারের ওপর নির্ভর করে, কম্পোনেন্ট ট্রিতে সেই কাউন্টারটি কোথায় অবস্থিত তা অডিট করা উচিত। যদি কাউন্টারটি একটি গ্লোবাল লেআউটে থাকে, তবে একটি চেক যোগ করুন যা ইভেন্টটিকে একটি পারসিস্টেন্ট আইডেন্টিফায়ারের সাথে যুক্ত করে—যেমন একটি কুকি বা লোকাল-স্টোরেজ ফ্ল্যাগ। টিমগুলোর তাদের ড্যাশবোর্ডে একটি স্যানিটি চেকও তৈরি করা উচিত: যদি পর্যবেক্ষিত ভ্যারিয়েন্ট ডিস্ট্রিবিউশন একটি ছোট পরিসংখ্যানগত মার্জিন ছাড়িয়ে যায়, তবে র্যান্ডমাইজারটি ত্রুটিপূর্ণ বলে ধরে নেওয়ার আগে কাউন্টার অডিটের জন্য টেস্টটিকে ফ্ল্যাগ করুন।
সংক্ষেপে, একটি নির্ভরযোগ্য এক্সপোজার কাউন্ট ছাড়া একটি সঠিকভাবে কাজ করা র্যান্ডমাইজারও অকেজো। দায়িত্ব ভাগ করা এবং ওয়ান-অফ ইভেন্টগুলোকে সবসময় রেন্ডার হওয়া কম্পোনেন্টের বাইরে রাখা A/B টেস্টগুলোকে সঠিক রাখে এবং সপ্তাহের পর সপ্তাহ সময়ের অপচয় বাঁচায়।
