تصور کنید در جلسه بررسی کد، بهجای غرق شدن در هزاران خط کد قدیمی، فقط روی همان چند خطی تمرکز کنید که خودتان تغییر دادهاید و واقعاً مسئولیت آنها را بر عهده دارید. Avouch دقیقاً همین تجربه را ممکن میکند؛ ابزاری که تحلیل ایستای پایتون را از کل مخزن به «تفاضل» (Diff) یک کامیت منتقل میکند. این ابزار با تغییر تمرکز از کل کدبیس به تغییرات خاص، اجازه میدهد توسعهدهنده فقط روی اثرات مستقیم کدهای جدیدش متمرکز شود.
این ابزار بهعنوان یک رابط خط فرمان (CLI) سبک، از Git میپرسد کدام فایلها در کامیت بعدی تغییر میکنند، آنها را با ماژول استاندارد ast تحلیل میکند و مشکلات ساختاری را بر اساس محدودیتهای قابل تنظیم گزارش میدهد. همانطور که در بحثهای گذشته ما دربارهی مدیریت بدهی فنی در پروژههای متنباز اشاره کردیم، بزرگترین مانع پذیرش استانداردهای کدنویسی، حجم عظیم هشدارهای قدیمی است.
بسیاری از ابزارهای تحلیل ایستا دچار «نویز» هستند؛ صدها هشدار مربوط به کدهای قدیمی که خطاهای جدید برنامهنویس را میبلعند. این وضعیت یک سد روانشناختی ایجاد میکند که در آن توسعهدهندگان گزارشهای Linting را نادیده میگیرند چون نسبت سیگنال به نویز بسیار پایین است. Avouch با محاسبه مجموعه بررسی در زمان اجرا از طریق دستور git diff HEAD --name-only به همراه فایلهای ردیابینشده (untracked)، این مشکل را حل میکند. طبق مستندات پروژه، هر یافتهای مستقیماً به کاری مربوط است که شما در حال Push کردن آن هستید، و هرگز به میراثی که از دیگران به ارث بردهاید مربوط نمیشود.
مزیت تحلیل AST
برخلاف بسیاری از Linterهای سبک که بر پایه عبارتهای منظم (Regex) هستند، Avouch از ماژول درخت نحو انتزاعی (AST) پایتون استفاده میکند. این رویکرد به ابزار اجازه میدهد ساختار واقعی کد را بفهمد؛ مثلاً میتواند تفاوت بین تعریف یک تابع و فراخوانی آن را تشخیص دهد، یا عمق تو در تو بودن یک بلوک را بدون فریب خوردن توسط فاصلهها (Indentation) یا رشتههای چندخطی بهطور دقیق اندازهگیری کند.
به نقل از مستندات فنی این پروژه، این روش ساختاری تضمین میکند که معیارهایی مثل تعداد پارامترها، عمق تو در تو بودن و بازه خطوط کاملاً دقیق باشند. اگر معیاری از طریق AST قابل محاسبه بهطور دقیق نباشد، Avouch ادعایی درباره آن نمیکند. این دقت در نحوه مدیریت خطاها نیز جاری است: فایلی که غیرقابل خواندن باشد یا از نظر نحوی شکسته باشد، بهعنوان یک ورودی ERROR در گزارش ثبت میشود، اما یک فایل خراب هرگز مانع از بررسی سایر فایلها نمیشود.
قوانین تحلیل هسته
این ابزار با ۱۷ شناسهی قانون داخلی (SCR001 تا SCR017) و دو بررسی پیچیدگی سیکلوماتیک برای توابع و کلاسها عرضه شده است. تمام یافتههای قوانین در سطح WARNING هستند و سطح ERROR فقط برای فایلهایی رزرو شده است که نمیتوان آنها را خواند یا تجزیه (Parse) کرد.
معیارهای پیچیدگی و منطق:
- پیچیدگی سیکلوماتیک: توابع و کلاسهایی که از
max_complexity(پیشفرض ۴۰) فراتر روند را علامتگذاری میکند. این مقدار با یک پایه ۱ شروع شده و برای هرif،for،while،try،except،match، عملگرهای سه تایی (ternary)،assertو زنجیرههایand/orیک واحد اضافه میشود. - عمق تو در تو (SCR013): حداکثر تو در تو بودن گرههای بلوک را که از
max_nesting(پیشفرض ۵) بیشتر باشد شناسایی میکند. در اینجاif،for،while،async for،with،async with،tryوmatchشمرده میشوند. توجه داشته باشید که Comprehensionها و Lambdaها عمق را افزایش نمیدهند. - پیچیدگی بولی (SCR003): زنجیرههای تک
and/orبا تعداد عملوندهای بیش از حد (پیشفرض ۵) را هشدار میدهد. در زنجیرههای تو در تو، تعداد عملوندها با هم جمع میشوند. - بوی منطقی (Logic Smells): شناسایی بلوکهای
exceptخالی یا «لخت» (SCR002) که باعث میشوندKeyboardInterruptوSystemExitبهطور ناخواسته گرفته شوند، و همچنین توابع async که هرگز ازawaitاستفاده نمیکنند (SCR001)، زیرا این توابع بدون فراهم کردن همروندی، سربار حلقه رویداد (event-loop) را تحمیل میکنند.
محدودیتهای ساختاری و اندازه:
- تورم پارامترها (SCR014): توابعی با بیش از
max_parameters(پیشفرض ۵) را شناسایی میکند. این قانون پارامترهای موقعیتی و کلیدواژهای را میشمارد اما*argsو**kwargsرا نادیده میگیرد. - محدودیت اندازه: نظارت بر تعداد خطوط فایل (
max_file_linesپیشفرض ۱۰۰۰)، اندازه کلاس (max_class_linesپیشفرض ۲۰۰) و طول تابع (max_function_linesپیشفرض ۳۰۰). - متغیرهای محلی (SCR009): توابعی که بیش از
max_local_variables(پیشفرض ۳۰) نام متمایز را تخصیص میدهند. این مورد شاملast.Assignبا اهدافast.Nameمیشود. - دستورات بازگشتی (SCR016): توابعی با بیش از
max_return_statements(پیشفرض ۶) را علامتگذاری میکند.
بوی کدهای پیشرفته:
- مقادیر پیشفرض تغییرپذیر (SCR017): شناسایی مقادیر پیشفرمی برای پارامترها که تغییرپذیر هستند، مانند
[]یا{}یا فراخوانیlist()وset()، که باعث نشت وضعیت (state leak) بین فراخوانیهای مختلف تابع میشوند. - شاخههای تکراری (SCR004/SCR006): شناسایی شاخههای
if/elifبا بدنههای کاملاً یکسان. قانون SCR004 روی توابع و SCR006 روی هر دو بخش توابع و کلاسها اجرا میشود. - اندازه Comprehension (SCR005): شناسایی لیستها، مجموعهها یا دیکشنریهای تو در تو که از
max_large_comprehensions(پیشفرض ۴۰) گره AST فراتر روند. - پیچیدگی Lambda (SCR008): بدنههای لمدایی که بیش از
max_lambda_nodes(پیشفرض ۱۰) گره دارند. - توابع تو در تو (SCR015): شناسایی تعریفهای ساده
defدر داخل توابع دیگر برای جلوگیری از ایجاد Closureهایی که تست واحد (Unit Testing) را دشوار میکنند.
یکپارچهسازی و گردش کار
Avouch برای اجرا در ثانیههای پیش از git push طراحی شده است. این ابزار به پایتون ۳.۱۰ به بالا (برای استفاده از ast.Match و tomllib) و وجود Git در مسیر PATH سیستم نیاز دارد. این ابزار بدون نیاز به دیمون یا اتصال شبکه کار میکند و زمان اجرای آن توسط اندازه Diff محدود میشود، نه اندازه کل مخزن.
حالتهای بررسی:
۱. حالت پیشفرض: بررسی فایلهای ردیابیشده که نسبت به HEAD تغییر کردهاند و فایلهای .py ردیابینشده. این حالت بهطور خودکار فایلهایی که به نظر تولیدشده (generated) میرسند (مانند generated.py یا codegen.py) را نادیده میگیرد.
۲. حالت Staged: تحلیل فقط فایلهایی که برای کامیت بعدی Stage شدهاند با استفاده از دستور git diff --cached --name-only (--staged).
۳. حالت CI: اسکن کامل مخزن (--all-files) که برای CI Runnerها (مانند GitHub Actions) ضروری است، زیرا در آنجا Checkout تمیز است و git diff HEAD مجموعه خالی برمیگرداند.
۴. حالت غیر Git: بررسی تمام فایلهای .py واجد شرایط روی دیسک با پیمایش دایرکتوری جاری و نادیده گرفتن پوشههای .git، کش و محیطهای مجازی (--not-git).
برای اتوماسیون، پرچم --json یک خروجی استاندارد، نسخهبندی شده و پایدار ارائه میدهد. هر تخلف شامل شناسهی قانون، شدت، پیام، فایل، نام کامپوننت، نوع (func یا class یا file) و شماره خط است.
کدهای خروجی (Exit Codes) برای سیگنالدهی به نتایج استفاده میشوند:
- ۰: بررسی پاک و بدون تخلف.
- ۱: یافتن تخلفات.
- ۲: خطای داخلی Avouch (مثلاً عدم یافتن مخزن Git یا پیکربندی نامعتبر).
در GitHub Actions، این ابزار با actions/checkout@v6 و actions/setup-python@v5 با پایتون ۳.۱۲ به راحتی ادغام میشود. دستور پیشنهادی برای CI عبارت است از avouch --all-files --json. از آنجا که GitHub Actions هر کد خروجی غیرصفر را به عنوان شکست Job تلقی میکند، هرگونه تخلف گزارششده باعث توقف خط لوله میشود، مگر اینکه مسیرها در ignore_paths در فایل avouch.toml نادیده گرفته شوند.
پیکربندی و شخصیسازی
تنظیمات اختیاری، جزئی و توصیفی هستند و از طریق یک فایل محلی avouch.toml در دایرکتوری کاری جاری مدیریت میشوند. Avouch دایرکتوریهای والد را جستوجو نمیکند تا تضمین شود پیکربندیها کاملاً محلی به هر مخزن هستند.
کاربران میتوانند تنظیمات خود را بر روی مقادیر پیشفرض اعمال کنند:
[limits]: آستانههای عددی قابل تغییر هستند. برای مثال، تنظیمmax_parameters = 8اجازه امضاهای منعطفتری را میدهد.[rules]: قوانین را میتوان خاموش کرد. تنظیمnested_function = falseگزارشهای SCR015 را متوقف میکند.ignore_paths: یک لیست در سطح بالا (مثلاًignore_paths = ["tests", "migrations"]) که مسیرها را بهصورت کامپوننت حذف میکند. این لیست با پرچم--ignore-pathدر CLI ترکیب میشود.
معماری داخلی
کدبیس برای کمترین سربار طراحی شده است. ارکستراتور cli.py خط لوله را مدیریت میکند: بارگذاری تنظیمات توسط config/loader.py، محاسبه مجموعه بررسی توسط git.py و تحلیل فایلها توسط analyzer.py. فرآیند تحلیل، فایلهای UTF-8 را خوانده، آنها را به AST تبدیل کرده و با استفاده از یک پیمایش کششده (utility/walk.py) گرهها را به ماژولهای قوانین در rules/*.py میفرستد.
تحلیل تحریریه
Avouch نشاندهنده تغییری به سمت تجربه توسعهدهنده «جراحیشده» (Surgical) است. با گره زدن تحلیل ایستا مستقیماً به Git Diff، اثر «پنجره شکسته» را از بین میبرد؛ وضعیتی که در آن توسعهدهندگان هشدارها را نادیده میگیرند چون کدبیس از قبل بههمریخته است. این ابزار Linter را از یک حسابرس جهانی به یک دستیار شخصی تبدیل میکند که فقط زمانی صحبت میکند که شما یک مشکل جدید ایجاد کرده باشید.
برای تیمهای پایتون، این رویکرد اصطکاک پذیرش استانداردهای سختگیرانه کدنویسی را کاهش میدهد. بهجای وظیفه دلهرهآور اصلاح ۵۰۰۰ هشدار قدیمی، توسعهدهندگان فقط باید مطمئن شوند که PR فعلی آنها پاک است. این استراتژی بهبود تدریجی برای پروژههای سازمانی در مقیاس بزرگ، بسیار پایدارتر از تلاش برای بازنگری کلی کدبیس است. در همین راستا، تلاش برای بهینهسازی زیرساختهای توسعه با استفاده از ابزارهای تخصصیتر، مانند آنچه در کاهش هزینههای بازگردانی وضعیت فایلسیستم توسط Shepherd دیدیم، میتواند بهرهوری کلی تیم را افزایش دهد.
برای شروع، میتوانید ابزار را با دستور pip install avouch نصب کنید و در هر مخزن Git اجرا کنید تا دقیقاً ببینید چه بدهی ساختاری را در حال Push کردن هستید.
- ابزار را نصب کنید و در یک مخزن Git فعال اجرا کنید تا بدهیهای ساختاری فعلی خود را ببینید.
- یک فایل
avouch.tomlایجاد کنید و محدودیتهایmax_complexityرا متناسب با استانداردهای تیم خود تنظیم کنید. - این ابزار را به Pre-commit hookهای خود اضافه کنید تا هیچ کد با پیچیدگی غیرمجاز Push نشود.
اما برای کسانی که به دنبال تحلیلهای عمیقتر در سطح معماری هستند، ابزارهای مبتنی بر گراف کد (Code Graph) ابعاد جدیدی را میگشایند — به بررسی ما درباره تحلیلهای استاتیک پیشرفته مراجعه کنید. این مسیر تکامل ابزارهای تحلیل، ما را به سمت استفاده از عاملهای کدنویسی هوشمندتر میبرد، مشابه رویکرد عامل Ante در کاهش مصرف حافظه که استقلال توسعهدهنده از ابزارهای متمرکز را هدف قرار داده است.




گفتگو