Amirali Yaghoutiمهندس ارشد نرم‌افزار

مطالعه موردی مهندسی

اطلاع‌رسان موجودشدن کالا

در فروشگاه ساعت و جواهری که در هر لحظه حدود سه‌چهارم کاتالوگش ناموجود است، فهرست انتظار یک امکان تزئینی نیست؛ تنها راهی است که یک صفحهٔ ناموجود می‌تواند بازدیدکننده را به فروش برساند. افزونه‌ای که این کار را می‌کرد فقط یک ایمیل می‌گرفت و بس، آن هم در فروشگاهی که مشتری‌هایش را با SMS پیدا می‌کند. جایش سیستمی ساختم که هر دو کانال را می‌پذیرد، دقیقاً یک بار خبر می‌دهد و آدم را به‌عنوان سرنخ به CRM فروشگاه می‌سپارد.

مسئله تجاری

افزونهٔ فهرست انتظارِ نصب‌شده یک آدرس ایمیل می‌گرفت و هیچ‌چیز دیگر. در این فروشگاه هر پیام تراکنشی، از رمز یک‌بارمصرف تا بازیابی سبد و جایزهٔ باشگاه، با SMS می‌رود؛ فهرستی که نمی‌تواند SMS بفرستد فهرستی است که کسی به آن عمل نمی‌کند. حدود سه‌چهارم کاتالوگ در هر لحظه ناموجود است، همان صفحه‌ها همچنان از جست‌وجو بازدید می‌گیرند، و بازدیدکننده‌ای که به یکی از آن‌ها می‌رسید راهی جز رفتن نداشت.

بازبینی داده‌های افزونهٔ قبلی پیش از مهاجرت نشان داد این کاستی چه هزینه‌ای داشته. ۲۵۰ اشتراک در دو سال جمع شده بود: ۲۱۷ ایمیل متمایز که برای بیشترشان هیچ شماره‌ای ثبت نبود؛ ۲۴ تایشان به یک حساب مشتری می‌خورد و ۲۱ تای آن‌ها شماره داشتند؛ و ۱۱ محصولی که مردم منتظرش بودند از قبل برگشته بود، بعضی بیش از یک سال، و به هیچ‌کس خبر داده نشده بود.

آنچه تحویل دادم

  • اشتراکی با کلیدِ محصول به‌علاوهٔ هویت؛ هویت می‌تواند شماره تلفن، ایمیل یا حساب واردشده باشد. دو ایندکس یکتا روی محصول+تلفن و محصول+ایمیل نشسته و چون ایندکس یکتای MySQL هر تعداد NULL را می‌پذیرد، ردیف فقط‌ایمیل و ردیف فقط‌تلفن برای یک محصول کنار هم می‌مانند، بی‌آنکه مقداری ساختگی جای «هیچ» بنشیند.
  • تشخیص برگشت موجودی که با ذخیرهٔ ادمین، اجرای ایمپورتر، بازپرداخت، یا افزونه‌ای که هیچ‌وقت هوک مورد انتظار را نمی‌زند، سر پا می‌ماند. یک جاروی ساعتی هم هست، به‌عنوان تور ایمنی نه سازوکار اصلی: محصولی را می‌گیرد که از مسیری برگشته که کسی حواسش به آن نبود.
  • ارسال صف‌شده و فقط یک بار. ارسال ناهمگام است چون موجودی معمولاً وسط ذخیرهٔ فرم ادمین یا اجرای ایمپورتر برمی‌گردد، و پنجاه رفت‌وبرگشت به درگاه داخل همان درخواست یعنی ذخیره‌ای که انگار هنگ کرده. هر اشتراک یک بار خدمت می‌گیرد و این را وضعیتِ خودِ ردیف تضمین می‌کند، نه یک ورودی کش.
  • اتصال به CRM. مشترک ناشناس اگر مخاطب موجودی داشته باشد به همان وصل می‌شود و یک سرنخ باز می‌شود تا اپراتور بتواند روی تقاضایی که فروشگاه فعلاً نمی‌تواند برآورده کند کار کند. انصراف و فهرست سیاهِ موجود پیش از نوشتن ردیف رعایت می‌شود، نه پیش از ارسال.
  • وضعیت رابط کاربری سازگار با کش. دکمه‌ها هیچ وضعیتی در HTML ندارند؛ صفحه بعد از تعاملی‌شدن، یک بار و در یک درخواست، وضعیت همهٔ دکمه‌ها را می‌پرسد. این‌طور پیام «شما همین حالا در این فهرست هستید» که برای بازدیدکنندهٔ اول ساخته شده، در کش تمام‌صفحهٔ طولانی‌عمر جا نمی‌ماند.
  • مهاجرتی از افزونهٔ قبلی که دوبار اجرا شدنش بی‌خطر است، چیزی را تکراری نمی‌کند و عمداً به هیچ‌کس خبر نمی‌دهد؛ به‌علاوهٔ نمایی در اپ اپراتور که اشتراک‌های امروز و ردیف‌های خبرداده‌شده را فهرست می‌کند تا کارکنان تماس بگیرند.

رویکرد فنی

  • شش ماژول کوچک که به ترتیب بار می‌شوند و هرکدام یک مسئولیت دارد: هسته برای اسکیما، نرمال‌سازی هویت، عضویت، لغو و اتصال CRM؛ یک API سه‌اندپوینتی برای عضو شدن، خارج شدن و خواندن وضعیت؛ دکمه و دیالوگش در سه جایی که محصول ناموجود دیده می‌شود؛ استایل و اسکریپتی که درون‌خطی چاپ می‌شود و فقط به صفحه‌های دکمه‌دار می‌رسد؛ ارسال؛ و یک تب در حساب خود مشتری که نشان می‌دهد منتظر چیست.
  • خواندن روی هر سه هویت هم‌زمان انجام می‌شود. کسی می‌تواند ماه‌ها پیش از ساختن حساب، به‌عنوان مهمان و با شماره تلفن عضو شده باشد؛ جواب «آن شما نبودید» را نمی‌پذیرد.
  • ایمیل را برای تمیزتر شدن طراحی حذف نکردم، چون حذفش یعنی دور ریختن ۱۹۳ آدم واقعی. و مهاجرت را بدون ارسال انجام دادم، چون پیام دربارهٔ ساعتی که هجده ماه پیش برگشته خدمت نیست.
  • دو چیز را حاضر نشدم باور کنم. هیچ نشانه‌گذاری‌ای داخل HTML قیمت نمی‌رود، چون فهمیدم افزونهٔ بومی‌سازی ارقام داخلش را بازنویسی می‌کند، حتی رقمِ داخل نام کلاس CSS را. و خروجی نرمال‌سازِ تلفنِ موجود اعتبارسنجی می‌شود نه باور، بعد از آنکه یک رشتهٔ پنج‌رقمی را دست‌نخورده برگرداند.
  • خدمت‌گرفته یعنی خدمت‌گرفته. کسی که در فروردین پرسیده و در شهریور خبر گرفته، خبرش را گرفته و ردیف این را برای همیشه ثبت می‌کند. اگر بخواهد دوباره خبردار شود باید دوباره بخواهد، و دکمه همین را از قبل پیشنهاد می‌دهد.

نتیجه و شواهد

اعداد بازبینی بالا اندازه‌گیریِ داده‌های افزونهٔ قدیمی پیش از مهاجرت است؛ آنچه را جایگزین کردم توصیف می‌کند، نه آنچه سیستم جدید تولید کرده. آنچه زنده تأیید شده: الگوی SMS موجودشدن کالا سیم‌کشی و اثبات شده، با نام محصول و لینک در دو توکن جداگانهٔ قالب، و نمای «موجودشدن کالا» در اپ اپراتور ردیف‌هایی با وضعیت خبرداده‌شده برمی‌گرداند. عدم‌ارسال‌های اولیه به یک نیم‌فاصله در نام محصول‌ها و به میان‌بُر HTTP در حالت امن سایت برمی‌گشت. سیستم در تولید است.

اهمیت برای کارفرما

سه‌چهارم کاتالوگِ ناموجود یعنی سه‌چهارم ترافیک جست‌وجو روی صفحه‌هایی می‌نشیند که نمی‌توانند بفروشند. فهرست انتظاری که آدم‌ها را روی کانالی که واقعاً می‌خوانند پیدا کند، یک بار، و آن‌ها را به‌عنوان سرنخ جلوی اپراتور بگذارد، آن صفحه‌ها را به تقاضایی تبدیل می‌کند که فروشگاه می‌بیند و رویش کار می‌کند. به قضاوت من همین شکل به هر خرده‌فروشی با کسری موجودی منتقل می‌شود، و به کلینیک‌ها و سالن‌ها برای اعلام خالی‌شدن نوبت.

خلاصه-اجرایی-پروژه

خلاصه اجرایی خوانا

خلاصه_اجرایی {
  پروژه: "اطلاع‌رسان موجودشدن کالا"
  وضعیت: "زنده؛ جایگزین افزونهٔ فهرست انتظار شخص ثالث"
  هویت: "محصول + تلفن | ایمیل | حساب؛ دو ایندکس یکتا،
         NULL-پذیر، بدون مقدار ساختگی"
  تشخیص: "هوک‌های موجودی + جاروی ساعتی برای مسیرهایی
          که هوک نمی‌زنند"
  ارسال: "صف‌شده، هرگز درون‌خطی؛ یک بار به ازای هر ردیف،
          با وضعیت ردیف نه با کش"
  CRM: "اتصال به مخاطب موجود، باز کردن سرنخ؛
        انصراف و فهرست سیاه پیش از نوشتن چک می‌شود"
  رابط: "بی‌وضعیت در HTML؛ یک درخواست وضعیت برای هر صفحه،
         بعد از تعاملی‌شدن، تا کش تمام‌صفحه امن بماند"
  مهاجرت: "بی‌خطر در اجرای دوباره؛ به هیچ‌کس خبر نمی‌دهد"
}

ارزش حرفه‌ای این پروژه

سیاست مهاجرت همان تصمیمی است که دوست دارم بازبین نگاهش کند. یازده محصول برگشته بود، بعضی بیش از یک سال، و به کسی خبر داده نشده بود. فرستادن آن پیام‌ها با تأخیر از سکوت بدتر بود؛ پس مهاجرت ردیف‌ها را جابه‌جا می‌کند و هیچ‌چیز نمی‌فرستد.

بیشتر مهندسی این پروژه نپذیرفتنِ فرض‌هاست: اینکه هوک حتماً می‌زند، اینکه نرمال‌ساز نرمال می‌کند، اینکه HTML قیمت بی‌آزار است، اینکه صفحهٔ کش‌شده می‌تواند وضعیت هر بازدیدکننده را حمل کند. دوتای این‌ها، افزونهٔ بومی‌سازی که رقم‌ها را بازنویسی می‌کرد و نرمال‌سازی که رشتهٔ پنج‌رقمی را دست‌نخورده برگرداند، را فقط با گزیده‌شدن یاد گرفتم.