کپیپیست (copy-paste) — روشی است برای کپی کردن تکههای کد از یک مکان به مکان دیگر بدون تطبیق با زمینه جدید. اغلب توسعهدهنده یک بلوک را از ماژول موجود کپی میکند، حداقل تغییرات را اعمال میکند و در ماژول جدید میچسباند — همراه با باگها، کامنتهای قدیمی و وابستگیهای غیرضروری. بر اساس تحقیق TIOBE Code Quality Survey (2025)، پروژههایی با سطح بالای کپیپیست سه برابر بیشتر نقص در هر هزار خط کد دارند. تکراری شدن کد — تأمینکننده اصلی بدهی فنی: هر کپی نیاز به نگهداری جداگانه دارد و رفع باگ در یک مکان، رفع آن در بقیه را تضمین نمیکند.
نکات اصلی
کپیپیست (copy-paste programming) — انتقال کد موجود به مکان جدید با تغییرات اندک یا بدون تغییر. این اصطلاح به معنای تحقیرآمیز استفاده میشود: نشان میدهد که توسعهدهنده راهحل را طراحی نمیکند، بلکه بلوک آماده را به صورت مکانیکی کپی میکند، اغلب بدون اینکه کاملاً بفهمد چگونه کار میکند.
کپیپیست دو نوع دارد: موجه (intentional) و تصادفی (accidental). موجه — زمانی که توسعهدهنده عمداً کد را با برنامه بازآرایی بعدی کپی میکند (اما اغلب برنامه اجرا نمیشود). تصادفی — زمانی که تکراری شدن ناآگاهانه رخ میدهد، مثلاً دو توسعهدهنده مستقل همان منطق را برای صفحههای مختلف مینویسند.
بر اساس گزارش SonarQube State of Clean Code (2025)، کد تکراری به طور متوسط ۱۲–۱۸ درصد از حجم کل کد در پروژههای تجاری را تشکیل میدهد. در عین حال، هزینه رفع باگ در کد تکراری ۲.۵ برابر بیشتر از کد با پیادهسازی واحد است، زیرا توسعهدهنده باید همه کپیها را پیدا کرده و اصلاح کند.
ابزار اصلی مبارزه با کپیپیست اصل DRY (Don't Repeat Yourself) است. با این حال، مطلقانگاری DRY نیز خطرناک است: گاهی کپی کردن زمانی موجه است که دو کپی باید مستقل از یکدیگر تکامل یابند. مهم است بین «تکراری شدن تصادفی» (که باید حذف شود) و «تکراری شدن ضروری» (که باید مستند شود) تفاوت قائل شوید.
اولین و مهمترین خطر — تکثیر باگها. اگر در کد مبدأ نقصی وجود داشته باشد، همراه با کد به همه مکانهای جدید کپی میشود. وقتی نقص کشف و در ماژول مبدأ رفع میشود، کپیها ناقص میمانند. توسعهدهنده ممکن است حتی نداند که باگ در پنج فایل مختلف وجود دارد.
دومین خطر — تکامل نابرابر. دو کپی از یک الگوریتم به مرور زمان تغییرات متفاوتی پیدا میکنند. در یک کپی اعتبارسنجی مقادیر مرزی اضافه شده، در دیگری — فرمت خروجی تغییر کرده است. پس از چند ماه تشخیص اینکه کدام نسخه «درست» است غیرممکن میشود و پروژه یکپارچگی رفتار خود را از دست میدهد.
سومین خطر — افزایش حجم تستها. هر کپیپیست به تستهای خود نیاز دارد. اگر منطق مشترک در یک تابع واحد استخراج شود، میتوان آن را با یک دسته تست پوشش داد و دوباره استفاده کرد. در تکراری شدن، هر کپی باید جداگانه تست شود — این زمان اجرای CI و حجم پایگاه تست نگهداریشده را چندین برابر افزایش میدهد.
چهارمین خطر — توهم بهرهوری. کپیپیست احساس نادرست سرعت ایجاد میکند: توسعهدهنده سریع کد را وارد میکند و میبیند که صفحه کار میکند. اما این «سرعت» به بدهی فنی تبدیل میشود که وقتی در بلوک تکراری باگی پیدا شود یا تغییر منطق تجاری لازم باشد، باید با بهره بازپرداخت شود.
درک دلایل کپیپیست به ایجاد پیشگیری صحیح کمک میکند. اغلب توسعهدهندگان کد را نه از روی تنبلی، بلکه به دلیل فشار مهلتها، کمبود دانش یا معماری ناخوشایند کپی میکنند.
دلیل اول — مهلتها. وقتی باید صفحهای را در دو روز بسازید و صفحه مشابهی قبلاً پیادهسازی شده، توسعهدهنده آن را به طور کامل کپی میکند و فقط آنچه کاربر میبیند تغییر میدهد. برای بازآرایی با استخراج کامپوننت مشترک وقت نیست — مشتری نتیجه میخواهد. در نتیجه صفحه دومی با ۸۰ درصد کد مشترک، اما با تاریخچه تغییرات مستقل ایجاد میشود.
دلیل دوم — فقدان انتزاع واحد. اگر در پروژه کامپوننت مشترکی برای کار معمولی وجود نداشته باشد (مثلاً صفحه لیست با pull-to-refresh)، هر توسعهدهنده پیادهسازی خود را مینویسد یا از همسایه کپی میکند. تصمیمات معماری گرفتهشده در شروع پروژه مستقیماً بر میزان کپیپیست آینده تأثیر میگذارد.
دلیل سوم — ترس از شکستن کد کار. توسعهدهنده میداند که ماژول موجود کار میکند. بازآرایی با استخراج کد مشترک ممکن است عملکرد موجود را تحت تأثیر قرار دهد. اگر پوشش تست پایین باشد، خطر شکستن از مزیت درکشده بازآرایی بیشتر است و توسعهدهنده مسیر امن — کپی کردن — را انتخاب میکند.
علل را برطرف کنید، نه علائم را. کوتاه کردن مهلتها و پیادهسازی بازبینی کد مشکل را حل نمیکند اگر در پروژه پایه معماری مشترکی وجود نداشته باشد. در مراحل اولیه روی ایجاد کامپوننتهای قابل استفاده مجدد سرمایهگذاری کنید — این تنها راه کاهش وسوسه کپیپیست در آینده است.
جستجوی کپیپیست توسط تحلیلگرهای خودکاری انجام میشود که تکههای کد را مقایسه کرده و تطابقهای بالاتر از آستانه مشخص را تعیین میکنند. بهترین ابزارها در سطح AST (درخت نحوی انتزاعی) کار میکنند و قالببندی، نام متغیرها و کامنتها را نادیده میگیرند.
PMD CPD (Copy-Paste Detector) — رایجترین ابزار برای Java، Kotlin، Swift، JavaScript، Python و C++. CPD توکنهای کد منبع را تحلیل کرده و تکراریهای طولانیتر از حداقل تعداد توکن مشخص (پیشفرض ۱۰۰) را پیدا میکند. تنظیم آستانه کلید نتیجه کیفی است: آستانه خیلی پایین هشدارهای کاذب زیادی میدهد (الگوهای رایج مانند importها)، آستانه خیلی بالا تکراریهای واقعی را از دست میدهد.
plugins {
id 'pmd'
}
pmd {
toolVersion = '7.0.0'
ruleSetFiles = files("pmd-rules.xml")
}
tasks.register('cpd') {
doLast {
exec {
workingDir = projectDir
commandLine 'cpd',
'--minimum-tokens', '75',
'--language', 'kotlin',
'--files', 'src/main/kotlin',
'--format', 'xml',
'--failOnViolation', 'true'
}
}
}
SonarQube آشکارساز تکراریها را مستقیماً در Quality Gate تعبیه میکند. قانون Duplicated Blocks (%) سهم کد تکراری را نشان میدهد. آستانه ۵ درصد برای پروژههای تجاری سالم در نظر گرفته میشود. تجاوز از آن ارتقا به شاخه انتشار را مسدود میکند. SonarQube همچنین تکراریها را بر اساس نوع گروهبندی میکند: کپیهای دقیق (exact match) و کپیهای ساختاری (با نامهای تغییر یافته).
برای JavaScript و TypeScript تکراریها توسط ESLint با افزونه eslint-plugin-sonarjs (قانون no-duplicate-string) و ابزار jscpd که از ۱۵۰+ زبان پشتیبانی میکند جستجو میشوند. jscpd به ویژه برای مخازن یکپارچه مناسب است: تکراریها را بین بستهها پیدا میکند، نه فقط درون یک ماژول.
بازآرایی کپیپیست به یک اصل خلاصه میشود: مشترک را استخراج کنید و تفاوتها را پارامتریزه کنید. روش خاص به حجم تکراری شدن و زمینه بستگی دارد.
سادهترین حالت — تکراری شدن در یک کلاس (مثلاً دو متد با منطق یکسان اما انواع مختلف). راهحل — تعمیم از طریق جنریکها یا استفاده مجدد از متد با پارامتر نوع. اگر تکراری شدن چندین کلاس را پوشش میدهد — کد مشترک را به یک کلاس ابزاری یا تابع گسترش استخراج کنید.
حالت پیچیدهتر — تکراری شدن در سطح صفحات یا ماژولها. در اینجا استخراج ساده تابع کمکی نمیکند، زیرا ساختار UI، منطق چرخه حیات و اتصال داده تکراری میشود. راهحل — ایجاد یک کلاس پایه مشترک برای صفحه یا کامپوننت نمای ترکیبی، و انتقال تفاوتها از طریق پارامترها یا پروتکل.
// قبل - دو کپی از همان UITableViewController
class UserListController: UITableViewController {
private let viewModel = UserListViewModel()
// 40 خط کد
}
class ProductListController: UITableViewController {
private let viewModel = ProductListViewModel()
// همان 40 خط اما با Product به جای User
}
// بعد - کلاس پایه عمومی به اشتراک گذاشته شد
class ListViewController<T: ListViewModel>: UITableViewController {
let viewModel: T
// 40 خط کد - فقط یک بار
init(viewModel: T) {
self.viewModel = viewModel
super.init(style: .plain)
}
}
پیچیدهترین حالت — تکراری شدن بین میکروسرویسها یا کتابخانهها. استخراج کد مشترک ممکن است به وابستگیهای چرخهای یا اتصال ناروا منجر شود. در چنین مواردی کپیپیست میتواند تصمیمی آگاهانه باشد: دو تیم سرویسهای مستقل را نگهداری میکنند و کتابخانه مشترک مشکلات بیشتری نسبت به راهحلها ایجاد میکند. مهمترین نکته — مستند کردن چنین تصمیمی و بررسی منظم اینکه آیا کپیها آنقدر از هم فاصله گرفتهاند که زمان یکپارچهسازی فرا رسیده است.
پیشگیری از کپیپیست مؤثرتر از بازآرایی کد از قبل تکراری شده است. اقدامات پیشگیرانه اصلی در سازماندهی فرآیند توسعه نهفته است، نه در فناوریها.
اولین اقدام — بازبینی کد با تأکید بر تکراریها. چکلیست بازبینی باید شامل این نکته باشد: «آیا در این درخواست ادغام کدی وجود دارد که قبلاً در پروژه موجود است؟». اگر بازبین کپیپیست ببیند — ادغام را تا استخراج کامپوننت مشترک مسدود میکند. این الزام باید در Definition of Done تیم ثبت شود.
دومین اقدام — کتابخانه مشترک کامپوننتها. هر الگوی UI که در دو یا چند صفحه ظاهر میشود باید به یک ماژول مشترک استخراج شود. یک ماژول اشتراکی در پروژه ایجاد کنید و آن را به نقطه ورود اجباری برای همه کامپوننتهای UI تبدیل کنید. اگر کامپوننتی وجود ندارد — ابتدا آن را ایجاد کنید، سپس در صفحه استفاده کنید.
سومین اقدام — خودکارسازی در CI/CD. گامی با بررسی کد تکراری (PMD CPD, jscpd, SonarQube) به خط لوله اضافه کنید. تجاوز از آستانه — خطای ساخت. توسعهدهنده نمیتواند درخواست ادغامی را که سهم کپیپیست را فراتر از سطح مجاز افزایش میدهد ادغام کند. این مسئولیت را از بازبینی کد به خودکارسازی منتقل میکند و تضمین میکند که هیچ تکراری نادیده گرفته نمیشود.
فرهنگ «یک پیادهسازی — یک مکان» را پیادهسازی کنید. اگر امکان استفاده مجدد میبینید — بازآرایی را به بعد موکول نکنید. هر کپیپیستی که «برای بعد» باقی میماند تکثیر شده و به بدهی فنی غیرقابل کنترل تبدیل میشود.
سوالات متداول
خیر، سناریوهای تکراری شدن آگاهانه وجود دارد: میکروسرویسهای مختلف که باید مستقل تکامل یابند؛ کد کپیشده برای آزمایش با برنامه حذف؛ DTOهای قالبی برای نسخههای مختلف API. مهم است که دلیل را مستند کرده و مهلت بررسی برای بازآرایی تعیین کنید.
کپیپیست — زمانی که دو بخش کد کار یکسان انجام میدهند اما انتزاع مشترکی ندارند. استفاده مجدد سالم — زمانی که کد مشترک به تابع، کلاس یا ماژول استخراج شده و تفاوتها پارامتریزه شدهاند. اگر تغییر منطق نیاز به اصلاح در سه یا بیشتر مکان دارد — این کپیپیست است.
PMD CPD از Swift و Objective-C پشتیبانی میکند. برای Xcode افزونههایی مانند SwiftCop و آشکارساز تکراری تعبیهشده در AppCode وجود دارد. SonarQube نیز پروژههای Swift را تحلیل کرده و بلوکهای تکراری را مستقیماً در pull request نشان میدهد.
برای هر کپی بزرگ یک تسک فنی بازآرایی ایجاد کنید. اولویت را تعیین کنید: صفحههایی که مرتب تغییر میکنند — در اولویت اول، پایدارها — در اولویت دوم. برای هر درخواست ادغام جدید که کد تکراری را لمس میکند، ۱۵–۲۰ درصد زمان را به یکپارچهسازی تدریجی اختصاص دهید.
بله، دستیارهای هوش مصنوعی مدرن (GitHub Copilot, Codeium) میتوانند زمینه را تحلیل کرده و هنگام تشخیص الگوهای تکراری، استخراج کد مشترک را پیشنهاد دهند. با این حال آنها جایگزین تحلیلگرهای خودکار نمیشوند — از Copilot برای پیشگیری و از CPD / SonarQube برای تشخیص استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.