pipeline ما هر push حدود ۱۲ دقیقه طول می‌کشید. تیم کم‌کم از CI/CD فاصله می‌گرفت چون feedback loop خیلی کند بود. بیشتر وقت صرف npm install و docker build از صفر می‌شد — هر بار بدون استثنا.

اول dependency cache را جدا کردیم. با actions/cache و کلید بر اساس hash فایل package-lock.json، npm ci از حدود ۳ دقیقه به ۲۰ ثانیه رسید. ساده به نظر می‌رسد، ولی بیشترین impact را همین مرحله داشت.

بعد نوبت Docker بود. از docker/build-push-action با cache-from و cache-to استفاده کردیم و registry را به عنوان cache backend گذاشتیم. layerهای base image و dependency install دیگر هر بار rebuild نمی‌شدند.

نتیجه نهایی: از ~۱۲ دقیقه به ~۲.۵ دقیقه — حدود ۸۰٪ کاهش. تیم دوباره هر commit را push می‌کرد چون منتظر نمی‌ماند.

نکته‌ای که دیر فهمیدم: cache key باید بر اساس lockfile باشد، نه branch — وگرنه cache بی‌ربط hit می‌شود و باگ عجیب می‌گیرید. یک بار branch feature قدیمی cache مربوط به main را hit کرد و نسخه dependency اشتباه deploy شد.

cache را فقط برای speed نگه ندار — invalidation strategy هم مهم است. ما یک workflow دستی داریم برای «purge cache» وقتی مشکوک می‌شویم. ارزان است، یک بار جان نجاتمان داد.

یک اشتباه رایج دیگر: cache کردن node_modules بدون restore-keys درست. اگر key دقیق match نکند و fallback نداشته باشی، عملاً از صفر شروع می‌کنی و فکر می‌کنی cache کار می‌کند چون step سبز است.

حالا هر pipeline گزارش کوتاهی از cache hit rate چاپ می‌کند. اگر hit rate زیر ۷۰٪ برود، یعنی چیزی در key strategy یا ساختار layerها خراب شده. اندازه‌گیری نکن، حدس بزن — این شعار pipeline ما شده.