فراتر از خروجیهای مفقود: ساخت یک Early Garbage Collector
سعی کردم از یک پلاگین استاندارد برای اصلاح لینکهای مفقود در مستندات Webpack استفاده کنم، اما شکست خورد.
ادغام یک پلاگین در یک کدبیس (codebase) بزرگ کار سادهای نیست. این موضوع به یک چالش معماری تبدیل شد. من مجبور بودم دستکاری Abstract Syntax Tree (AST)، مصرف حافظه و حلقههای بازگشتی (recursive loops) را مدیریت کنم.
مشکل پلاگینهای استاندارد
برای یافتن راه حل، سه آزمایش انجام دادم.
آزمایش ۱: پلاگین ۱۳۵ تایپ (type) را بازیابی کرد. با این حال، آنها را در یک ماژول داخلی قرار داد. ابزار ما یک پوشه جداگانه برای آنها ایجاد کرد و ما مجبور شدیم آنها را به صورت دستی مرتب کنیم. همچنین ساختار مستندات نیز اشتباه بود.
آزمایش ۲: تنظیمی را برای مخفی کردن تایپهای خارجی فعال کردم. این کار برای برخی موارد جواب داد و حجم دادهها (payload) را به ۶۰ تایپ Webpack کاهش داد. اما TypeDoc وابستگیهای تودرتو (nested dependencies) را نادیده گرفت. این باعث شد بسیاری از تایپها با وجود اینکه همچنان حافظه اشغال میکردند، مخفی باقی بمانند.
آزمایش ۳: سعی کردم تایپها را دوباره به ماژولهای اصلی خود نگاشت (map) کنم. این کار باعث ایجاد یک حلقه بازگشتی شد. پلاگین مدام به استخراج اینترفیسهای تودرتو ادامه داد تا اینکه ۶۳۰ تایپ ایجاد کرد. مستندات پر از نویز (noise) شد و تجربه کاربری را خراب کرد.
راه حل: Garbage Collection زودهنگام
من به تایپها در ماژولهای صحیح خود، بدون نویز اضافی نیاز داشتم. بنابراین به جای تلاش برای اصلاح خروجی، شروع به اصلاح فرآیند کردم.
من از هوکی به نام EVENT_RESOLVE_END استفاده کردم. این کار به من اجازه داد تا AST را دقیقاً پس از مرحله resolution رهگیری کنم. من این کار را قبل از اینکه TypeDoc دستهبندیها را اختصاص دهد یا حافظه زیادی مصرف کند، انجام دادم.
منطق من شامل سه مرحله بود:
- پیدا کردن ماژول داخلی در AST.
- بررسی هر گره (node) با استفاده از یک ابزار کمکی (utility) سفارشی.
- حذف نویز. من از
project.removeReflectionاستفاده کردم تا ۳۰۰ گره غیرضروری را بلافاصله حذف کنم. این کار به Node.js اجازه داد تا حافظه را آزاد کند. - انتقال تایپهای مهم. من ۳۰۰ تایپ باقیمانده را با اسکوپ (scope) اصلی ادغام کردم.
نتیجه: من ۲۴۰ تایپ حیاتی را نجات دادم. آنها اکنون قابل مشاهده هستند و مسیردهی درستی دارند. من از نویز بازگشتی آزمایش سوم جلوگیری کردم.
درسهای آموخته شده
• مدیریت زودهنگام ASTها. حذف گرههای غیرضروری از هدر رفتن حافظه در مراحل بعدی جلوگیری میکند. • استفاده از hookها به جای hackها. درک چرخه حیات (lifecycle) کامپایلر اجازه میدهد تا کار تمیزی انجام دهید. • بازخوردها باعث بهبود معماری میشوند. بررسیهای نگهدارنده (maintainer) به من کمک کرد تا سیستم بهتری بسازم. • دادهی بیشتر لزوماً بهتر نیست. مهندسی خوب یعنی یافتن تعادل بین داده و قابلیت استفاده (usability).