یک کوئری اشتباه در کنترلر یا یک موجودیت (Entity) تعریفنشده در پایگاهداده میتواند هش رمزهای عبور در پاسخهای JSON لو بدهد. اگرچه عاملهای کدنویس (AI Coding Agents) در نوشتن نحو NestJS مهارت دارند، اما در حدس زدن قراردادهای خاص هر پروژه بهشدت ضعیف هستند و اغلب مدیریت خطاهای متناقض ایجاد کرده یا بدون اجازه، مهاجرتهای (Migrations) خطرناک اجرا میکنند. این ابزارها وقتی رها شوند، کوئریها را مستقیماً در کنترلرها مینویسند، موجودیتها را بدون فیلتر از دیتابیس برمیگردانند و با خوشبینی تمام، دستور migration:run را روی هر دیتابیسی که در فایل .env باشد اجرا میکنند.
همانطور که در تحلیل قبلی ما دربارهی چرخش تمرکز توسعه از پرامپت به معماری سیستم اشاره کردیم، مشکل دیگر کیفیت پرامپت نیست. این تغییر رویکرد را در بررسی ما دربارهی گذار از پرامپت به مهندسی بهطور مفصلتر تحلیل کردهایم. راهکار در ایجاد مخزنی است که قوانین خود را در جایی که عاملها آنها را میخوانند بیان کند و تغییرات صحیح را برای تأیید ارزان و سریع کند. بر اساس راهنمای عملی منتشر شده در ۱۷ اکتبر ۲۰۲۶، هدف باید تغییر از «توصیههای استایلی» به «قوانین توقف» (Stop Rules) باشد؛ قوانینی که عاملها نتوانند از آنها عبور کنند.
منبع واحد حقیقت (The Single Source of Truth)
برای جلوگیری از پراکندگی دستورات (Instruction Drift)، توسعهدهندگان باید دقیقاً یک فایل دستورالعمل داشته باشند. در حال حاضر، هر عامل اصلی یک فایل دستورالعمل پروژه را میخواند: Codex، Cursor، GitHub Copilot و Gemini CLI (از طریق یک تنظیم خاص) فایل AGENTS.md را میخوانند، در حالی که Claude Code فایل CLAUDE.md را بررسی میکند.
استراتژی بهینه این است که فایلهای اختصاصی هر ابزار، صرفاً فایل اصلی AGENTS.md را فراخوانی کنند. برای مثال، فایل CLAUDE.md باید از @AGENTS.md استفاده کند و فقط مهارتهای خاص کلود یا قوانین دسترسی او را لیست کند. برای Gemini، فایل .gemini/settings.json باید با مقدار "context": { "fileName": ["AGENTS.md"] } پیکربندی شود.
این ساختار تضمین میکند که تغییر یک قانون در یک نقطه، بهطور همزمان روی تمام عاملها اعمال شود. این موضوع حیاتی است زیرا برخی ابزارها، مانند Copilot CLI، هر فایل دستورالعملی را که پیدا کنند بدون ترتیب اولویت با هم ادغام میکنند. فایل AGENTS.md را کوتاه نگه دارید و بر روی نقشه پروژه، دستورات، قراردادها، قوانین ایمنی و فرمت گزارش تمرکز کنید و برای جزئیات عمیقتر به پوشه docs/ لینک بدهید.
پیادهسازی قوانین توقف (Stop Rules)
توصیههای کلی مثل «کد تمیز بنویس» بیاثر هستند. بهجای آن، راهنما پیشنهاد میکند از «قوانین توقف» صریح با استفاده از دقیقاً همان زبانی که عامل باید برگرداند استفاده شود. آنچه رفتار مدل را تغییر میدهد، لیستی کوتاه از کارهایی است که عامل «نباید» انجام دهد.
- ایمنی مهاجرتها: عاملها باید از تولید، اجرا یا بازگرداندن (Revert) مهاجرتها منع شوند، مگر اینکه در گفتگوی جاری صراحتاً به آنها دستور داده شود. تغییر در یک موجودیت (Entity) به معنای اجازه برای مهاجرت نیست. وقتی تغییری در طرح (Schema) شناسایی شد، عامل باید گزارش دهد: «تغییر طرح شناسایی شد. مهاجرت لازم است. بدون تأیید صریح، ایجاد یا اجرا نشد».
- دادههای اولیه (Seed Data): عاملها نباید بدون دستور صریح کاربر، Seedها را اجرا یا ویرایش کنند.
- فرمتبندی (Formatting): عاملها را از فرمتبندی کد منع کنید. از آنجایی که قلابهای پیش از کامیت (Pre-commit hooks) از طریق Husky و lint-staged و همچنین خط لولههای CI فرمتبندی را مدیریت میکنند، ممنوع کردن این کار باعث صرفهجویی در توکنهای عامل و جلوگیری از ایجاد Diffهای مزاحم در کنترل نسخه میشود.
معماری خستهکننده و قوانین وابستگی
عاملها هر چه ببینند کپی میکنند. با ایجاد یک ساختار «خستهکننده» (Boring Architecture) و صریح، توسعهدهندگان میتوانند عاملها را به یک الگوی پیشبینیپذیر مجبور کنند:
- کنترلرها (Controllers): فقط مدیریت مسیریابی HTTP، گاردها و دکوراتورهای Swagger.
- سرویسها (Services): حاوی قوانین کسبوکار؛ هر کلاس برای یک مسئولیت واحد.
- DTOها: مدیریت اعتبارسنجی درخواستها (با استفاده از
class-validatorو محدودیتهای طول رشته) و نگاشت پاسخها. - موجودیتها (Entities): تعریف مدل داده؛ اینها نباید حاوی هیچ منطق کوئری، HTTP، ایمیل یا JWT باشند.

جریان وابستگی باید کاملاً خطی باشد: کنترلر $\rightrightarrows$ سرویس $\rightrightarrows$ مخزن (Repository) $\rightrightarrows$ موجودیت (Entity). سایر ماژولها باید سرویس مالک را تزریق کنند و هرگز مستقیماً از مخزن ماژول دیگری استفاده نکنند.
برای جلوگیری از نشت امنیتی، راهنما چندین قانون «هرگز» را الزامی میکند: پاسخها باید موجودیتها را از طریق ResponseDto.fromEntity() نگاشت کنند تا تضمین شود فیلدهای داخلی دیتابیس (مانند هش رمز عبور) هرگز به کلاینت نرسند. دادههای متعلق به کاربر باید با استفاده از ID احراز هویت شده از @CurrentUser() جستجو شوند و هرگز نباید تنها بر اساس ID ارسالی در درخواست (Request) باشد. در نهایت، کلاینتها هرگز نباید بتوانند فیلدهای تحت مالکیت سرور مانند وضعیت (Status)، نقشها (Roles) یا وضعیت تأیید (Verification State) را تنظیم کنند. این موضوع بهصورت مکانیکی توسط یک ValidationPipe جهانی با تنظیمات whitelist: true و forbidNonWhitelisted: true پشتیبانی میشود.
استانداردسازی شکست (Standardizing Failure)
برای جلوگیری از اینکه عاملها روش چهارمی برای پرتاب خطا ابداع کنند، معماری نیازمند یک قرارداد واحد برای خطاهاست. این کار با اکسپورت کردن یک کلاس انتزاعی AppException که از Error ارثبری میکند برای مدیریت وضعیت (Status) و کدهای پایدار محقق میشود، همراه با پیادهسازیهای خاص مانند NotFoundError و ConflictError.
این رویکرد در راستای استفاده از قراردادهای JSON برای جلوگیری از خطاهای خاموش است تا خروجیهای عاملها برای سیستمهای نظارتی قابل پیشبینی باشد.
مثال از نحوه استفاده: throw new ConflictError('EMAIL_ALREADY_REGISTERED', 'Email is already registered');
یک فیلتر استثنای جهانی (Global Exception Filter) هر خطایی، حتی خطاهای غیرمنتظره را به یک شکل JSON ثابت تبدیل میکند:{ "statusCode": 409, "code": "EMAIL_ALREADY_REGISTERED", "message": "...", "path": "/api/v1/auth/register", "requestId": "6f1c...", "timestamp": "..." }
کدهای پایدار با فرمت UPPER_SNAKE به هر دو بخش عاملها و فرانتاند یک قرارداد قابل اتکا برای Assert کردن میدهد و فیلتر تضمین میکند که هیچ جزئیاتی از SQL، Stack Trace یا SMTP به کلاینت نشت نکند.
تأیید بدون زیرساخت (Infrastructure-Free Verification)
عاملها اغلب تستهایی که نیاز به Docker یا دادههای Seed دارند را بهدلیل پیچیدگی تنظیمات رد میکنند، یا ممکن است کانتینرهایی را بالا بیاورند که شما نخواستهاید. برای حل این مشکل، راهنما توصیه میکند از تستهای واحد بدون زیرساخت با استفاده از یک مخزن حداقلی در حافظه (In-memory Repository) استفاده شود.
این MockRepository<T> فقط متدهای TypeORM را که سرویسها واقعاً استفاده میکنند (مانند save ،findOne و findAndCount) پیاده میکند و به عامل اجازه میدهد منطق را در چند ثانیه تأیید کند. تستهای E2E باید AppModule واقعی را با تمام پایپها، گاردها، فیلترها و Throttlerهای واقعی بالا بیاورند، اما فقط مخازن و سرویس ایمیل را Override کنند. این کار اجازه میدهد احراز هویت و اعتبارسنجی — جایی که اکثر باگها زندگی میکنند — بدون نیاز به یک نمونه PostgreSQL واقعی تست شوند.
توسعهدهندگان باید دستورات دقیقی را که عامل باید اجرا کند (شروع از محدودترین حالت، مثلاً npx jest <path>) به او بگویند و اصرار کنند که یک تست قرمز (Fail) باید به دلیل واقعی اصلاح شود، نه با تضعیف Assertionها. از آنجایی که مخازن در حافظه، محدودیتهای SQL یا Cascadeها را تست نمیکنند، این شکافها باید در docs/testing.md مستند شوند و مهاجرتها باید بهصورت دستی (Up, Down, Up) روی یک دیتابیس PostgreSQL یکبارمصرف قبل از انتشار بررسی شوند.
اجبار در دستورات مخرب
برای عملیاتی که میتوانند دادهها را نابود کنند، قوانین مکتوب کافی نیستند. در Claude Code، توسعهدهندگان میتوانند از قوانین دسترسی (Permission Rules) استفاده کنند تا قبل از اجرای دستورات خاص توسط عامل، حتی در حالتهای Permissive، یک درخواست تأیید (Prompt) اجباری شود:
- درخواست اجازه (Ask Permission): برای دستورات
Bash(npm run migration:*)،Bash(npm run seed *)،Bash(psql *)،Bash(docker compose down *)وBash(git push *). - رد دسترسی (Deny Access): برای
Read(**/.env)وRead(**/.env.production).
دانستن محدودیتها مهم است: این قوانین با متن دستور مطابقت دارند. اسکریپتی که بهطور غیرمستقیم یک مهاجرت را اجرا میکند، شناسایی نمیشود و ممنوعیت خواندن .env مانع از کدی نمیشود که فایل را باز میکند. برای سایر عاملها، تأیید دستورات باید بهطور مشابه پیکربندی شود.
فرمت گزارش ثابت (The Fixed Report Format)
برای حذف پاسخهای مبهم مانند «انجام شد! همه چیز کار میکند»، فایل AGENTS.md باید یک فرمت گزارش ثابت را مطالبه کند. این کار یک ادعای مبهم را به چیزی تبدیل میکند که در سی ثانیه قابل تأیید است:
- پیادهسازی شده (Implemented): چه چیزی تغییر کرد و چرا.
- فایلهای تغییر یافته (Files changed): مسیرهای دقیق فایلها.
- دستورات تأیید (Verification commands): دستورات واقعی اجرا شده و نتایج واقعی آنها.
- اقدامات انجام نشده (Not performed): مثلاً تولید مهاجرت، اجرای Seed، داکر یا استقرار.
- موارد تأیید نشده (Unverified): هر چیزی که عامل نتوانست بررسی کند.
عاملها باید صراحتاً متوجه شوند: «هرگز ادعا نکن که یک دستور پاس شده است، اگر آن را اجرا نکردهای».
اتوماسیون رویهای (Procedural Automation)
وظایف تکراری چندمرحلهای باید به فایلهای رویه تبدیل شوند. در Claude Code، اینها «مهارتها» (Skills) هستند که در مسیر .claude/skills/<name>/SKILL.md قرار میگیرند. برای سایر عاملها، همین فایلهای Markdown با دستور «از رویه .claude/skills/debug/SKILL.md پیروی کن» کار میکنند.
کاندیداهای مناسب برای رویههای NestJS عبارتند از:
- generate: ابتدا یک ماژول موجود را بررسی کن، قبل از ایجاد مجدد از آن استفاده کن، تستها را اضافه کن و گزارش بده.
- database-change: قراردادهای موجودیت را چک کن، تصمیم بگیر آیا مهاجرت لازم است یا خیر، متد
down()را بررسی کن و هرگز بدون درخواست مهاجرت تولید نکن. - debug: علائم را به فایل درست نگاشت کن (مثلاً خطای 400 مربوط به یک Property نشاندهنده نبود فیلد در DTO است)، با یک تست بدون دیتابیس بازتولید کن و در ریشه اصلاح کن.
این رویهها باید بهگونهای نامگذاری شوند که با دستورات داخلی تداخل نداشته باشند (مثلاً review-change بهجای review).
این تغییر در رویکرد، عامل هوش مصنوعی را نه به عنوان یک کدنویس جادویی، بلکه به عنوان یک توسعهدهنده جونیور میبیند که برای بهرهوری، به یک دفترچه راهنمای سختگیرانه و یک خط لولهی CI دقیق نیاز دارد.
برای کسانی که میخواهند این سیستم را فوراً پیاده کنند، نویسنده یک استارتر کیت NestJS 11 + PostgreSQL منتشر کرده است که شامل سیستم احراز هویت، API ادمین، ابزارهای مهاجرت، ۹۶ تست و تمام پیکربندیهای عاملی توصیف شده در بالا است.
توسعهدهندگان اکنون باید فایلهای CLAUDE.md یا .cursorrules خود را بازبینی کنند تا ببینند آیا در حال ارائه توصیههای استایلی هستند یا قوانین توقف قابل اجرا.
گام بعدی شما
- فایلهای
CLAUDE.mdیا.cursorrulesخود را بازبینی کنید و توصیههای استایلی را به «قوانین توقف» صریح تبدیل کنید. - یک فایل
AGENTS.mdواحد ایجاد کنید و تمام دستورات مربوط به معماری NestJS و محدودیتهای دیتابیس را در آن متمرکز کنید. - تستهای واحد خود را به مدل In-memory منتقل کنید تا نرخ تأیید تغییرات توسط عاملها افزایش یابد.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو