Code Smell হলো কোডের একটি উপরিভাগের সূচক যা অ্যাপ্লিকেশনের ডিজাইন বা আর্কিটেকচারে সম্ভাব্য সমস্যার ইঙ্গিত দেয়। শব্দটি তৈরি করেছিলেন কেন্ট বেক এবং মার্টিন ফাওলার “Refactoring: Improving the Design of Existing Code” বইয়ে এটি জনপ্রিয় করেছিলেন। Martin Fowler-এর মতে, কোড গন্ধ অগত্যা বাগ বোঝায় না, তবে এটি প্রায় সবসময় রক্ষণাবেক্ষণযোগ্যতা উন্নত করতে রিফ্যাক্টরিংয়ের প্রয়োজনীয়তা নির্দেশ করে।
মূল বিষয়
Code Smell সোর্স কোডের উপসর্গের জন্য একটি রূপক যা উচ্চ সম্ভাবনার সাথে গভীর সমস্যা নির্দেশ করে। শব্দটির কোনো আনুষ্ঠানিক সংজ্ঞা নেই — এটি ডেভেলপারদের অভিজ্ঞতার উপর ভিত্তি করে একটি হিউরিস্টিক। মার্টিন ফাওলার এবং কেন্ট বেক 1999 সালে “Refactoring” বইয়ে প্রথম 22টি গন্ধকে পদ্ধতিগতভাবে সাজান, এবং তাদের বেশিরভাগই দশকের পর দশক পরেও প্রাসঙ্গিক রয়েছে।
Code Smell এবং বাগের মধ্যে পার্থক্য বোঝা গুরুত্বপূর্ণ। গন্ধ কোনো ত্রুটি নয়: কোড কম্পাইল হয়, কাজ করে এবং সঠিক ফলাফল দেয়। সমস্যা হলো এই ধরনের কোড পড়া, পরিবর্তন করা এবং পরীক্ষা করা কঠিন। সময়ের সাথে সাথে, প্রতিটি পরিবর্তনের খরচ বাড়ে এবং রিফ্যাক্টরিংয়ের সঠিকতার উপর আস্থা কমে। স্ট্যাটিক অ্যানালাইসিস টুল (SonarQube, Detekt, SwiftLint) স্বয়ংক্রিয়ভাবে অনেক গন্ধ সনাক্ত করে।
Code Smell-এর হিউরিস্টিক প্রকৃতি মানে এই নয় যে প্রতিটি লম্বা মেথডকে ভাগ করতে হবে, এবং প্রতিটি বড় ক্লাসের রিফ্যাক্টরিং প্রয়োজন। সিদ্ধান্ত ডেভেলপার প্রসঙ্গ মূল্যায়ন করে নেয়: পরিবর্তনের ফ্রিকোয়েন্সি, মডিউলের গুরুত্ব, উন্নয়ন পরিকল্পনা। অভিজ্ঞ ইঞ্জিনিয়াররা স্বজ্ঞাতভাবে গন্ধ অনুভব করেন — কোড “দুর্গন্ধযুক্ত” হয় যদিও সব আনুষ্ঠানিক নিয়ম পালন করা হয়।
ফাওলার 22টি গন্ধ চিহ্নিত করেছেন যা কয়েকটি বিভাগে বিভক্ত। মোবাইল ডেভেলপমেন্টের জন্য, সবচেয়ে প্রাসঙ্গিক হলো কাঠামোগত গন্ধ, অবজেক্ট-ওরিয়েন্টেড ডিজাইনের গন্ধ এবং প্ল্যাটফর্মের সীমাবদ্ধতার সাথে সম্পর্কিত নির্দিষ্ট সমস্যা। চলুন বাস্তব উদাহরণ সহ প্রতিটি গ্রুপ পরীক্ষা করি।
Long Method মোবাইল অ্যাপ্লিকেশনে সবচেয়ে সাধারণ গন্ধ। একটি রেজিস্ট্রেশন ফর্ম স্ক্রিনে প্রায়ই 200+ লাইনের একটি setupUI মেথড থাকে যা সব Views তৈরি করে, কনস্ট্রেইন্ট সেট করে, ইভেন্টে সাবস্ক্রাইব করে এবং ত্রুটিগুলি হ্যান্ডেল করে। সমাধান: লজিক্যাল ব্লক অনুযায়ী মেথডে ভাগ করুন — configureEmailField, configurePasswordField, setupConstraints, bindViewModel।
Large Class — একটি Activity বা ViewController যা প্রদর্শন, নেভিগেশন, বিজনেস লজিক এবং নেটওয়ার্ক ইন্টারঅ্যাকশন সবকিছুর জন্য দায়ী। এই ধরনের ক্লাস Single Responsibility Principle লঙ্ঘন করে এবং ডজন ডজন ফিল্ড ও মেথড ধারণ করে। Android-এ, এটি প্রায়ই 1000+ লাইনের একটি Fragment যা বিভিন্ন স্ক্রিনের যুক্তি ধারণ করে। সমাধান: presenter/ViewModel বের করুন, নেটওয়ার্ক কোড রিপোজিটরিতে এবং নেভিগেশন কোঅর্ডিনেটরে স্থানান্তর করুন।
Duplicate Code — অ্যাপ্লিকেশনের বিভিন্ন অংশে অভিন্ন ব্লক কপি করা। একটি সাধারণ উদাহরণ: দুটি স্ক্রিন একটি প্রোডাক্ট কার্ড প্রদর্শন করছে — ক্যাটালগে এবং পছন্দের তালিকায়। যদি প্রদর্শনের যুক্তি কপি করা হয়, তাহলে এক জায়গায় বাগ ঠিক করলে অন্য জায়গায় ঠিক হবে না। সমাধান: সাধারণ যুক্তি একটি পুনঃব্যবহারযোগ্য কম্পোনেন্ট বা এক্সটেনশনে বের করুন।
Feature Envy — একটি ক্লাসের মেথড অন্য ক্লাসের ডেটা প্রচুর পরিমাণে ব্যবহার করে। Android-এ, এটি প্রকাশ পায় যখন ViewModel সরাসরি User মডেলের ফিল্ড অ্যাক্সেস করে মডেলের মেথড কল করার পরিবর্তে। সংকেত: যদি একটি মেথডকে সেই ক্লাসে স্থানান্তর করা যায় যার ডেটা এটি ব্যবহার করে — স্থানান্তর করুন। Switch Statements (শর্ত শৃঙ্খল) — একটি switch নির্মাণ বা if-else শৃঙ্খল যা অবজেক্টের টাইপ পরীক্ষা করে। এর পরিবর্তে, পলিমরফিজম বা strategy প্যাটার্ন ব্যবহার করুন।
Data Class — একটি ক্লাস যা শুধু ডেটা সংরক্ষণ করে কিন্তু কোনো আচরণ ধারণ করে না। Kotlin-এ data classes বা Swift-এ স্ট্রাকচার নিজেরা গন্ধ নয়। সমস্যা তখন দেখা দেয় যখন সেই ডেটার সাথে কাজ করা বিজনেস লজিক এনক্যাপসুলেটেড না হয়ে কোডবেস জুড়ে ছড়িয়ে থাকে। Refused Bequest — একটি সাবক্লাস পিতামাতার অধিকাংশ মেথড ব্যবহার করে না এবং সেগুলো খালি স্টাব দিয়ে ওভাররাইড করে। ভুল উত্তরাধিকারের লক্ষণ: উত্তরাধিকারকে কম্পোজিশন দিয়ে প্রতিস্থাপন করুন।
God Activity / God Fragment — একটি Activity বা Fragment যা সবকিছু জানে: লাইফসাইকেল, ডেটা, নেভিগেশন, অনুমতি, DI। এটি অ্যাপ্লিকেশনে রক্ষণাবেক্ষণের জন্য সবচেয়ে ব্যয়বহুল ক্লাস। সমাধান: MVVM, MVI বা Clean Architecture আর্কিটেকচারাল প্যাটার্ন দায়িত্ব পৃথক করে। Giant ViewController — iOS-এর সমতুল্য, যেখানে UIViewController স্ক্রিনের সমস্ত যুক্তি ধারণ করে এবং প্রায়ই 500 লাইন অতিক্রম করে।
Hardcoded Resources — স্ট্রিং, রঙ, আকার, API URL সরাসরি কোডে এম্বেড করা। Android-এ, এটি R রিসোর্স সিস্টেম লঙ্ঘন করে; iOS-এ, NSLocalizedString এবং Asset Catalog। সমাধান: সব স্ট্রিং strings.xml বা Localizable.strings-এ, URL কনফিগ ফাইলে, আকার dimens-এ স্থানান্তর করুন। Leaking Context — Activity বা ViewController-এর রেফারেন্স কম্পোনেন্টের জীবনকালের চেয়ে বেশি সময় ধরে রাখা। মেমরি লিক এবং ক্র্যাশের কারণ হয়। সমাধান: দুর্বল রেফারেন্স, Jetpack Lifecycle, RxSwift DisposeBag।
| গন্ধ | কোথায় দেখা যায় | সমাধান |
|---|---|---|
| Long Method | Android/iOS | Extract Method, বিভাজন |
| Large Class | Activity, ViewController | MVVM, VIPER, Clean Arch |
| Duplicate Code | যেকোনো স্ক্রিন | শেয়ার্ড কম্পোনেন্ট, DRY |
| Feature Envy | ViewModel, Presenter | Move Method |
| Leaking Context | Android | Lifecycle-aware কম্পোনেন্ট |
কোড রিভিউ গন্ধ সনাক্ত করার সবচেয়ে নির্ভরযোগ্য উপায়। মানুষের চোখ অপ্রাকৃতিক কাঠামো লক্ষ্য করে যা স্বয়ংক্রিয় বিশ্লেষক মিস করে। কোড রিভিউ কার্যকারিতা বাড়ে যখন টিম সাধারণ গন্ধের একটি চেকলিস্ট ব্যবহার করে। প্রতি সেশনে 200–400 লাইনের বেশি কোড রিভিউ না করার পরামর্শ দেওয়া হয় — এই সীমার পরে মনোযোগ কমে এবং গন্ধগুলি এড়িয়ে যেতে শুরু করে।
স্ট্যাটিক অ্যানালাইসিস কাঠামোগত গন্ধের অনুসন্ধান স্বয়ংক্রিয় করে। Android-এর জন্য, মানক টুল হলো Detekt (Kotlin) এবং Android Lint; iOS-এর জন্য, SwiftLint এবং SonarQube। এই টুলগুলি লম্বা মেথড, বড় ক্লাস, ডুপ্লিকেট কোড এবং আরও অনেক সমস্যা খুঁজে পায়। প্রকল্প অনুযায়ী নিয়ম টিউন করা গুরুত্বপূর্ণ — ডিফল্ট কনফিগারেশন প্রায়ই খুব কঠোর বা বিপরীতভাবে গুরুত্বপূর্ণ গন্ধ উপেক্ষা করে।
কোড মেট্রিক্স বস্তুনিষ্ঠ মানদণ্ড প্রদান করে: চক্রীয় জটিলতা (সীমা >10 মনোযোগ প্রয়োজন), প্রতি মেথডে কোডের লাইন (সীমা >30), উত্তরাধিকারের গভীরতা (>3 — চিন্তা করার কারণ)। CodeMetrics (Xcode) এবং Gradle Metrics Plugin-এর মতো টুল সময়ের সাথে মেট্রিক্স পরিবর্তনের গ্রাফ তৈরি করে। যদি শেষ commit-এর পরে কোনো মেথডের জটিলতা 5 থেকে 15 বেড়ে যায় — এটি রিফ্যাক্টরিংয়ের সংকেত।
// উদাহরণ: চক্রীয় জটিলতা = 7 সহ মেথড (সীমা 5 এর উপরে)
fun processOrder(order: Order) {
if (order.status == Status.NEW) { /* 10 লাইন */ }
else if (order.status == Status.PAID) { /* 15 লাইন */ }
else if (order.status == Status.SHIPPED) { /* 20 লাইন */ }
else if (order.status == Status.DELIVERED) { /* 8 লাইন */ }
else if (order.status == Status.CANCELLED) { /* 5 লাইন */ }
else { throw IllegalStateException() }
}
// সমাধান: switch-এর পরিবর্তে পলিমরফিজম
interface OrderHandler {
fun handle(order: Order)
}
স্বয়ংক্রিয় গন্ধ সনাক্তকরণ কোড রিভিউ প্রতিস্থাপন করে না: স্ট্যাটিক অ্যানালাইজার শুধু কাঠামোগত সমস্যা খুঁজে পায় কিন্তু শব্দার্থগত গন্ধ (Feature Envy, Inappropriate Intimacy) ধরে না। স্বয়ংক্রিয় টুল এবং মানব রিভিউয়ের সংমিশ্রণ সর্বোত্তম ফলাফল দেয়। আপনার CI/CD পাইপলাইন এমনভাবে কনফিগার করুন যাতে জটিলতা বা মেথড দৈর্ঘ্যের সীমা অতিক্রম করলে বিল্ড ব্যর্থ হয়।
রিফ্যাক্টরিং কোড গন্ধ দূর করার প্রাথমিক পদ্ধতি। ফাওলার ডজন ডজন রিফ্যাক্টরিং কৌশল বর্ণনা করেছেন, প্রতিটি নির্দিষ্ট গন্ধে প্রযোজ্য। Extract Method — লম্বা মেথডের জন্য, Extract Class — বড় ক্লাসের জন্য, Move Method — Feature Envy-এর জন্য। প্রতিটি পরিবর্তনের পরে কোড কার্যকর রেখে ছোট ছোট ধাপে রিফ্যাক্টরিং করা গুরুত্বপূর্ণ।
রিফ্যাক্টরিংয়ের আগে পরীক্ষা বাধ্যতামূলক। যদি কোড ইউনিট টেস্ট দ্বারা আচ্ছাদিত না হয়, তাহলে রিফ্যাক্টরিং অজানা ফলাফলের সাথে পুনর্লিখনে পরিণত হয়। টেস্ট ছাড়া লিগ্যাসি কোডের জন্য, Characterization Tests ব্যবহার করুন — বর্তমান আচরণ ক্যাপচার করে এমন টেস্ট লিখুন, তারপর রিফ্যাক্টর করুন। টেস্টিং আস্থা দেয় যে রিফ্যাক্টরিংয়ের পরে বিজনেস লজিক ভাঙেনি।
ক্রমিকতা মোবাইল ডেভেলপমেন্টে গন্ধ সফলভাবে ঠিক করার চাবিকাঠি। God Activity সম্পূর্ণভাবে পুনরায় লেখার চেষ্টা করবেন না। প্রথমে নেভিগেশন স্তর বের করুন, তারপর ডেটা স্তর, তারপর প্রদর্শনের যুক্তি। প্রতিটি ধাপ একটি commit এবং টেস্ট রানের সাথে হওয়া উচিত। ব্যবহারকারীদের একটি উপসেটের জন্য রিফ্যাক্টরিং সক্ষম করতে এবং সমস্যা হলে ফিরিয়ে আনতে feature toggle ব্যবহার করুন।
IDE টুল অনেক রিফ্যাক্টরিং কৌশল স্বয়ংক্রিয় করে। Android Studio এবং IntelliJ IDEA বিল্ট-ইন রিফ্যাক্টরিং অফার করে: Extract Method, Extract Interface, Pull Members Up, Encapsulate Fields। Xcode (সংস্করণ 14 থেকে শুরু) Swift-এর জন্য রিফ্যাক্টরিং সমর্থন উন্নত করেছে। স্বয়ংক্রিয় রিফ্যাক্টরিং ব্যবহার ম্যানুয়াল কোড কপি করার তুলনায় ত্রুটির ঝুঁকি কমায়।
মোবাইল ডেভেলপমেন্ট প্ল্যাটফর্মের সীমাবদ্ধতার সাথে সম্পর্কিত নিজস্ব নির্দিষ্ট গন্ধ যোগ করে। Android-এ, এর মধ্যে রয়েছে Context লিক, বন্ধ না করা Cursor, এবং Lifecycle-এর ভুল ব্যবহার। iOS-এ, closures-এর মাধ্যমে retain cycle, Auto Layout-এর ভুল পরিচালনা এবং বিশাল ViewController। এই গন্ধগুলি শুধু রক্ষণাবেক্ষণযোগ্যতাই খারাপ করে না বরং সরাসরি অ্যাপ্লিকেশনের কর্মক্ষমতা এবং স্থিতিশীলতাকে প্রভাবিত করে।
Callback Hell অ্যাসিঙ্ক্রোনাস অপারেশনের সাথে কাজ করা কোডের একটি বৈশিষ্ট্যপূর্ণ গন্ধ। নেস্টেড কলব্যাক কোডকে অপাঠ্য এবং ডিবাগ করা কঠিন করে তোলে। সমাধান: coroutines (Kotlin), async/await (Swift 5.5+), RxJava/RxSwift বা Combine। Google I/O 2023 অনুযায়ী, যে প্রকল্পগুলি কলব্যাক স্টাইল থেকে coroutines-এ স্থানান্তরিত হয়েছে তারা বাগের সংখ্যা 30% কমিয়েছে এবং নতুন ফিচার যোগ করা ত্বরান্বিত করেছে।
Platform Coupling — প্ল্যাটফর্ম কম্পোনেন্টের সাথে বিজনেস লজিকের দৃঢ় সংযুক্তি। এই ধরনের লজিক পরীক্ষা করতে ইমুলেটর চালানোর প্রয়োজন হয়, যা ফিডব্যাক চক্র ধীর করে। সমাধান: Clean Architecture কোডকে Domain (প্ল্যাটফর্ম নির্ভরতা ছাড়া বিশুদ্ধ Kotlin/Swift) এবং Data/UI (প্ল্যাটফর্ম নির্ভরতা সহ) স্তরে পৃথক করে। বিজনেস লজিক ইমুলেটর ছাড়া JVM-এ পরীক্ষা করা হয়।
সচরাচর জিজ্ঞাসিত প্রশ্ন
না — Code Smell কোনো ত্রুটি নয়। গন্ধযুক্ত কোড সঠিকভাবে কাজ করে, কিন্তু এটি রক্ষণাবেক্ষণ, পরিবর্তন এবং পরীক্ষা করা কঠিন। বাগ হলো ভুল আচরণ; গন্ধ ভবিষ্যতের সম্ভাব্য সমস্যার সতর্কতা।
22টি গন্ধ “Refactoring” (2019) দ্বিতীয় সংস্করণে। তার মধ্যে Long Method, Large Class, Primitive Obsession, Data Clumps, Switch Statements, Speculative Generality এবং অন্যান্য অন্তর্ভুক্ত। সম্প্রদায় আধুনিক প্যারাডাইম এবং প্ল্যাটফর্মের জন্য ডজন ডজন নতুন গন্ধ যোগ করেছে।
একটি সংমিশ্রণ সর্বোত্তম ফলাফল দেয়: স্বয়ংক্রিয় বিশ্লেষণের জন্য Detekt (Android/Kotlin), SwiftLint (iOS), SonarQube (উভয়) এবং শব্দার্থগত গন্ধের জন্য কোড রিভিউ। কোনো একক টুল 100% সমস্যা খুঁজে পায় না — মানব অভিজ্ঞতা নির্ণায়ক থাকে।
হ্যাঁ, যদি কোড খুব কমই পরিবর্তিত হয় বা শীঘ্রই সম্পূর্ণরূপে পুনর্লিখিত হবে। তবে, গন্ধ জমা হওয়া টেকনিক্যাল ডেটে পরিণত হয়: প্রতিটি নতুন পরিবর্তন কঠিন হতে থাকে এবং ঠিক করার খরচ দ্রুতগতিতে বাড়ে।
হ্যাঁ — ডিক্লারেটিভ ফ্রেমওয়ার্ক নতুন গন্ধের জন্ম দিয়েছে: বিশাল @State ব্লক, পুনরাবৃত্ত রেন্ডারের ভুল পরিচালনা, অত্যধিক পুনঃগঠন এবং পৃথক Views-এ এক্সট্র্যাকশনের অভাব। SwiftUI-এর জন্য, একটি সাধারণ গন্ধ হলো ডজন ডজন @State ভেরিয়েবলযুক্ত Massive View।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন