
وقتی هوشمصنوعی کد مینویسد: چطور تغییرات مخرب در API را شناسایی کنیم؟
ابزارهای هوشمصنوعی مثل GitHub Copilot و Cursor گاهی با اضافه کردن پارامترهای اختیاری یا عمومیکردن توابع، قراردادهای نرمافزار را عوض میکنند. این مقاله یک روش عملی و اسکریپت پایتون برای پیدا کردن
نکات کلیدی
- اضافه شدن یک پارامتر اختیاری توسط هوشمصنوعی، تغییر در قرارداد عمومی و عملا یک انتشار (Release) است.
- اسکریپت پایتون معرفیشده میتواند اختلاف امضاهای عمومی، پارامترهای تازه و مقدارهای پیشفرض تغییرکرده را بین دو برنچ پیدا کند.
- راهکار تکمیلی، فریز کردن فهرست نامهای عمومی و خط فرمان اصلی در CI است تا هر تغییر نیازمند بررسی صریح باشد.
یک پارامتر اختیاری جدید، در واقع یک انتشار (Release) است. همین جمله کوتاه، مرکز یک مشکل رو به رشد در توسعه نرمافزار است: وقتی از دستیارهای کدنویسی هوشمصنوعی مانند GitHub Copilot یا Cursor استفاده میکنیم، برای اینکه کد جدید کامپایل یا اجرا شود، ممکن است یک پارامتر اختیاری به تابع اضافه کنند و برایش مقدار پیشفرض بگذارند. مشکل اینجاست که هر caller قدیمی که آن پارامتر را صراحتا نفرستاده، ناگهان با رفتار تازهای روبهرو میشود. این تغییر، صرفا یک اصلاح سبک کدنویسی نیست؛ یک تغییر در قرارداد عمومی (Public Contract) نرمافزار است و باید همانطور بررسی شود که یک رفتار منتشرشده را بررسی میکنید، نه مثل یک باقیمانده سبک کد.
چرا دستیارهای هوشمصنوعی این کار را میکنند؟
این ابزارها معمولا برای همان محل فراخوانی که جلوی چشمشان است تصمیم میگیرند. آنها از همه مصرفکنندههای پاییندستی، اسکریپتهای wrapper، یا دشبوردی که به CLI شما دستور میفرستد خبر ندارند. هدفشان حل مسئله محلی است: «این تابع بدون این آرگومان کار نمیکند؛ پس یک آرگومان اختیاری با پیشفرض اضافه میکنم.» اما اثر این تصمیم از همان خط کد فراتر میرود:
- اضافه شدن یک پارامتر با مقدار پیشفرض، رفتار callerهایی را عوض میکند که هرگز آن را نمیفرستادند.
- معرفی یک نام عمومی جدید، یعنی یک قول پشتیبانی.
- یک فلگ CLI که پیشفرضش روشن است، میتواند پارامترهای خط فرمان در محیط production را بیصدا عوض کند.
برخلاف باگهای کلاسیک مثل I/O پنهان یا استثناهای قورتدادهشده، این نوع رگرسیون مستقیما در امضای تابع (Function Signature) دیده میشود و شاید اثرش دو سرویس آنطرفتر خودش را نشان دهد.
چه چیزی «عمومی» محسوب میشود؟
در یک پکیج پایتون، موارد زیر معمولا عمومیاند، مگر اینکه مخزن قبلا قاعده محدودتری را مستند کرده باشد:
- نامهای سطح ماژول که با زیرخط (
_) شروع نمیشوند. - نامهای فهرستشده در
__all__. - سمبلهایی که از
__init__.pyدوباره صادر (re-export) شدهاند. - گزینههای argparse / click / typer و مقدار پیشفرض آنها.
- فیلدهای dataclass و کلیدهای typed-dict که به JSON یا ردیفهای ORM سریالایز میشوند.
توابع کمکی خصوصی (مثل _parse) میتوانند جابهجا شوند. اما نامهای عمومی نه. اگر دستیار هوشمصنوعی برای راحتی یک تست واحد، _parse را به parse تبدیل کند تا بتوان آن را import کرد، این یک تصمیم محصول است: یا باید این تغییر را برگردانید (revert)، یا هزینه پشتیبانی از آن را بپذیرید.
یک ابزار عملی: اسکریپت شناسایی تغییرات امضا
در این مقاله یک اسکریپت پایتون معرفی میشود که دو درخت سورس (مثلا main و شاخه feature) را میخواند و این موارد را گزارش میکند: ۱. توابع عمومی اضافهشده ۲. پارامترهای اضافهشده ۳. مقدارهای پیشفرضی که عوض شدهاند
نحوه اجرا به این شکل است:
git fetch origin pull/123/head:pr-123
git worktree add /tmp/pr-base origin/main
git worktree add /tmp/pr-head pr-123
python3 signature_diff.py /tmp/pr-base /tmp/pr-head
بهتر است این اسکریپت را همراه با یک جستوجو برای قراردادهای CLI و متغیرهای محیطی هم اجرا کنید؛ چون ممکن است parser آنها را نبیند:
git diff origin/main...HEAD -- '*.py' \
| rg -n 'add_argument\(|click\\.(option|command)|typer\\.Option|os\\.environ|getenv\('
توجه: این اسکریپت extensionهای C، forward کردن *args یا __getattr__ سطح ماژول را تشخیص نمیدهد. به آن مثل یک تور نگاه کنید، نه یک اثبات قطعی.
جدول تصمیمگیری: اعتماد کنیم، برگردانیم یا تست بنویسیم؟
در ادامه یک جدول تصمیمگیری میآید که بر اساس نوع تغییر، اقدام مناسب را پیشنهاد میکند:
| کلاس تغییر | اعتماد؟ | Revert؟ | تست |
|---|---|---|---|
| پارامتر جدیدی که پیشفرض آن یک timeout، limit یا batch size را تغییر میدهد | خیر | مقدار پیشفرض برگردد؛ نیاز به opt-in صریح داشته باشد | تستی که مقدار قدیمی را پیشفرض ضمنی بداند |
مقدار پیشفرض تغییر کرده (مثلا limit=100 → limit=1000) |
خیر | مقدار پیشفرض قدیمی برگردد | snapshot از callerهایی که آرگومان را حذف کردهاند |
| پارامتر اجباری جدید | خیر، مگر تغییر عمدی major | یک مقدار پیشفرض برگردانده شود یا تابع جدید ساخته شود | کامپایل مثالهای پاییندستی |
| فلگ CLI جدید با پیشفرض روشن | خیر | پیشفرض خاموش شود یا فلگ حذف شود | اجرای خط فرمان مستندشده بدون فلگهای جدید |
| عمومیکردن تابع فقط برای استفاده تستها | خیر | بله، یا آن را خصوصی نگه دارید | یک linter برای import: تستها نباید نام عمومی جدید بسازند |
در این جدول، «اعتماد» یعنی «تغییر در مرحله merge باقی بماند»، نه اینکه «مدل هوشمصنوعی رفتار production را بهدرستی فهمیده است».
راهکار نهایی: فریز کردن قرارداد عمومی
پیشنهاد نهایی، افزودن یک تست ساده اما حیاتی است: فریز کردن لیست importهای عمومی و خط فرمان مستندشده در CI. هر Pull Requestی که (بهویژه اگر با کمک هوشمصنوعی ساخته شده) هرکدام از این دو را تغییر میدهد، باید فایلهای فریز را در همان commit بهروز کند. این بهروزرسانی باید مثل یک تغییر محصولی جدی (مثل migration در schema) بررسی شود.
نمونهای از تست فریز به این شکل است:
# tests/test_public_names_freeze.py
import json
from pathlib import Path
import myservice as pkg
FREEZE = Path(__file__).parent / "public_names.json"
def test_public_names_match_freeze():
names = sorted(n for n in dir(pkg) if not n.startswith("_"))
expected = json.loads(FREEZE.read_text())
assert names == expected, (
"Public names changed. If intentional, update public_names.json "
"and the changelog in the same PR."
)
جمعبندی: سه اصل برای بررسی کدهای هوشمصنوعی
روش پیشنهادی را میتوان در سه اصل خلاصه کرد: ۱. تغییرات امضا را diff بگیرید. ۲. عمومیکردن برای راحتی تست را برگردانید. ۳. رفتار جدید را پشت آرگومانهای صریح نگه دارید. و یک اقدام تکمیلی: یک لیست import و خط فرمان اصلی را فریز کنید.
با رعایت این اصول، میتوانید از مزایای دستیارهای کدنویسی استفاده کنید، بیآنکه قراردادهایی که مشتریان و سرویسهای دیگر به آن وابستهاند ناخواسته و مخرب تغییر کنند. هر آرگومان حذفشده یعنی یک caller در میدان است؛ مقدارهای پیشفرض تازه، حتی بدون یک خط در changelog، به دست همان caller میرسد.
ابزارهای مرتبط والور
سوالات متداول
آیا این روش فقط برای پایتون کاربرد دارد؟
خیر. اصول کلی — مثل بررسی تغییرات در قراردادهای عمومی و مقدارهای پیشفرض — درباره بیشتر زبانها صدق میکند. اما اسکریپت ارائهشده در این مقاله مخصوص تحلیل AST پایتون نوشته شده است.
اگر تیم ما از قبل یک بات برای فریز کردن API دارد، باز هم به این روش نیاز داریم؟
بله. داشتن بات API-freeze خوب است، اما ارزش اضافه این مقاله، جدول تصمیمگیری است که معیار روشن برای هر کلاس از تغییرات میدهد. میتوانید همان جدول را بهعنوان استاندارد بازبینی کنار ابزارهای موجود نگه دارید.
دستیار هوشمصنوعی چطور میتواند فایل فریز را بهروز کند؟
اگر دستورالعملها اجازه دهند، مدل میتواند فایل فریز را هم ویرایش کند. این تغییر باید مثل یک تغییر محصولی (مشابه migration در schema) بازبینی شود و بهصورت خودکار پذیرفته نشود.
منابع
این مقاله با کمک هوش مصنوعی از منابع بالا تدوین و توسط امیرعلی کریمی بازبینی شده است. قوانین محتوایی والور

بنیانگذار و سردبیر والور
امیرعلی کریمی بنیانگذار و سردبیر والور است. مقالههای والور با کمک هوش مصنوعی و بر پایهی چند منبع معتبر بینالمللی تدوین میشوند و من پیش از انتشار، هر مطلب را از نظر درستی اطلاعات، روانی متن و منابع بازبینی و تایید میکنم. منابع هر مقاله در انتهای همان صفحه فهرست شدهاند.
مطالب مرتبط
اتوماسیون CI با Temporal و Nomad: اجرای خودکار و هوشمند رانرهای GitHub Actions
چطور با ترکیب Temporal، Nomad و یک اپلیکیشن GitHub، یک سیستم مقیاسپذیر و خودکار برای اجرای رانرهای CI ساختیم که منابع را تنها در زمان نیاز مصرف میکند و مدیریت دستی را حذف میکند.
چطور کد C++ را ایمن و سریعتر از C بنویسیم؛ قواعد یک مهندس با ۲۰ سال سابقه
یک مهندس نرمافزار با تجربه در سیستمهای نهفته، قواعد عملی را گردآوری کرده که رعایت آنها کد C++ را نهتنها ایمنتر، که در بعضی موارد سریعتر از معادل C میکند. این قواعد حول کنترل حافظه و مدیریت خطا
معماری توسعه نرمافزار در عصر هوش مصنوعی: از کدنویس تا مهندس زمینه
آیندهی برنامهنویسی دیگر دربارهی تعداد خطوط کد نیست. با ظهور مدلهای «سیستم ۱» و پروتکل MCP، نقش توسعهدهنده از کدنویس به معمار سیستمهای خودگردان هوش مصنوعی در حال تغییر است.