تصور کنید یک دستور SQL اشتباه در دیتابیس تولیدی، تمام دادههای مشتریان شما را در یک لحظه پاک کند؛ کابوسی که هر برنامهنویسی از آن میترسد. برای جلوگیری از این اتفاق، یک توسعهدهنده شش ماه وقت صرف ساخت «پشته حفاظتی» (Guard Stack) کرد تا Claude Code بتواند تغییرات ساختاری را پیشنهاد دهد، اما هیچ دسترسی مستقیمی برای اجرای آنها روی دیتابیس اصلی نداشته باشد. این سیستم اجازه میدهد مدل پیشنویسها و تستهای تغییرات شمای دیتابیس را انجام دهد، در حالی که هرگونه قدرت واقعی برای اعمال این تغییرات روی محیط Production از عامل گرفته شده است.
مهاجرتهای دیتابیس (Database Migrations) بهشدت خطرناک هستند، چون یک کد میتواند از نظر دستوری (Syntactically) درست باشد اما در عمل فاجعهبار. هوش مصنوعی زاینده (Generative AI) — مثل یک دستیار بسیار سریع اما بیسابقهای که گاهی جزئیات حیاتی را فراموش میکند — بهراحتی دستور ALTER TABLE مینویسد، اما اثر آن روی جدولی با میلیونها ردیف را نادیده میگیرد. این موضوع یک گلوگاه ایجاد میکند که در آن توسعهدهندگان مجبورند تمام مهاجرتها را دستی بنویسند، زیرا میترسند یک «اشتباه کوچک» از سوی عامل، یک جدول حساس و پرتردد (Hot Table) را برای چندین دقیقه قفل کند.
همانطور که در تحلیلهای قبلی ما دربارهی امنیت مدلهای عاملمحور اشاره کردیم، مشکل اصلی نبودِ توانایی کدنویسی نیست، بلکه نبودِ زیرساختی برای کنترل خروجیهاست. در این راستا، شناسایی نقاط ضعف این سیستمها اهمیت دارد؛ شما میتوانید راهنمای چهارمرحلهای برای یافتن نقطه شکست عاملهای هوش مصنوعی را برای درک بهتر نحوه عیبیابی این خطاها مطالعه کنید. در یک مورد واقعی، این توسعهدهنده اشاره کرد که حتی مهاجرتهای دستنویس خودش هم لزوماً ایمن نبودند. او once یک ایندکس را بدون کلمه کلیدی CONCURRENTLY ایجاد کرد که باعث قفل شدن یک جدول پرتردد در PostgreSQL 16 برای ۴۰ ثانیه شد. این سابقه، استدلالی برای اتوماسیون شد: هدف این نبود که صرفاً بپرسیم «آیا کلود میتواند SQL بنویسد» — چرا که این کار برای مدل بسیار ساده است — بلکه هدف ساخت زیرساختی بود که SQLهایی را که از نظر دستوری کامل اما از نظر عملیاتی خطرناک هستند، شناسایی کند.
به نقل از یک راهنمای فنی که در ۱۰ اوت ۲۰۲۶ در سایت dev.to منتشر شد، راهکار این است که با عامل هوش مصنوعی به عنوان یک «پیشنویسکننده» (Drafter) رفتار شود، نه یک «اپراتور». عامل آزادی کامل دارد تا تغییرات را پیشنهاد دهد، اما پیش از آنکه یک انسان درخواست تغییر (Pull Request) را ببیند، باید از یک مسیر سختگیرانه شامل سه لایه بررسی خودکار عبور کند. اصل طراحی بسیار ساده است: آزادی کامل برای پیشنویس، توانایی صفر برای اجرا.
لایه اول: اجرای آزمایشی در دیتابیس سایه
هر مهاجرت ابتدا روی یک دیتابیس سایه (Shadow Database) اجرا میشود. این دیتابیس یک شمای خالی نیست، بلکه یک نمونه موقت (Throwaway) از Postgres است که با یک اسنپشات بینام شده (Anonymized) از دادههای واقعی تولیدی تغذیه شده است. این تمایز بسیار حیاتی است؛ دیتابیسهای خالی مشکلاتی مانند قفل شدن (Locking) که در مقیاس واقعی دادهها رخ میدهد را آشکار نمیکنند. یک دیتابیس خالی بهراحتی مهاجرتی را میپذیرد که در واقعیت ۴۵ دقیقه زمان میبرد و نیاز به یک قفل انحصاری (Exclusive Lock) روی دادههای واقعی دارد.
سیستم از یک اسکریپت Bash خاص به نام migration-dryrun.sh برای نظارت بر عملکرد استفاده میکند. این اسکریپت توسط عامل از طریق یک هوک (Hook) اجرا میشود و هرگز روی محیط تولید اجرا نمیشود. این اسکریپت از یک کانتینر Docker که نسخه postgres:16 را اجرا میکند استفاده کرده و آخرین دامپ بینام شده را بازیابی میکند. برای اندازهگیری رفتار واقعی، اسکریپت محدودیت lock_timeout = '2s' را پیادهسازی میکند:
- اسکریپت یک تایمر را با استفاده از دستور
date +%sشروع میکند. - فایل SQL را اجرا کرده و در همان حال زمان انتظار برای قفل را روی ۲ ثانیه تنظیم میکند.
- هرگونه خطا را ثبت کرده و مجموع ثانیههای سپری شده را خروجی میدهد.
اگر مهاجرت نتواند در کمتر از دو ثانیه قفلهای مورد نیاز خود را در دیتابیس سایه به دست آورد، با صدای بلند شکست میخورد (Fail Loudly). این اتفاق Claude Code را مجبور میکند تا مهاجرت را با استراتژیهای ایمنتر، مانند CREATE INDEX CONCURRENTLY یا بهروزرسانیهای دستهای (Batched Backfills) بازنویسی کند. چون این فرآیند به صورت یک هوک تعریف شده، هر فایلی که در پوشه migrations/ نوشته شود، بهطور خودکار اجرای آزمایشی را فعال کرده و لاگها مستقیماً به کانتکست عامل بازگردانده میشوند. عامل شکست خود را میبیند، پیش از آنکه توسعهدهنده اصلاً درخواست Pull Request را مشاهده کند.
لایه دوم: تایید بازگشتپذیری
هر مهاجرت باید همراه با یک اسکریپت بازگشتی (Down script) فعال باشد و «فعال بودن» آن بهجای ادعای مدل، از طریق یک فرآیند خودکار سختگیرانه تأیید میشود. خط لوله (Pipeline) این کار را از طریق فرآیند Diff کردن شما (Schema Diff) بررسی میکند:
- وضعیت اولیه: سیستم از شمای دیتابیس سایه با دستور
pg_dump --schema-onlyیک خروجی میگیرد تا فایلbefore.sqlایجاد شود. - اجرا: ابتدا فایل
migration_up.sqlاجرا میشود و سپس بلافاصله فایلmigration_down.sqlاجرا میگردد. - وضعیت نهایی: سیستم دوباره از شما خروجی میگیرد تا فایل
after.sqlایجاد شود. - مقایسه: یک دستور
diffبین این دو دامپ اجرا میشود. اگر خروجی خالی نباشد، مهاجرت شکست میخورد.
در مراحل اولیه پیادهسازی، این بررسی در حدود یکسوم (۳۳٪) از پیشنویسهای مدل شکست میخورد. کلود تمایل داشت اسکریپت «Up» بنویسد که سه اقدام را انجام میدهد، اما اسکریپت «Down» را طوری بنویسد که فقط دو مورد از آنها را لغو کند. پس از افزودن خروجی Diff به حلقه بازخورد عامل، نرخ شکست تقریباً به صفر رسید. عامل بهطور خودگردان یاد گرفت که ابتدا اسکریپت Down را بنویسد و سپس اسکریپت Up را از روی آن استخراج کند تا تطابق کامل تضمین شود.
لایه سوم: بررسی تعداد ردیفها و Checksum
مخوفترین خطاها، مهاجرتهای دادهای هستند که بدون تغییر در ساختار (Schema)، ردیفها را بهطور مخفیانه حذف میکنند. برای جلوگیری از این اتفاق، سیستم قبل و بعد از اجرای سایه، از SELECT count(*) برای هر جدول با استفاده از pg_stat_user_tables (بهطور خاص بررسی relname و n_live_tup) اسنپشات میگیرد. همچنین یک Checksum روی کلیدهای اصلی (Primary Keys) برای هر جدولی که مهاجرت با آن درگیر است، محاسبه میکند.
هرگونه کاهش در تعداد ردیفها باعث شکست خودکار میشود، مگر اینکه عامل یک یادداشت خاص را در داخل فایل SQL بگنجاند: -- EXPECTED_ROW_LOSS.
برای مثال، مهاجرتی که مطابق با سیاست نگهداری دادهها، رکوردهای قدیمیتر از ۹۰ روز را حذف میکند، به این شکل نوشته میشود:-- EXPECTED_ROW_LOSS: sessions (deleting rows older than 90 days per retention policy)DELETE FROM sessions WHERE created_at < now() - interval '90 days';
این یادداشت برای ماشین نیست، بلکه برای بازبین انسانی است. این کار عامل را مجبور میکند قصد خود را دقیقاً در جایی که بازبین در حال نگاه کردن است بیان کند و ابهام را در مورد اینکه آیا از دست رفتن دادهها عمدی بوده یا اتفاقی، از بین میبرد.
بررسی یک مورد بحرانی: نجات از توقف دیتابیس
ارزش این پشته حفاظتی دو ماه پس از استقرار ثابت شد، زمانی که از عامل خواسته شد ستون users.locale را به حالت NOT NULL با مقدار پیشفرض 'en' تغییر دهد. پیشنویس اولیه از نظر کتابی کاملاً درست بود:UPDATE users SET locale = 'en' WHERE locale IS NULL;ALTER TABLE users ALTER COLUMN locale SET NOT NULL;
برای یک بازبین انسانی، این کد کاملاً معتبر به نظر میرسد. با این حال، اجرای سایه یک نقص عملیاتی بحرانی را آشکار کرد: دستور UPDATE حدود ۱.۸ میلیون ردیف را در یک تراکنش واحد بازنویسی میکرد. این کار باعث تورم (Bloat) جدول شد و قفلهای ردیفی (Row Locks) را برای بیش از ۳۰ ثانیه نگه داشت. در یک محیط تولیدی با ترافیک بالا، این اتفاق باعث ایجاد توده عظیمی از Timeoutهای قفل در شلوغترین جدول سیستم میشد.
Claude Code پس از مطالعه لاگ اجرای آزمایشی خود، بهطور خودگردان مهاجرت را به یک حلقه بهروزرسانی دستهای (Batched Backfill Loop) تغییر داد تا تراکنشها کوتاه بماند و به autovacuum اجازه دهد همگام با تغییرات پیش برود:
- پیشنویس جدید از یک بلوک
DO $همراه با یک حلقه استفاده کرد. - ردیفها را در دستههای ۵۰۰۰ تایی با استفاده از
WHERE id IN (SELECT id FROM users WHERE locale IS NULL LIMIT 5000)بهروزرسانی کرد. - از
GET DIAGNOSTICSبرای ردیابی ردیفهای بهروزرسانی شده و ادامه حلقه تا تکمیل عملیات استفاده کرد. - در نهایت با دستور
ALTER TABLEکار را به پایان رساند.
این نتیجه یک بینش کلیدی را برجسته کرد: لایه حفاظتی چیزی را شکار کرد که چشم یک بازبین انسانی احتمالاً از آن میگذشت. پشته حفاظتی فقط «چرخ کمکی» برای عامل نیست؛ بلکه زیرساختی است که توسعهدهنده را هم از عامل و هم از خودش محافظت میکند.
درسهای عملیاتی
این رویکرد تمرکز را از «بازبینی کد» به «حسابرسی رفتار» تغییر میدهد. بازبینی انسانی معمولاً اشتباهات سطح سینتکس را میگیرد، اما تنها اجراهای سایه با دادههایی به شکل دادههای تولیدی است که باگهای عملیاتی باعث توقف سیستم (Downtime) را شناسایی میکنند. این موضوع تأکیدی است بر این واقعیت که تست روی دیتابیسهای خالی، بدتر از نبودِ تست است — زیرا حس امنیت کاذب میدهد و چیزی را ثابت نمیکند. خط لوله اسنپشاتهای بینام ۷۰٪ از کارهای آمادهسازی را به خود اختصاص داد اما ۹۵٪ از ارزش سیستم را به ارمغان آورد.
امنیت با جداسازی سختگیرانه اعتبارنامهها (Credentials) تامین شده است. عامل هرگز اعتبارنامههای تولیدی را دریافت نمیکند، حتی دسترسی Read-only. برای بررسی عمیقتر روشهای ایزولهسازی، میتوانید مقاله فایلهای تله در برابر محیطهای ایزوله را بخوانید که به سنجش واقعی امنیت سیستمفایل در محیطهای ایزوله میپردازد. پیکربندی Claude Code فقط رشته اتصال (Connection String) برای نمونه سایه را فراهم میکند؛ DSN تولیدی در مکانی ذخیره شده که عامل قادر به خواندن آن نیست و بدین ترتیب شعاع تخریب (Blast Radius) یک خطای پرامپت بهطور ساختاری صفر میشود.
یافته کلیدی دیگر این است که حلقههای بازخورد رفتاری بسیار برتر از دستورالعملهای استاتیک هستند. توسعهدهنده تنها ده خط راهنما درباره مهاجرتها در دستورات عامل نوشت. تغییر رفتاری واقعی از طریق انتقال شکستهای لایههای حفاظتی (مانند شکست diff before.sql after.sql) به کانتکست عامل اتفاق افتاد. عامل از طریق یک فرآیند تکرارشونده یاد گرفت: عاملی که به او گفته شود «مهاجرتهای بازگشتپذیر بنویس» احتمالاً تا سهشنبه این را فراموش میکند، اما عاملی که میبیند مهاجرتش در تست Diff شکست میخورد، واقعاً کد بازگشتپذیر خواهد نوشت.
در نهایت، این زیرساخت استانداردهای خود توسعهدهنده را نیز بالا برد. پیش از این، بسیاری از مهاجرتهای دستی فاقد اسکریپتهای Down یا تحلیل قفل بودند. ساخت دروازههایی که به اندازه کافی سختگیر باشند تا بتوان به یک هوش مصنوعی اعتماد کرد، باعث رسمی شدن بررسیهایی شد که توسعهدهنده باید از ابتدا روی کدهای خودش هم اجرا میکرد.
تکرارهای آینده
در حال حاضر، لایههای حفاظتی مهاجرت را در برابر یک اسنپشات استاتیک میسنجند. گام بعدی، اندازهگیری عملکرد در برابر ترافیک واقعی از طریق بازپخش (Replay) نمونهای از بارهای پرسوجوی تولیدی روی دیتابیس سایه است. این کار اجازه میدهد تداخلهای قفل (Lock Contention) بهجای اینکه فقط عددی در یک لاگ باشند، به شکل پرسوجوهای شکستخورده ظاهر شوند.
علاوه بر این، توسعهدهنده در حال آزمایش این است که اجازه دهد عامل برنامههای استقرار ساختاریافته — مانند مراحل Expand/Contract برای تغییرات بدون توقف (Zero-downtime) — را پیشنهاد دهد، بهجای اینکه فقط SQL خام بنویسد. همچنین برنامههایی برای بررسی پیچیدگیهای خط لوله اسنپشاتهای بینام، بهخصوص «سوراخ خرگوشی» پاکسازی اطلاعات شناسایی شخصی (PII) در حالی که توزیعهای واقعگرایانه دادهها حفظ شود، وجود دارد.
گام بعدی شما
- اگر از عاملهای کدنویسی استفاده میکنید، دسترسی آنها را از محیط Production کاملاً ببرید و یک محیط Sandbox با دادههای واقعی (اما بینام) بسازید.
- برای هر تغییر دیتابیس، تست بازگشتپذیری (Up/Down) را به صورت خودکار در CI/CD قرار دهید.
- بهجای نوشتن پرامپتهای طولانی برای رعایت استانداردها، خروجی تستهای شکستخورده را به عنوان بازخورد به مدل برگردانید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو