مدتی است که یک آزمایشگاه خانگی راه انداختهام و همزمان با کنترلگرهای ESP32، ماهوارهٔ صوتی Home Assistant، چند کانتینر Docker خودمیزبان و برنامههای Next.js سروکله میزنم. با این همه تجربه، خیال میکردم سامانههایم را خوب میشناسم؛ اما سخت در اشتباه بودم.
بخشهایی از برخی پروژههای ESPHome من درست کار نمیکردند و ماهها دنبال این باگها گشته بودم. بعد Claude Code را به کار گرفتم و باگهایی را پیدا کرد که خودم هرگز به آنها نمیرسیدم. علت ریشهای هرکدام آنقدر کوچک بود که آدم احساس میکرد به هوشش توهین شده است؛ بااینحال، پیدا کردن هر یک ساعتها و گاهی روزها زمان میبرد.
روزی بیست قطع اتصال وایفای؛ بیآنکه مقصر وایفای باشد
جستوجوی دوبارهٔ ESPHome برای نقطهٔ دسترسیِ ناموجود





با استفاده از یک ESP32 C3 Supermini، دستگاه کنترل تهویهای ۱۰دلاری ساختم که کولرهای آپارتمانم را کنترل میکرد. مشکل این بود که دستگاه روزی بیش از ۲۰ بار از شبکهٔ وایفای جدا میشد. همهچیز به مشکل سیگنال شباهت داشت؛ بنابراین ولتاژ باتری و منابع تغذیه را بررسی کردم، کابل USB را عوض کردم و حتی تنظیمات جداسازی کاربران در شبکهٔ مهمان را هم زیرورو کردم.
مقصر واقعی، قابلیت خودکار جابهجایی پس از اتصال در ESPHome بود؛ قابلیتی که مدام برای یافتن نقطهٔ دسترسی بهتر جستوجو میکند، حتی با اینکه در خانه فقط یک نقطهٔ دسترسی دارم. این قابلیت بیهیچ دلیلی اتصال کاملاً سالم را قطع میکرد و هر تلاش برای اتصال دوباره نیز با یک باگ قدیمی Auth Expired تداخل پیدا میکرد و چرخهای طوفانی از تلاشهای چنددقیقهای به راه میانداخت.
راهحل نهایی فقط افزودن دو خط YAML بود: فعال کردن RRM و BTM. اما یافتن آن به ثبت خروجی درگاه سریال نیاز داشت، چون API تاریخچهٔ Home Assistant فقط نشان میداد دستگاه قطع شده است و هرگز علت را مشخص نمیکرد.
یک خودکارسازی معیوب و چهار علت ریشهای نامرتبط
دامنهٔ اشتباه، پیشوند تکراری، محرک قدیمی و نویسهٔ خط جدید اضافی

دستگاه کنترل تهویهٔ من به خودکارسازی متناظری وابسته است که در Home Assistant ساخته بودم؛ اما این خودکارسازی هیچوقت کار نکرد و برای فهمیدن علتش ناچار شدم چهار لایهٔ جداگانه را کنار بزنم. نخست، جستوجویی نوشته بودم که انتظار داشت در Home Assistant دامنهای به نام test_sensor وجود داشته باشد. اما چون این نامگذاری فقط قراردادی داخلی در ESPHome است، موجودیت در واقع زیر دامنهٔ sensor قرار داشت و جستوجوی من هر بار بیسروصدا به هیچ نتیجهای نمیرسید.
دوم، ناخواسته با تکرار نام دستگاه در فیلد نام خود حسگر، پیشوند شناسهٔ یک موجودیت را دو بار افزوده بودم. در نتیجه، رشته بهجای آنکه درست و تمیز یکپارچه شود، به عبارتی تبدیل میشد که با هیچچیز مطابقت نداشت. سوم، محرک numeric_state فقط هنگام عبور از یک آستانه فعال میشود، نه وقتی مقدار از پیش از آن گذشته باشد؛ بنابراین جابهجایی به اتاقی که دمایش از قبل بالاتر بود، هیچ واکنشی ایجاد نمیکرد.
و سرانجام، یک بلوک شرطی چندخطی Jinja نویسهٔ خط جدیدی را به انتهای رشتهٔ شناسهٔ موجودیت نشت میداد؛ در نتیجه، دو رشتهٔ نادرست با هم مقایسه میشدند و تطبیق شکست میخورد. درست وقتی فکر میکردم همهچیز را برطرف کردهام، فهمیدم نسخهٔ فعال خودکارسازی در رابط کاربری Home Assistant بهصورت دستی جایگذاری شده و هرگز بهروز نشده است؛ بنابراین هرچه به مخزنم میفرستادم، باز هم همان منطق قدیمی و معیوب اجرا میشد.
دستیار صوتیای که ناشنوا شد و بعد با خودش حرف زد
سه خرابیِ رویهمافتاده در یک برد





دستگاه دیگری که اشکالزداییاش برایم به کابوس تبدیل شد، ماهوارهٔ صوتی Home Assistant روی یک ESP32-S3 بود؛ دستگاهی که سه خرابی جداگانه بهطور همزمان در آن رخ داده بود. پس از آنکه در هر بار راهاندازی دقیقاً یک بار واژهٔ بیدارباش را میشنید، کاملاً ناشنوا میشد. رفع این مشکل به اتصال صریح میکروفون و ترتیب راهاندازی بسیار مشخصی نیاز داشت.
پس از رفع آن مشکل، دستگاه تقریباً هر پنجونیم ثانیه در حلقهای بیپایان با خودش حرف میزد؛ چون کدی که پایان نشست را مدیریت میکرد، فرمان توقف اجباری خود دستگاه را بهاشتباه نشانهٔ تمام شدن جملهٔ کاربر میپنداشت و دوباره میکروفون را باز میکرد. برای رفع این مشکل باید یک پرچم suppress_follow_up اضافه میشد.
آخرین خرابی، یعنی سکوت کامل، با مسیری حل شد که بیش از همه غرورم را شکست: تمام YAMLهایم را کنار گذاشتم، آزمایش تولید صدای سادهای را مستقیماً روی Arduino اجرا کردم و با مولتیمتر پایههای تقویتکننده را سنجیدم. همانجا فهمیدم یکی از اتصالهای جامپر شل است و فشار کوچک پراب موقتاً آن را درست میکند. حدس خودم را هم که میگفت تعویض واژهٔ بیدارباش مشکل را حل میکند، نادیده گرفته بودم؛ چون هنگام آزمایش، درست وقتی یکی از فایلهای مدل واژهٔ بیدارباش خراب بود، دستگاه واژهٔ بیدارباش را عوض کرده بود.
فرمان npm که بیسروصدا پایگاه داده را از کار میانداخت
npm swallows SIGTERM. Next.js doesn't get the memo
برنامهٔ Next.js خودمیزبان من که از PGlite بهره میبرد، در هر تلاش برای ورود با خطای RuntimeError: Aborted() روبهرو میشد و پوشهٔ دادهها نیز دیگر بازیابیپذیر نبود. سرانجام علت به یک خط در Dockerfile من رسید: اجرای npm start بهجای اجرای مستقیم next start.
NPM swallows and delays SIGTERM signals to its child process, so every container restart was killing PGlite mid-write with zero graceful shutdown. Swapping to next start as PID 1 and adding a proper SIGTERM handler solved it permanently.
راهحل یک باگ، پاسخی اشتباه برای باگی دیگر شد
یک سیم شل، نه ناهماهنگی عمق بیت

یکی دیگر از باگهایی که بیشتر به سختافزار مربوط میشد و Claude Code در یافتن علت آن کمکم کرد، صدای درهمریختهٔ پروژهٔ هدست هدایت استخوانیام بود. گمان میکردم مشکل از ناهماهنگی عمق بیت میان میکروفون و تقویتکنندهای است که به کار میبردم.
درست مانند مشکل جامپرِ دستیار صوتی، مشکل واقعی شل بودن سیم میکروفون روی بردبورد سفارشی و چاپ سهبعدیشدهام بود؛ دادهٔ خام صوتی هم که تقریباً سراسر از مقادیر کران تغذیه تشکیل شده بود، همین را نشان میداد. پس از جا زدن دوبارهٔ سیم، یک آزمون A/B ثابت کرد که ناهماهنگی عمق بیت در عمل هیچ اهمیتی نداشته است و قاعدهای را که تا آن زمان حقیقتی بیچونوچرا میپنداشتم، رد کرد.
کلود کد حدس نزد؛ فقط دقیقتر نگاه کرد
وقتی باگ ناممکن به نظر میرسد، نخست سادهترین فرض را بیازمایید
وقتی علت واقعی آشکار شد، هیچیک از این راهحلها به کشفی نبوغآمیز نیاز نداشتند؛ و دقیقاً همین موضوع آنها را تا این اندازه آزاردهنده میکرد. تغییری دوخطی در Wi-Fi، یک نویسهٔ خط جدید نامرئی، جامپری شل و یک فرمان نادرست در Docker، همگی خرابیهایی پدید آورده بودند که بسیار بزرگتر از واقعیت به نظر میرسیدند.
Claude Code بهشکلی جادویی جای آزمایش یا عیبیابی سختافزار را نگرفت، اما در به چالش کشیدن فرضهای من، دنبال کردن رفتارهای عجیب در سراسر کد و آشکار کردن ارتباطهایی که از چشمم دور مانده بودند، بهطرز چشمگیری توانمند بود. مهمترین درس ساده است: وقتی باگی ناممکن به نظر میرسد، به آنچه ساختهاید اعتماد نکنید و نخست کوچکترین فرضها را بیازمایید.
منبع: این مطلب ترجمه و بومیسازی مقالهای از MakeUseOf به قلم Yadullah Abidi است. مشاهده مقاله اصلی