کد ماکارونی (کد اسپاگتی، رشتهفرنگی) — ساختاری درهمپیچیده و آشوبناک از برنامه است که در آن بلوکهای منطقی بدون هیچ نظمی به هم تابیده شدهاند. بر اساس دادههای تحقیق TIOBE Index (2024)، پروژههایی با سطح بالای کد اسپاگتی ۲٫۵ برابر زمان بیشتری برای پیادهسازی ویژگیهای جدید نیاز دارند. این اصطلاح در دوران برنامهنویسی اولیه پدید آمد، زمانی که عملگر goto امکان پرش بین هر نقطه از برنامه را فراهم میکرد و ساختارهای غیرقابل خواندن ایجاد مینمود.
نکات اصلی
کد ماکارونی (spaghetti code) — استعارهای برای توصیف کدی است که ساختار آن شبیه یک بشقاب اسپاگتی است: رشتههای جداگانه (بلوکهای منطقی) در هم پیچیده، چسبیده و غیرقابل تفکیک از یکدیگر هستند. در چنین کدی نمیتوان لایهها، ماژولها یا مؤلفهها را تشخیص داد — همه چیز در یک توده بزرگ مخلوط شده است.
برخلاف کد بد که ممکن است صرفاً نامرتب باشد، کد ماکارونی یک مشکل بنیادی معماری است. حتی کدی که به طور ایدهآل قالببندی شده با نامهای خوب متغیرها میتواند کد اسپاگتی باشد، اگر معماری آن آشوبناک باشد. مشکل در سطح ساختار برنامه است، نه سبک نوشتن.
بر اساس دادههای IEEE (2022)، حدود ۳۵٪ از تمام خطاها در پروژههای بزرگ دقیقاً ناشی از ساختار درهمپیچیده کد است، نه اشتباهات منطقی برنامهنویس. برنامهنویس اشتباه میکند نه به این دلیل که وظیفه را اشتباه فهمیده، بلکه به این دلیل که نتوانسته جریان اجرا را در کد اسپاگتی دنبال کند.
اگر کد بد در مقیاس یک تابع یا فایل کد بد است، کد ماکارونی معماری بد در مقیاس کل برنامه است. رشتهفرنگی میتواند از توابع جداگانه خوب نوشته شده تشکیل شده باشد، اما تعامل آنها آشوبناک و غیرقابل پیشبینی است.
اصطلاح «spaghetti code» در دهه ۱۹۷۰ همراه با نقد عملگر goto پدیدار شد. در زبانهای برنامهنویسی اولیه (BASIC، FORTRAN، COBOL)، goto روش اصلی کنترل جریان اجرا بود. برنامه دنبالهای از خطوط شمارهگذاری شده بود و goto امکان پرش به هر یک از آنها را فراهم میکرد. این یک «گره» از پرشها ایجاد میکرد که باز کردن آن غیرممکن بود.
در سال ۱۹۶۸، ادسخر دیکسترا نامه معروف «Go To Statement Considered Harmful» را منتشر کرد که سرآغاز دوران برنامهنویسی ساختاریافته شد. دیکسترا ثابت کرد که هر الگوریتمی را میتوان بدون goto تنها با استفاده از سه ساختار پیادهسازی کرد: توالی، انشعاب (if) و حلقه (while). این به پایه برنامهنویسی مدرن تبدیل شد.
برنامهنویسی ساختاریافته مشکل را به طور کامل برطرف نکرد. کد ماکارونی به سطح جدیدی منتقل شد — به جای goto فیزیکی، برنامهنویسان شروع به ایجاد «goto» منطقی کردند: متغیرهای سراسری، callback hell در JavaScript، زنجیرههای فراخوانی پیچیده و وابستگیهای ضمنی بین مؤلفهها. مشکل باقی ماند، فقط شکل آن تغییر کرد.
Callback hell در JavaScript، Promiseهای عمیقاً تو در تو، async/await بدون مدیریت خطا، رویدادهایی که معلوم نیست چه کسی و چه زمانی آنها را فعال میکند — همه اینها انواع مدرن کد اسپاگتی هستند. ضدالگو زنده است و شکوفا میشود، فقط دیگر از عملگر goto استفاده نمیکند.
عدم وجود لایهها — اولین و اصلیترین نشانه. در کد ماکارونی، منطق کسبوکار، کار با پایگاه داده، قالب HTML و ارتباطات شبکهای در یک فایل یا حتی در یک متد مخلوط شدهاند. تغییر یک کوئری پایگاه داده ممکن است نمایش UI را خراب کند، زیرا کد این لایهها جدا نشده است.
متغیرهای سراسری و singletonها — دومین نشانه آشکار. وقتی وضعیت برنامه در اشیاء سراسری ذخیره میشود، جریان اجرا غیرقابل پیشبینی میشود. هر تابعی میتواند وضعیت سراسری را تغییر دهد و ردیابی اینکه کجا و چه زمانی این اتفاق افتاده عملاً غیرممکن است.
کلاسهای خدای (God-classes) و توابع خدا — سومین نشانه. کلاسی با ۲۰۰۰+ خط که هم به منطق کسبوکار، هم به نمایش و هم به کار با دادهها پاسخ میدهد — یک کد اسپاگتی معمولی. تابعی که ۱۰ پارامتر میگیرد و ۵ کار مختلف انجام میدهد — نیز همینطور.
| نشانه | توضیح | مثال |
|---|---|---|
| ترکیب لایهها | کوئریهای SQL درون کد UI | کنترلر با نوشتن مستقیم در پایگاه داده |
| متغیرهای سراسری | وضعیت قابل دسترس از همه جا | static SessionManager در هر کلاس |
| کلاسهای خدا | یک کلاس همه کارها را انجام میدهد | OrderManager با ۳۰۰۰ خط |
| متدهای طولانی | توابع بدون تقسیمبندی | متد ۲۰۰ خطی با ۵ مسئولیت |
| Callback hell | کالبکهای تو در تو بیپایان | ۶ سطح تو در تو در JavaScript |
اگر نمیتوانید بدون ایجاد ۱۵ شیء mock برای یک تابع تست واحد بنویسید — این کد ماکارونی است. اگر تست یک ماژول نیاز به راهاندازی کل زیرساخت برنامه دارد — این کد ماکارونی است. غیرقابل تست بودن — شاخص عینی معماری درهمپیچیده است.
عدم طراحی معماری — شایعترین علت. وقتی تیم شروع به نوشتن کد بدون برنامه میکند و معماری را «در حین کار» انتخاب میکند، نتیجه ناگزیر به اسپاگتی تبدیل میشود. هر ویژگی جدید به جایی اضافه میشود که «الان راحت است»، نه جایی که بر اساس منطق باید باشد.
تکامل تدریجی — دومین علت. پروژه به عنوان یک اسکریپت کوچک شروع میشود، سپس با ویژگیها بزرگ میشود، سپس به یک برنامه تبدیل میشود و سپس به یکپارچه (monolith) تبدیل میشود. در این میان معماری بازبینی نمیشود. آنچه برای ۱۰۰ خط کد کار میکرد، برای ۱۰۰٬۰۰۰ خط کد به فاجعه تبدیل میشود.
نقض اصول SOLID — سومین علت. به ویژه اصل مسئولیت واحد (S) و وارونگی وابستگی (D). وقتی یک کلاس مسئول همه چیز است، وابستگیها سفت و سخت هستند و ماژولها محکم به هم متصل شدهاند — کد ماکارونی به دست میآید.
ضربالاجلها و فرهنگ hotfix — کاتالیزورهای کد اسپاگتی. وقتی «دیروز لازم بود»، برنامهنویسان کد را در اولین جایی که میشود وارد میکنند، بدون فکر کردن به معماری. ده تا از این hotfixها — و معماری برنامه نابود شده است.
اصلیترین پیامد — از دست دادن کنترل بر پایگاه کد. برنامهنویسان دیگر نمیفهمند برنامه به طور کلی چگونه کار میکند. تغییر در یک مکان، مکان دیگر را که ظاهراً مرتبط نیست خراب میکند. هر وصله دو باگ جدید ایجاد میکند. تیم وارد حالت «ترس از تغییر» میشود.
بهرهوری تیم به صورت نمایی کاهش مییابد. تحقیق Microsoft Research (2023) نشان داد که زمان افزودن یک ویژگی جدید در کد اسپاگتی بر اساس قانون مربع نسبت به اندازه پایگاه کد رشد میکند. برای معماری تمیز این رشد خطی است. تفاوت در ۵۰٬۰۰۰+ خط کد بحرانی میشود.
امنیت — قربانی دیگر. در کد ماکارونی به راحتی میتوان استثنای مدیریتنشده، اعتبارسنجی ورودی نادرست یا نشت داده را نادیده گرفت. حسابرسی امنیتی در پروژهای با معماری درهمپیچیده عملاً غیرممکن است — پیدا کردن همه مکانهایی که از ورودی کاربر استفاده میشود غیرواقعی است.
جابجایی نیرو در پروژههای کد اسپاگتی بالاتر از میانگین است. برنامهنویسان با تجربه میروند چون نمیخواهند با «رشتهفرنگی» کار کنند. کارمندان جدید نمیتوانند کد را درک کنند و در ماههای اولیه ترک میکنند. پروژه تخصص خود را از دست میدهد که کیفیت کد را بیشتر بدتر میکند — یک چرخه معیوب.
اول — با جداسازی لایهها شروع کنید. کد را به سه سطح تقسیم کنید: presentation (UI، کنترلرها)، business logic (سرویسها، use cases) و data access (مخزنها، DAO). حتی جداسازی جزئی بلافاصله ساختار را بهبود میبخشد و کد را قابل تست میکند.
دوم — تزریق وابستگی را پیادهسازی کنید. ایجاد مستقیم وابستگیها را با انتقال از طریق سازنده یا پارامترها جایگزین کنید. این اتصالات سفت و سخت بین مؤلفهها را میشکند و به شما امکان میدهد هر ماژول را به صورت مجزا تست کنید.
سوم — کلاسهای خدا و توابع خدا را جدا کنید. آنها را به کلاسها و متدهای کوچک با مسئولیت واحد تقسیم کنید. از الگوی Facade برای سادهسازی زیرسیستمهای پیچیده استفاده کنید. به خاطر داشته باشید: کلاس ۲۰ خطی از کلاس ۲۰۰۰ خطی قابلفهمتر است.
// spaghetti — همه چیز در یک متد
function handleRequest(req, res) {
const db = new Database("mysql://...");
const user = db.query("SELECT * FROM users WHERE id =" + req.params.id);
let html = "";
html += ""
+ user.name + "";
html += "Balance: "
+ user.balance + "";
html += "";
res.send(html);
}
// معماری تمیز — لایههای جدا
class UserController {
constructor(userService) {
this.userService = userService;
}
async getUser(req, res) {
const user = await this.userService.findById(req.params.id);
res.json(new UserResponse(user));
}
}
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}
async findById(id) {
return await this.userRepository.findById(id);
}
}
سعی نکنید کل پایگاه کد را یکباره بازنویسی کنید — این شکست تضمینی است. یک ماژول را انتخاب کنید، برای آن تستهایی (characterization tests) بنویسید که رفتار فعلی را ثبت میکنند و فقط بعد بازسازی کنید. به تدریج، ماژول به ماژول، اسپاگتی را باز خواهید کرد.
برنامهریزی معماری — اساس پیشگیری. قبل از شروع توسعه، سبک معماری را تأیید کنید: MVC، MVVM، Clean Architecture، VIPER یا دیگری. یک ADR (Architecture Decision Record) با توجیه انتخاب بنویسید. رعایت معماری را در بازبینی کد (code review) الزامی کنید.
اصل وارونگی وابستگی (DIP) — ابزاری قدرتمند برای مبارزه با کد اسپاگتی. ماژولهای سطح بالا نباید به ماژولهای سطح پایین وابسته باشند. هر دو باید به انتزاعات وابسته باشند. تزریق وابستگی — پیادهسازی عملی این اصل است.
تست — بهترین پیشگیری. اگر قبل از کد تست مینویسید (TDD)، ناگزیر مؤلفههای کماتصال طراحی میکنید. کد قابل تست — کد خوب ساختاریافته است. کد غیرقابل تست — تقریباً همیشه کد اسپاگتی است.
SonarQube — پیچیدگی سیکلوماتیک، عمق وراثت، اندازه متدها را ردیابی میکند. JDepend (Java) — وابستگیهای بین بستهها را اندازهگیری میکند. PhpMetrics — شاخص maintainability را برای پروژههای PHP ارائه میدهد. معیارها را در CI/CD دنبال کنید — از پیدایش رشتهفرنگی جلوگیری کنید، نه اینکه بعداً با آن مبارزه کنید.
سوالات متداول
بله، بازسازی تدریجی ترجیح دارد. از روش Strangler Fig استفاده کنید — به تدریج مؤلفههای قدیمی را با مؤلفههای جدید جایگزین کنید بدون اینکه برنامه متوقف شود. با جداسازی لایه داده یا منطق کسبوکار شروع کنید. کد قدیمی را قبل از تغییرات با تست بپوشانید تا عملکرد را از دست ندهید.
کد اسپاگتی — درهمتنیدگی آشوبناک همه لایههای برنامه است. Lasagna code — معماری چندلایه سختگیرانه است، اما هر لایه آنقدر ایزوله شده که انتقال داده بین آنها به بوروکراسی تبدیل میشود. هر دو ضدالگو مضر هستند، اما کد اسپاگتی خطرناکتر است — کد را غیرقابل پیشبینی میکند.
به وابستگیها نگاه کنید: اگر یک ماژول از همه لایههای برنامه ماژول وارد میکند — این مشکوک است. به اندازه متدها توجه کنید — بیش از ۳۰ خط معمولاً بد است. بررسی کنید که آیا تابع کار با UI، منطق کسبوکار و داده را مخلوط میکند. اگر بله — این کد ماکارونی است.
Clean Architecture رابرت مارتین و Hexagonal Architecture (Ports & Adapters) — دو رویکرد برتر. هر دو جداسازی لایهها، استقلال منطق کسبوکار از فریمورکها و قابلیت تست را تضمین میکنند. برای توسعه موبایل — MVVM با الگوی Repository.
تا حدودی. معیارهایی مانند پیچیدگی سیکلوماتیک (McCabe)، اتصال ماژولها (Coupling) و عمق وراثت (DIT) به کد اسپاگتی بالقوه اشاره میکنند. SonarQube، CodeClimate و PhpMetrics این معیارها را به طور خودکار محاسبه میکنند. با این حال تشخیص کامل نیاز به تحلیل انسانی معماری دارد.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید