برچسب: CI/CD

  • پینترست چگونه زمان CI اندروید را بیش از ۳۶٪ کاهش داد؟

    پینترست چگونه زمان CI اندروید را بیش از ۳۶٪ کاهش داد؟

    پینترست به‌تازگی یک مطالعه موردی فنی (Technical Case Study) منتشر کرده است. این گزارش نشان می‌دهد تیم مهندسی شرکت چگونه با بازطراحی زیرساخت تست، زمان اجرای CI اندروید را بیش از ۳۶ درصد کاهش داد. این بهبود با پیاده‌سازی یک استراتژی شاردینگ آگاه از زمان اجرا (Runtime-aware Test Sharding) به دست آمد.

    پیش از این تغییرات، پایپ‌لاین CI اندروید پینترست عملکرد کند و غیرقابل‌پیش‌بینی داشت. توزیع نامناسب تست‌ها باعث می‌شد زمان بیلد به‌شدت افزایش پیدا کند. تیم تست‌ها را در پلتفرم‌های شخص ثالث فقط بر اساس نام پکیج‌ها شارد می‌کرد. در نتیجه، کندترین شارد کل فرایند CI را متوقف می‌کرد.

    مشکل کجا بود؟

    در معماری قبلی، پینترست از Firebase Test Lab (FTL) استفاده می‌کرد. این سرویس تست‌ها را بر اساس گروه‌بندی پکیج‌ها توزیع می‌کرد. این روش یک ضعف جدی داشت. برخی تست‌ها زمان اجرای بسیار طولانی‌تری نسبت به سایرین داشتند. این موضوع بار کاری شاردها را نامتوازن می‌کرد. در نهایت، کندترین شارد زمان کل CI را تعیین می‌کرد.

    علاوه بر این، تیم با چالش‌های دیگری هم مواجه بود. flakiness تست‌ها افزایش یافته بود. راه‌اندازی محیط تست overhead قابل‌توجهی داشت. هر اجرای CI چند دقیقه زمان سربار اضافه می‌کرد.

    پس از بررسی گزینه‌های موجود، تیم مهندسی به یک نتیجه روشن رسید. هیچ‌یک از راهکارهای شخص ثالث نیازهای پینترست را به‌طور کامل پوشش نمی‌دادند. این ابزارها پشتیبانی بومی مناسبی از emulator نداشتند. همچنین امکان کنترل دقیق orchestration و زمان‌بندی را فراهم نمی‌کردند.

    تولد PinTestLab؛ زیرساخت تست داخلی پینترست

    این تحلیل به توسعه یک پلتفرم داخلی به نام PinTestLab منجر شد. این زیرساخت emulatorهای اندروید را روی EC2 اجرا می‌کند. تیم با این سیستم کنترل کامل محیط اجرا، زمان‌بندی و orchestration را در اختیار دارد. PinTestLab وابستگی به سرویس‌های خارجی را نیز به‌طور قابل‌توجهی کاهش می‌دهد.

    نوآوری اصلی: شاردینگ مبتنی بر زمان اجرای واقعی

    هسته این تحول، یک الگوریتم شاردینگ آگاه از زمان اجرا است. این الگوریتم برخلاف روش‌های رایج مانند round-robin یا تقسیم ساده تست‌ها عمل می‌کند. سیستم از داده‌های تاریخی اجرای تست‌ها استفاده می‌کند. مدت‌زمان مورد انتظار هر تست از سیستم مدیریت تست پینترست (Metro) خوانده می‌شود.

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

    نتیجه این تغییرات چشمگیر بود. اختلاف بین سریع‌ترین و کندترین شارد از چند صد ثانیه به چند ده ثانیه کاهش یافت. زمان اجرای کندترین شارد حدود ۵۵ درصد کمتر شد. توسعه‌دهندگان نیز بازخورد CI را در هر بیلد حدود ۹ دقیقه سریع‌تر دریافت کردند.

    چرا داده‌های تاریخی نقش کلیدی دارند؟

    برخلاف توزیع مساوی تست‌ها بر اساس تعداد، این رویکرد از داده‌های واقعی استفاده می‌کند. سیستم emulatorها را همیشه مشغول نگه می‌دارد. tail latency کاهش می‌یابد و رفتار CI اندروید قابل پیش‌بینی‌تر می‌شود.

    در پیاده‌سازی، تیم از الگوریتمی ساده اما مؤثر استفاده کرد. این الگوریتم شامل مرتب‌سازی سبک، تخصیص greedy و استفاده از ساختار داده min-heap است. این ترکیب، تعادل خوبی میان کارایی عملی و سادگی محاسباتی ایجاد می‌کند.

    پایداری عملیاتی؛ اولویتی جدی

    منطق شاردینگ درون Buildkite اجرا می‌شود. اگر داده‌های Metro در دسترس نباشند، سیستم به‌صورت خودکار به round-robin fallback می‌کند. این تصمیم طراحی باعث شده CI حتی در شرایط اختلال زیرساختی هم پایدار باقی بماند.

    در آینده، تیم پینترست روی on-demand sharding، استفاده از message queue و اجرای granularتر تست‌ها تمرکز خواهد کرد. با این حال، راهکار فعلی بهترین نسبت کارایی به پیچیدگی را ارائه می‌دهد.

    این فقط پینترست نیست؛ یک روند عمومی در CI مدرن

    پینترست تنها شرکتی نیست که این مسیر را انتخاب کرده است. Dropbox با اجرای فقط تست‌های مرتبط با تغییرات، زمان CI اندروید را از ۷۵ دقیقه به ۲۵ دقیقه کاهش داد. Shopify با شاردینگ مبتنی بر داده‌های تاریخی، زمان CI را از ۴۵ دقیقه به ۱۱ دقیقه رساند. این شرکت اختلاف زمان شاردها را به کمتر از ۵ درصد کاهش داد.

    Square در ابزار داخلی Kochiku روی شاردینگ گسترده و زمان‌بندی هوشمند در مقیاس صدها شارد تمرکز دارد. پلتفرم‌هایی مانند Bitrise نیز با parallel execution آماده، کاهش زمان تست تا ۵۰ درصد را گزارش می‌کنند. Slack هم با حذف اجرای غیرضروری و reuse assetها، سرعت CI اندروید را تا ۵۰ درصد افزایش داده است.

    جمع‌بندی

    مطالعه موردی پینترست یک پیام روشن دارد. استفاده درست از داده‌های تاریخی می‌تواند CI اندروید را متحول کند. ترکیب داده‌های واقعی اجرا، حداکثرسازی parallelism و حذف کارهای غیرضروری، امروز به یک best practice در تیم‌های مهندسی پیشرو تبدیل شده است.

    راهکاری که پینترست پیاده‌سازی کرده، یک مدل عملی و قابل الگوبرداری است. این رویکرد به‌ویژه برای تیم‌هایی مفید است که با رشد تست‌ها از افزایش زمان CI اندروید رنج می‌برند؛ مشکلی رایج در توسعه نرم‌افزار مدرن.

    اگر به دنیای CI/CD علاقه دارید، می‌توانید مطالب این دسته‌بندی را در کدرزنیوز دنبال کنید.

  • تست End-to-End چیست و چرا برای اطمینان از عملکرد نرم‌افزار حیاتی است؟

    تست End-to-End چیست و چرا برای اطمینان از عملکرد نرم‌افزار حیاتی است؟

    اگر در تیم توسعه نرم‌افزار فعالیت کرده باشید، حتماً اصطلاح End-to-End Testing یا به اختصار E2E Testing را شنیده‌اید.
    تست End-to-End در واقع یکی از مراحل کلیدی کنترل کیفیت است که برای اطمینان از عملکرد صحیح کل نرم‌افزار از دید کاربر نهایی انجام می‌شود. این روش کمک می‌کند تا پیش از انتشار نسخه نهایی، از صحت تعامل بین ماژول‌ها، عملکردها و جریان کاری سیستم مطمئن شویم.

    در ادامه با تعریف، اهداف، زمان مناسب اجرا و ساختار تست E2E آشنا می‌شویم.

    تست End-to-End چیست؟

    تست End-to-End روشی است برای بررسی عملکرد نرم‌افزار از دید کاربر واقعی.
    در این نوع تست، مسیر واقعی استفاده از نرم‌افزار از ابتدا تا انتها شبیه‌سازی می‌شود تا اطمینان حاصل شود که همه بخش‌ها، از ماژول‌های ثبت‌نام و ورود گرفته تا تراکنش‌ها و گزارش‌ها، به درستی با هم کار می‌کنند.

    برای نمونه، در یک برنامه‌ی بانکی آزمایشی مثل Parabank، فرآیند تست End-to-End می‌تواند شامل مراحلی مانند ثبت‌نام، ورود به حساب، ایجاد حساب بانکی جدید، انجام تراکنش‌های مالی و مشاهده گزارش تراکنش‌ها باشد.
    این روند دقیقاً رفتار یک کاربر واقعی را بازسازی می‌کند و به تیم تست اجازه می‌دهد باگ‌ها و ناسازگاری‌های احتمالی را در جریان واقعی کار شناسایی کنند.

    هدف از اجرای تست End-to-End چیست؟

    هدف اصلی از اجرای تست E2E، اطمینان از عملکرد صحیح کل سیستم در شرایط واقعی است. در کنار آن، این تست به شناسایی خطاهای پنهان کمک می‌کند تا نرم‌افزار پیش از ورود به محیط عملیاتی، از نظر کارایی و تجربه کاربری تأیید شود.

    به‌عنوان مثال، در یک سامانه ثبت درخواست وام، تست E2E تضمین می‌کند که کاربر می‌تواند کل مسیر، از پر کردن فرم تا دریافت پاسخ نهایی را بدون خطا طی کند. علاوه بر بررسی عملکرد فنی، این نوع تست امکان دریافت بازخورد درباره تجربه کاربری (UX) را نیز فراهم می‌کند.

    چه زمانی باید تست End-to-End انجام شود؟

    معمولاً تست End-to-End پس از تکمیل تست‌های عملکردی (Functional) و سیستمی (System Testing) اجرا می‌شود. بهترین زمان برای اجرای آن، پیش از انتشار نسخه‌های اصلی نرم‌افزار است تا اطمینان حاصل شود سیستم از نگاه کاربر نهایی بدون مشکل کار می‌کند. همچنین توصیه می‌شود این تست‌ها در پایپ‌لاین CI/CD ادغام شوند تا فرآیند بررسی مداوم و دریافت بازخورد سریع‌تر صورت گیرد.

    استراتژی اجرای تست

    به گفته‌ی گوگل، ساختار تست‌ها باید به شکل یک هرم باشد:

    • ۷۰٪ تست واحد (Unit Test) برای بررسی بخش‌های مجزا
    • ۲۰٪ تست یکپارچگی (Integration Test) برای ارزیابی تعامل بین ماژول‌ها
    • ۱۰٪ تست End-to-End (E2E Test) برای اعتبارسنجی کل سیستم

    این ساختار باعث می‌شود تست‌ها سریع، قابل‌اعتماد و در عین حال جامع باشند.
    به‌طور خلاصه، تست‌های واحد پایه‌ی هرم را تشکیل می‌دهند، تست‌های یکپارچگی در لایه‌ی میانی قرار دارند و تست End-to-End در رأس هرم به‌عنوان نهایی‌ترین بررسی اجرا می‌شود.

    در بخش بعدی، مراحل سه‌گانه‌ی تست E2E شامل برنامه‌ریزی، اجرا و اختتام و همچنین یک نمونه عملی از تست API به‌صورت گام‌به‌گام توضیح داده می‌شود.

    مراحل مختلف تست End-to-End

    تست End-to-End معمولاً در سه مرحله اصلی انجام می‌شود:

    1. برنامه‌ریزی (Planning)
    2. تست (Testing)
    3. اختتام تست (Test Closure)

    در ادامه، هر مرحله را به‌طور مفصل بررسی می‌کنیم.

    برنامه‌ریزی (Planning)

    در مرحله برنامه‌ریزی، نکات زیر باید مدنظر قرار گیرند:

    • درک نیازمندی‌های کسب‌وکار و عملکردی نرم‌افزار
    • ایجاد برنامه تست بر اساس تحلیل نیازمندی‌ها
    • طراحی تست کیس‌های End-to-End

    تیم تست باید شناخت کاملی از نرم‌افزار پیدا کند و مسیرهای مختلف استفاده کاربر (Test Journeys) را در آن شناسایی نماید. این مسیرها باید از دید کاربر نهایی طراحی شوند و کل فرآیند از ابتدا تا انتها را پوشش دهند. مسیرهای موفق (Happy Paths) نیز باید مشخص و در طراحی تست کیس‌ها لحاظ شوند.

    به‌عنوان مثال، در یک نرم‌افزار فروشگاهی، یک مسیر تست ساده می‌تواند شامل اضافه کردن محصول به سبد خرید، خروج از حساب کاربری، ورود مجدد و ادامه فرآیند خرید از همان نقطه باشد.

    در مرحله برنامه‌ریزی همچنین باید به موارد زیر توجه شود:

    • راه‌اندازی محیطی مشابه محیط عملیاتی (Production-like Environment) برای شبیه‌سازی شرایط واقعی
    • تهیه داده‌های تست، استراتژی و طراحی تست کیس‌ها برای سناریوهای واقعی
    • تعریف معیارهای ورود و خروج (Entry & Exit Criteria) برای مشخص شدن اهداف تست End-to-End
    • بازبینی تست کیس‌ها، داده‌های تست و معیارهای ورود و خروج توسط تحلیل‌گر کسب‌وکار یا مالک محصول

    تست (Testing)

    مرحله تست شامل دو زیرمرحله اصلی است: پیش‌نیازها و اجرای تست.

    پیش‌نیازها (Prerequisites)

    در این مرحله باید اطمینان حاصل شود که:

    • توسعه تمامی ویژگی‌ها کامل شده است
    • همه زیرماژول‌ها و اجزای نرم‌افزار یکپارچه و به‌درستی کار می‌کنند
    • تست سیستم برای تمام زیرسیستم‌های مرتبط انجام شده است
    • محیط آزمایشی (Staging Environment) که شبیه محیط عملیاتی است، به‌طور کامل آماده باشد تا بتوان سناریوهای واقعی را شبیه‌سازی کرد

    پس از تکمیل پیش‌نیازها، می‌توان به مرحله اجرای تست رفت.

    اجرای تست (Test Execution)

    در این مرحله، تیم تست باید:

    • تست کیس‌ها را اجرا کند
    • در صورت بروز خطا، باگ‌ها را گزارش دهد
    • باگ‌های رفع شده را دوباره تست کند
    • تمام تست‌های End-to-End را مجدداً اجرا کند تا اطمینان حاصل شود همه چیز درست کار می‌کند

    تست‌های End-to-End می‌توانند به‌صورت دستی یا خودکار (Automation) در پایپ‌لاین CI/CD اجرا شوند. اجرای خودکار توصیه می‌شود، زیرا هم در زمان و تلاش تیم صرفه‌جویی می‌کند و هم کیفیت بالای نتایج را در کمترین زمان ممکن تضمین می‌کند.

    اختتام تست (Test Closure)

    در این مرحله، اقدامات زیر انجام می‌شود:

    • تحلیل نتایج تست‌ها
    • تهیه گزارش تست
    • ارزیابی معیارهای خروج (Exit Criteria)
    • اختتام رسمی تست

    مرحله اختتام شامل ثبت و مستندسازی نتایج تست‌ها و اطمینان از کامل بودن تحویل‌دادنی‌ها (Test Deliverables) است. همچنین شامل ارزیابی پوشش تست و مستندسازی نکات کلیدی مانند مشکلات شناخته شده می‌باشد.

    در نهایت، گزارش اختتام تست برای ذی‌نفعان آماده می‌شود که می‌تواند در جلسات تصمیم‌گیری Go/No-Go بسیار مفید باشد.

    مثال عملی تست End-to-End روی APIها

    برای درک بهتر، به یک نمونه عملی از APIهای RESTful در یک نرم‌افزار فروشگاهی آنلاین (E-commerce) می‌پردازیم. در این اپلیکیشن شش API اصلی وجود دارد:

    1. ایجاد توکن (Create Token): POST /auth
    2. افزودن سفارش جدید (Add Order): POST /addOrder
    3. دریافت سفارش (Get Order): GET /getOrder
    4. به‌روزرسانی سفارش (Update Order): PUT /updateOrder
    5. به‌روزرسانی جزئی سفارش (Partial Update Order): PATCH /partialUpdateOrder
    6. حذف سفارش (Delete Order): DELETE /deleteOrder

    آماده‌سازی قبل از تست

    قبل از شروع تست End-to-End روی این APIها، باید نیازمندی‌ها، الگوهای استفاده و مشخصات فنی آن‌ها تحلیل شوند. این اطلاعات برای طراحی تست کیس‌ها و استراتژی تست خودکار بسیار مفید هستند.

    براساس مستندات Swagger، نکات عملکردی هر API به شرح زیر است:

    • POST Add Order برای ایجاد سفارش جدید استفاده می‌شود و GET Order برای بازیابی سفارش با استفاده از شناسه سفارش (Order ID) کاربرد دارد.
    • Create Token توکن امنیتی ایجاد می‌کند که برای دسترسی به APIهای Update و Delete مورد نیاز است تا فقط کاربران ثبت‌شده بتوانند سفارش‌ها را ویرایش یا حذف کنند.
    • Update و Partial Update برای به‌روزرسانی سفارش‌ها استفاده می‌شوند.
    • Delete برای حذف سفارش کاربرد دارد.

    استراتژی تست End-to-End

    با توجه به مشخصات بالا، می‌توان مراحل زیر را برای تست End-to-End انجام داد:

    1. ایجاد توکن جدید با استفاده از POST /auth و ذخیره آن برای مراحل بعدی.
    2. ایجاد سفارش‌های جدید با استفاده از POST /addOrder.
    3. بازیابی سفارش‌های تازه ایجاد شده با استفاده از شناسه سفارش در GET /getOrder.
    4. به‌روزرسانی سفارش موجود با استفاده از توکن ذخیره شده و API PUT /updateOrder.
    5. بررسی عملکرد به‌روزرسانی جزئی با PATCH /partialUpdateOrder.
    6. حذف سفارش موجود با استفاده از DELETE /deleteOrder.
    7. برای اطمینان از حذف موفقیت‌آمیز سفارش، دوباره API GET /getOrder را فراخوانی کرده و دریافت کد وضعیت 404 نشان‌دهنده حذف شدن سفارش است.

    با این روش، تمام APIهای مهم در یک سناریوی واقعی تست می‌شوند و عملکرد آن‌ها از ابتدا تا انتها مورد بررسی قرار می‌گیرد.

    گسترش به اپلیکیشن‌های وب و موبایل

    مشابه این روش، می‌توان تست End-to-End را روی نرم‌افزارهای وب یا موبایل نیز انجام داد. نکته کلیدی این است که:

    • نرم‌افزار از دیدگاه کاربر نهایی ارزیابی شود.
    • سناریوهای مرتبط تست طراحی شوند.
    • سناریوها توسط تحلیل‌گر کسب‌وکار یا مالک محصول بازبینی شوند.

    جمع‌بندی

    تست End-to-End یک رویکرد جامع برای ارزیابی کل جریان کاری نرم‌افزار است که از ابتدا تا انتها، عملکرد تمام اجزای یکپارچه را بررسی می‌کند.

    این روش سناریوهای واقعی کاربر را شبیه‌سازی می‌کند و مشکلات موجود در ماژول‌های مختلف و وابستگی‌های آن‌ها را شناسایی می‌کند. نتیجه آن، تجربه کاربری روان و قابل اعتماد و کشف مشکلات قبل از مواجهه کاربران نهایی است.