فرانکنشتاین در برنامهنویسی — کدی است که از قسمتهای ناسازگار فناوریها، سبکها و معماریهای مختلف جمعآوری شده است. بر پایه تحقیقات ThoughtWorks Technology Radar (2024)، 28% پروژههای بزرگ نشانههایی از سندرم فرانکنشتاین را نشان میدهند — التقاط معماری که در غیاب چشمانداز فنی واحد بوجود میآید. همانند رمان مری شلی، چنین کدی کار میکند، اما پشتیبانی از آن به کابوس تبدیل میشود.
نکات کلیدی
فرانکنشتاین (Frankenstein code, Frankenstein pattern) — یک الگوی ضدالگویی است که در آن سیستم نرمافزاری از بخشهایی ساخته میشود که برای کار مشترک طراحی نشدهاند. مانند هیول فرانکنشتاین، چنین کدی میتواند کار کند، اما زشت، غیرقابلپیشبینی و در برابر کوچکترین تغییرات خطرناک است.
این اصطلاح از ادبیات آمده است: در رمان مری شلی «فرانکنشتاین، یا پرومتئوس نوین» (1818)، دانشمندی از تکههای بدن افراد مختلف مرده یک موجود زنده ساخت. در برنامهنویسی، تشبیه دقیق است — توسعهدهندگان تکههایی از فریمورکها، کتابخانهها، زبانهای مختلف را برمیدارند و آنها را «با نخ زنده» به هم میچسبانند، نتیجهای کاربردی اما وحشتناک به دست میآورند.
تفاوت فرانکنشتاین با کد اسپاگتی در مقیاس و ماهیت مشکل است. کد اسپاگتی — ساختار درهم در یک پیشرانه فناوری است. فرانکنشتاین — التقاط در سطح معماری است: فناوریهای مختلف، پارادایمهای ناسازگار، رویکردهای متضاد در یک سیستم.
معماری میکروسرویس استفاده از فناوریهای مختلف برای سرویسهای مختلف را مجاز میداند، اما به شرط مرزهای واضح و پروتکلهای ارتباطی استاندارد. فرانکنشتاین — اختلاط بینظم بدون مرز است: REST و GraphQL در یک کنترلر، دو ORM در یک ماژول، SQL و NoSQL برای یک نهاد.
نبود رهبر فنی یا معمار — علت اصلی. وقتی در پروژه کسی مسئول یکپارچگی معماری نباشد، هر توسعهدهنده ابزارها را «مناسب خود» انتخاب میکند. یکی Spring را دوست دارد، دیگری — Guice، سومی — DI دستنوشته خود. نتیجه — ویناگرت معماری.
ادغام پروژهها — دلیل رایج دوم. دو تیم ماژولهای خود را به طور مستقل و با پیشرانههای فناوری مختلف توسعه دادند. وقتی ماژولها باید در یک برنامه ادغام شوند، آنها به سادگی با آداپترها و لایههای واسط «چسبانده» میشوند. نتیجه فرانکنشتاین است.
اتحادیه های شرکتی — سناریو سوم. شرکت A شرکت B را خرید و میخواهد محصول آن را در محصول خود ادغام کند. به جای بازنویسی — چسباندن از طریق API، پایگاه داده مشترک و چارههای موقت. پس از یک سال، سیستم به هیولی تبدیل میشود که هیچ کس آن را نمیفهمد.
| علت | توضیح | نتیجه معمولی |
|---|---|---|
| نبود معمار | هر توسعهدهنده پیشرانه خود را انتخاب میکند | 3 کلاینت HTTP مختلف در یک ماژول |
| ادغام پروژهها | دو محصول در یکی چسبانده میشوند | دو ORM، دو روش ورودگی |
| M&A | اتحاد شرکت با محصولش | هیبریدی از معماریها و سبکهای مختلف |
| آزمایشها | استقرار فناوریهای جدید بدون استراتژی | Java 8 + Java 21 در یک فایل |
| تصمیمات سیاسی | تحمیل فناوری از بالا بدون توجه به زمینه | فریمورک سازمانی برای یک اسکریپت ساده |
توسعهدهندگان باتجربه که میخواهند فناوریهای جدید را در تولید آزمایش کنند، اغلب منبع فرانکنشتاین میشوند. به جای محدود کردن آزمایشها به یک ماژول جداگانه، کد آزمایشی را در بخش حساس سیستم قرار میدهند.
مثال کلاسیک — استفاده از چندین ORM در یک برنامه. بخشی از ماژولها از Hibernate، بخشی از MyBatis و بخشی از پرسوجوهای مستقیم JDBC استفاده میکنند. تراکنشها غیرقابل مدیریت، کش ناهماهنگ، و توسعهدهنده جدید نمیداند چه رویکردی را برای ویژگی جدید انتخاب کند.
مثال دوم — اختلاط سبکهای معماری. در یک کنترلر REST API، فراخوانی سرویسهای SOAP، پرسوجوهای مستقیم SQL، دسترسی به سیستم پرونده و تولید HTML وجود دارد. چنین برنامهای قابل آزمایش، توسعه یا مستندسازی نیست.
مثال سوم — پیشرانه فناوری: Python برای بکاند، Node.js برای میکروسرویس، C# برای کلاینت رومیزی، و Java برای برنامه Android، در حالی که همه منطق کسب و کار بدون تفکیک واضح مسئولیت در بین آنها پخش شده است.
// فرانکنشتاین — سبکها و فناوریهای مختلط
// callbacks، Promises و async/await ترکیب شده
// callbacks
db.query("SELECT * FROM users", function(err, rows) {
if (err) handleError(err);
// Promise داخل callback
fetch("/api/data").then(function(data) {
// async/await داخل then
(async () => {
const result = await processData(data);
sendResponse(result);
})();
});
});
// کد تمیز — سبک واحد async/await
async function getUserData(userId) {
const user = await db.query("SELECT * FROM users WHERE id = ?", [userId]);
const data = await fetch("/api/data/" + userId);
return await processData(user, data);
}
یک پایگاه داده همزمان هم به عنوان SQL رابطهای (با نرمالسازی) و هم به عنوان مستندگرا NoSQL (با ستونهای JSON) استفاده میشود. بخشی از پرسوجوها از طریق ORM، بخشی از طریق رویههای ذخیرهشده، و بخشی از طریق SQL مستقیم از کد انجام میشود. شمات پایگاه داده مستند نشده، و مهاجرتها متضاد هستند.
سختی آشنایی نیروی جدید — نخستین پیامد. یک توسعهدهنده جدید برای فهم سیستم باید 5 زبان، 3 فریمورک، 2 سبک معماری بداند. آشنایی از هفتهها به ماهها کشیده میشود. به گزارش LinkedIn (2023)، پروژههای با التقاط فناوری کارکنان جدید را 2 برابر بیشتر از دست میدهند.
غیرقابلپیشبینی رفتار — دومین پیامد. تغییر در میکروسرویس Python میتواند به طور غیرمنتظره ماژول Java را تخریب کند، چون آنها از پایگاه داده مشترک بدون قراردادهای واضح استفاده میکنند. اشکالزدایی چنین مشکلاتی نیازمند دانش همزمان همه فناوریهای پیشرانه است.
امنیت — سومین پیامد. هر فناوری در پیشرانه به تنظیمات امنیتی خود، وبکتهای خود و نظارت خود نیاز دارد. حفظ امنیت در سطح قابل قبول برای 5–6 فناوری مختلف عملاً غیرممکن است. یکی از آنها قطعاً آسیبپذیر خواهد بود.
SonarQube میتواند بدهی فنی را اندازهگیری کند، اما نمیتواند «بدهی معماری» را بسنجد — ناسازگاری جزءها. این بدهی در هشدارهای لینتر ظاهر نمیشود، بلکه در غیرممکن بودن افزودن ویژگی جدید بدون تغییر سه ماژول مختلف نوشته شده به زبانهای مختلف.
اولین و مهمترین گام — انتصاب معمار یا رهبر فنی مسئول یکپارچگی پیشرانه فناوری. این شخص در جهت استقرار فناوریهای جدید بدون بررسی معماری حق وتو دارد. دموکراسی نیست، بلکه تصمیم مسئولانه فردی در مورد فناوریهای کلیدی.
دومین — پیادهسازی فرآیند Architecture Decision Record (ADR). هر تصمیم معماری مهم (انتخاب پایگاه داده، فریمورک، پروتکل) در قالب متنی کوتاه مستند میشود: زمینه، التماسهای مورد بررسی، تصمیم گرفته شده، پیامدها. ADRها در مخزن کد ذخیره و برای تمام اعضای تیم دسترس هستند.
سومین — ایجاد اصل «یک وظیفه — یک ابزار». برای درخواستهای HTTP — یک کلاینت. برای ORM — یک کتابخانه. برای ورودگی — یک فریمورک. استثنائات فقط از طریق ADR با توجیه مجاز است. اگر در پروژه Axios وجود دارد، fetch اضافه نکنید؛ اگر SLF4J وجود دارد، از طریق System.out ننویسید.
آزمایشها مجاز هستند، اما در محیطی جداگانه. یک ماژول یا سرویس جدا کنید که بتوان آن را در فناوری جدید بدون تأثیر بر باقی سیستم بازنویسی کرد. اگر آزمایش موفق شد، آن را از طریق ADR استاندارد کنید. در غیر این صورت، بدون عواقب حذف کنید.
احصاییه — گام اول. نقشه کاملی از پیشرانه فناوری تهیه کنید: چه فریمورکها، کتابخانهها، زبانها، پروتکلهایی استفاده میشود، در کدام ماژولها و برای چه وظایفی. مقیاس مشکل را خواهید دید: ابزارهای تکراری، فناوریهای متضاد، وابستگیهای استفادهنشده.
استانداردسازی — گام دوم. برای هر وظیفه یک ابزار انتخاب کنید. مثالاً: تنها Hibernate برای ORM، تنها SLF4J + Logback برای ورودگی، تنها REST برای API. استاندارد را در ADR مستند کنید. از ماژولهایی شروع کنید که التقاط بیشترین مشکل را ایجاد میکند.
راهبرد Parallel Run — گام سوم. ابزارهای قدیمی و جدید به صورت موازی کار میکنند تا ابزار جدید قابلیت اعتماد خود را ثابت کند. به عنوان مثال، کلاینت HTTP قدیمی و جدید همزمان کار میکنند، اما جدید تنها برای بخشی از درخواستها. پس از دوره پایداری، قدیمی حذف میشود.
// فرانکنشتاین — سه رویکرد HTTP در یک پروژه
// ماژول A: OkHttp
OkHttpClient client = new OkHttpClient().newCall(request);
// ماژول B: RestTemplate (Spring)
restTemplate.getForObject(url, String.class);
// ماژول C: java.net.HttpURLConnection
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
// رویکرد واحد: RestTemplate برای همگام، WebClient برای واکنشی
@Autowired
private RestTemplate restTemplate;
public String callApi(String url) {
return restTemplate.getForObject(url, String.class);
}
رهبر فنی — ابزار اصلی مبارزه با فرانکنشتاین است. نه مدیر، نه معمار در برج عاج، بلکه توسعهدهندهای عملی که کد مینویسد، PRها را بررسی میکند و تصمیمات معماری میگیرد. بدون چنین شخصی، پروژه به طور حتمی به سمت التقاط فناوری کشیده میشود.
RFC (Request for Comments) — فرآیندی برگرفته شده از جامعههای Open Source. قبل از استقرار هر فناوری مهم، نویسنده RFC مینویسد: مشکل، راهحل پیشنهادی، التماسها، طرح استقرار. تیم بحث میکند، رای میدهد، میپذیرد یا رد میکند. RFC شفافیت ایجاد میکند و از تصمیمات معماری «خاموش» جلوگیری میکند.
رادار فناوری (Technology Radar از ThoughtWorks) — ابزار دستهبندی فناوریها: Adopt، Trial، Assess، Hold. تیم به طور منظم رادار را بررسی و وضعیتها را بهروز میکند. این کمک میکند «مد» را از «مفید» تمایز دهد و از ورود فناوریهای آزمایشنشده به کد حساس جلوگیری کند.
مهمترین ویژگی معماری — توانمانی (consistency) است. حتی ابزاری که بهترین نیست، اگر در کل پروژه استفاده شود، بهتر از بهترین ابزاری است که تنها در یک ماژول استفاده شود. توانمانی بار شناختی را کاهش میدهد، آشنایی را سادهتر میکند و کد را قابل پیشبینی میسازد.
پرسشهای متداول
Polyglot persistence — استفاده آگاهانه از پایگاههای داده مختلف برای وظایف مختلف (PostgreSQL برای تراکنشها، Redis برای کش، Elasticsearch برای جستجو) است. فرانکنشتاین — اختلاط بینظم بدون استراتژی. تفاوت در وجود تصمیم معماری است: polyglot — یک طرح است، فرانکنشتاین — نبود آن.
بله، و این یک مشکل رایج است. وقتی هر میکروسرویس از زبان خود، پایگاه داده خود، پروتکل خود و روش استقرار خود را بدون استانداردهای متمرکز استفاده کند، فرانکنشتاین توزیعشده ایجاد میشود. برای میکروسرویسها استانداردهای مشترک مهم است: پروتکل واحد (REST/gRPC)، فرمت مشترک لاگ، و observability متمرکز.
ممنوع نکنید — هدایت کنید. به نویسنده RFC پیشنهاد دهید: توضیح دهد چرا راهحل فعلی مناسب نیست، چه التماسهایی را بررسی کرده، چگونه مهاجرت خواهد کرد. اغلب در فرآیند نوشتن RFC، توسعهدهنده خود متوجه میشود که فناوری جدید لازم نیست. اگر RFC قانعکننده است، آن را استقرار دهید، اما با طرح و محدودیت.
ابتدا احصاییه، سپس استانداردسازی. سعی نکنید همه چیز را یکباره بازنویسی کنید. یک لایه را جدا کنید (مثلاً کلاینتهای HTTP یا ورودگی)، یک ابزار واحد انتخاب کنید، ADR بنویسید و تدریجی مهاجرت کنید. روش Strangler Fig — جزءهای قدیمی را یکییکی با جدید جایگزین کنید بدون توقف کار برنامه.
هر چقدر کمتر، بهتر. ایدهآل — یک زبان، یک فریمورک، یک پایگاه داده، یک روش ورودگی. واقعگرایانه — 2–3 زبان (با تفکیک واضح)، 1–2 پایگاه داده، 1–2 فریمورک. هر فناوری اضافی بار شناختی تیم و هزینه پشتیبانی را افزایش میدهد.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید