প্রোগ্রামিং-এ জাদু কোনো রূপক নয়, বরং একটি নির্দিষ্ট শব্দ যা সেই মানগুলিকে (সংখ্যা, স্ট্রিং, ফ্ল্যাগ) বোঝায় যাদের অর্থ প্রসঙ্গ থেকে স্পষ্ট নয় এবং বুঝতে বাহ্যিক জ্ঞানের প্রয়োজন হয়। জাদুর সবচেয়ে সাধারণ রূপ হল ম্যাজিক নাম্বার: সংখ্যাগত ধ্রুবক যা কোনো ব্যাখ্যা ছাড়াই সরাসরি কোডে লেখা হয় কেন এই নির্দিষ্ট মানটি বেছে নেওয়া হয়েছে। SonarSource কোড গুণমান রিপোর্ট (2025) অনুসারে, সমস্ত স্ট্যাটিক বিশ্লেষক সতর্কতার প্রায় ৮ শতাংশ অব্যক্ত লিটারেলের সাথে সম্পর্কিত। জাদুকরী মান কোডকে ভঙ্গুর করে: পরিবর্তনের জন্য সমস্ত অবস্থান খুঁজে বের করতে হয়, এবং একজন নতুন ডেভেলপার বুঝতে পারেন না যে সংখ্যাটি স্পর্শ করা যাবে নাকি এটি সিস্টেমের কাজের জন্য গুরুত্বপূর্ণ।
মূল বিষয়
জাদু হলো সোর্স কোডের এমন কোনো মান যার অর্থ অতিরিক্ত ডোমেইন জ্ঞান ছাড়া স্পষ্ট নয়। শব্দটি কমিউনিটিতে প্রতিষ্ঠিত: যদি একজন ডেভেলপার কোনো সংখ্যা দেখে এবং বলতে না পারে যে এটি কোথা থেকে এসেছে — সেটাই জাদু।
জাদু বিভিন্ন ধরণের হয়: সংখ্যাগত (ম্যাজিক নাম্বার), স্ট্রিং (ম্যাজিক স্ট্রিং), বুলিয়ান (ম্যাজিক ফ্ল্যাগ), এবং কনফিগারেশন (হার্ডকোডেড প্যারামিটার যা সেটিংসে থাকা উচিত)। চার ধরণেরই একটি সমস্যা ভাগ করে: যখন কোনো প্রয়োজনীয়তা পরিবর্তিত হয়, ডেভেলপারকে মানটি ব্যবহৃত প্রতিটি স্থান খুঁজে বের করে ম্যানুয়ালি প্রতিস্থাপন করতে হয়। একটি মাত্র অবস্থান মিস করলে বাগ তৈরি হয়।
JetBrains কোড গুণমান জরিপ (2025) অনুসারে, ৭৩ শতাংশ ডেভেলপার ম্যাজিক নাম্বারকে নিম্ন কোড গুণমানের সূচক মনে করেন, যখন ৪১ শতাংশ স্বীকার করেন যে তারা মাঝে মাঝে এগুলো রেখে দেন। প্রধান কারণ হল তাড়াহুড়ো: “আমি পরে কনস্ট্যান্ট যোগ করব” — কিন্তু সেই পরে আর আসে না, এবং এক মাস পর সংখ্যা 0.85 ব্যাখ্যা ছাড়াই মেথডের মাঝখানে থেকে যায়।
মূল নিয়ম: 0, 1, true, false এবং খালি স্ট্রিং ছাড়া প্রতিটি লিটারেল মানকে নামযুক্ত কনস্ট্যান্টে নেওয়া উচিত। ব্যতিক্রম: কাউন্টার বৃদ্ধি (i + 1), গাণিতিক শূন্য (0 চেক করা), এবং সঞ্চালকের প্রাথমিক মান। বাকি সবকিছু নামকরণের জন্য প্রার্থী।
ম্যাজিক নাম্বার হলো একটি সংখ্যাগত লিটারেল যার মান প্রসঙ্গ থেকে স্পষ্ট নয়। একটি ক্লাসিক উদাহরণ: টাইমআউট সম্পর্কিত কোডে 86400। ডেভেলপার সংখ্যাটি দেখে এবং অনুমান করতে বাধ্য হন যে এটি এক দিনে সেকেন্ডের সংখ্যা। যদি তিনি ভুল করে 84600 লেখেন, তাহলে বাগটি ধরা কঠিন হবে কারণ টাইমআউট ১৮ মিনিট আগে সক্রিয় হবে।
কেন ম্যাজিক নাম্বার বিপজ্জনক: প্রথমত, এরা পাঠযোগ্যতা নষ্ট করে। সংখ্যা 1024 এর অর্থ কিলোবাইট আকার, পেজিনেশন সীমা, বা আইটেমের সর্বোচ্চ সংখ্যা হতে পারে। প্রসঙ্গ ছাড়া — এটি শুধু একটি সংখ্যা। দ্বিতীয়ত, এরা পুনরাবৃত্তি তৈরি করে: যদি 1024 পাঁচ জায়গায় ব্যবহৃত হয়, তাহলে যখন সীমা 2048 এ পরিবর্তিত হয়, ডেভেলপারকে সব পাঁচটি খুঁজে বের করে প্রতিস্থাপন করতে হয়। যদি একটি জায়গা মিস হয়, সিস্টেম ভুলভাবে কাজ করে কিন্তু স্পষ্ট ত্রুটি ছাড়া।
// before - magic in its pure form
fun calculateTimeout(base: Int): Int {
return base * 3 + 5000
}
// after - values replaced with constants
private const val RETRY_MULTIPLIER = 3
private const val BASE_TIMEOUT_MS = 5000
fun calculateTimeout(base: Int): Int {
return base * RETRY_MULTIPLIER + BASE_TIMEOUT_MS
}
তৃতীয় বিপদ হল পরীক্ষার অক্ষমতা। যদি সীমার মান লিটারেল হিসেবে হার্ডকোড করা থাকে, পরীক্ষা এটি ওভাররাইড করে সীমানা শর্ত যাচাই করতে পারে না। companion object বা কনফিগারেশন ফাইলে নেওয়া কনস্ট্যান্ট কোডকে পরীক্ষাযোগ্য করে: পরীক্ষা একটি ভিন্ন মান বসিয়ে সীমানায় সিস্টেম আচরণ যাচাই করে।
একটি অভ্যাস গড়ে তুলুন: যখনই আপনি 0, 1, 100 বা 2 ছাড়া কোনো সংখ্যা লেখেন — থামুন এবং ভাবুন এটি কনস্ট্যান্টে নেওয়া উচিত কিনা। যদি সংখ্যাটি ব্যবসায়িক যুক্তির (সীমা, থ্রেশহোল্ড, টাইমআউট, আকার) সাথে সম্পর্কিত হয় — বিনা দ্বিধায় নিন। যদি সংখ্যাটি গাণিতিক ধ্রুবক (pi, e) হয় — স্ট্যান্ডার্ড লাইব্রেরি ব্যবহার করুন (Math.PI, Math.E)।
ম্যাজিক স্ট্রিং হলো স্ট্রিং লিটারেল যা কনস্ট্যান্ট বা রিসোর্সে না এনে কোডে এম্বেড করা হয়। সাধারণ উদাহরণ: এন্ডপয়েন্ট URL, SharedPreferences কী নাম, Intent Actions, bundle কী, ফাইলের নাম এবং SQL কোয়েরি।
ম্যাজিক স্ট্রিং-এর বিপদ হল কম্পাইল-টাইম চেকের অভাব। “user_prefs” স্ট্রিং-এ টাইপো রানটাইম পর্যন্ত ধরা পড়বে না। যদি স্ট্রিংটি দশ জায়গায় ব্যবহৃত হয় এবং ডেভেলপার একটিতে “user_pref” (s ছাড়া) লেখেন — অ্যাপ ক্র্যাশ করে না, কিন্তু ডেটা সংরক্ষিত হয় না। এই ধরনের বাগ মাসের পর মাস প্রোডাকশনে থাকতে পারে কারণ এটি ক্র্যাশ ঘটায় না।
Android প্রকল্পের জন্য, ম্যাজিক স্ট্রিং রিসোর্সে (strings.xml, arrays.xml) বা companion object-এর কনস্ট্যান্টে নেওয়া উচিত। iOS-এর জন্য — স্ট্রিং রিসোর্সে (Localizable.strings) বা enum কনস্ট্যান্টে। ব্যাকএন্ডের জন্য — কনফিগারেশন ফাইলে (.env, application.properties)। কোনো কী, URL বা পাথ কোডে স্ট্রিং লিটারেল হিসেবে থাকা উচিত নয়।
// before - magic strings across the class
let prefs = UserDefaults.standard
prefs.set(token, forKey: "auth_token")
prefs.set(userId, forKey: "current_user_id")
// after - strings extracted to enum
enum PrefKeys: String {
case authToken = "auth_token"
case currentUserId = "current_user_id"
}
prefs.set(token, forKey: PrefKeys.authToken.rawValue)
prefs.set(userId, forKey: PrefKeys.currentUserId.rawValue)
যে স্ট্রিংগুলি পুনরাবৃত্তি হয় সেগুলির প্রতি বিশেষ মনোযোগ দিন। যদি একই কী “user_settings” তিনটি ফাইলে দেখা যায় — ৯৯ শতাংশ সম্ভাবনা যে শেষ পর্যন্ত একটিতে টাইপো হবে। enum বা কনস্ট্যান্টে নেওয়া নিশ্চিত করে যে সব রেফারেন্স একই মান ব্যবহার করে।
ম্যাজিক ফ্ল্যাগ হলো বুলিয়ান প্যারামিটার যাদের অর্থ কল প্রসঙ্গ থেকে স্পষ্ট নয়। একটি ক্লাসিক অ্যান্টি-প্যাটার্ন: কোনো মেথডে true বা false পাঠানো ব্যাখ্যা ছাড়া যে ফ্ল্যাগটি আসলে কী সক্ষম বা অক্ষম করে।
উদাহরণ: userDao.fetch(includeDeleted = false)। একজন ডেভেলপার false দেখেন এবং বলতে পারেন না এর অর্থ “মুছে ফেলা অন্তর্ভুক্ত করবেন না” নাকি “সক্রিয় অন্তর্ভুক্ত করবেন না”। এক মাস পরে, false true-তে পরিণত হয়, এবং আউটপুটে মুছে ফেলা রেকর্ড দেখা দিতে শুরু করে। বাগটি শুধুমাত্র প্রোডাকশনে আবিষ্কৃত হয়।
সমাধান হল বুলিয়ান ফ্ল্যাগকে enum বা sealed class দিয়ে প্রতিস্থাপন করা। Boolean প্যারামিটারের পরিবর্তে, UserFilter.includeDeleted বা UserFilter.activeOnly ব্যবহার করুন। এভাবে কোড তার উদ্দেশ্য নথিভুক্ত করে, এবং IDE স্বয়ংসম্পূর্ণতার সময় উপলব্ধ বিকল্পগুলির পরামর্শ দেয়।
যদি একটি বুলিয়ান ফ্ল্যাগ একাধিক স্তরের মাধ্যমে পাঠানো হয় — এটি আরেকটি সংকেত যে অ্যাবস্ট্রাকশন ভুল। তিন স্তরের কলের মাধ্যমে ফ্ল্যাগ টেনে আনার পরিবর্তে, ভাবুন ফিল্টার নির্বাচনটি উপরের স্তরে নেওয়া উচিত কিনা এবং প্রস্তুত কনফিগারেশন হিসেবে পাঠানো উচিত। কোডে যত কম বুলিয়ান ফ্ল্যাগ থাকবে — তত কম জাদু।
একটি নিয়ম গ্রহণ করুন: কোনো বুলিয়ান প্যারামিটার নামযুক্ত আর্গুমেন্ট ছাড়া মেথডে পাঠানো হয় না (যদি ভাষা নামযুক্ত আর্গুমেন্ট সমর্থন করে)। Kotlin এবং Swift-এ, এই প্রয়োজনীয়তা স্বয়ংক্রিয়ভাবে পূর্ণ হয়। Java-তে, true/false-এর পরিবর্তে Builder বা enum কনস্ট্যান্ট ব্যবহার করুন।
জাদুকরী মান অনুসন্ধান স্ট্যাটিক বিশ্লেষক দ্বারা স্বয়ংক্রিয় হয় যা অপ্রত্যাশিত স্থানে লিটারেল শনাক্ত করতে কনফিগার করা হয়। প্রতিটি ভাষা কাস্টমাইজযোগ্য ব্যতিক্রম সহ নিজস্ব সরঞ্জাম সরবরাহ করে।
| সরঞ্জাম | ভাষা | নিয়ম |
|---|---|---|
| SonarQube | Java, Kotlin, Swift, Python, JS | MagicNumber, HardcodedString |
| ESLint | JavaScript, TypeScript | no-magic-numbers, no-hardcoded-strings |
| Detekt | Kotlin | MagicNumber, ComplexCondition |
| SwiftLint | Swift | magic_number (opt-in) |
| PMD | Java, Apex, PLSQL | MagicNumber (কনফিগারযোগ্য অনুমোদিত তালিকা) |
| PhpStorm পরিদর্শন | PHP | NumericLiteralWithContext (অন্তর্নির্মিত পরিদর্শন) |
ব্যতিক্রম কনফিগার করা গুরুত্বপূর্ণ — এটি ছাড়া, বিশ্লেষক প্রতিটি বৃদ্ধি (-1, +1) এবং গাণিতিক শূন্যকে চিহ্নিত করবে। SonarQube-এর জন্য, অনুমোদিত সংখ্যা তালিকা: 0, 1, -1, 2 (দ্বিগুনের জন্য), 100 (শতাংশ), 60 এবং 24 (সময়)। অন্যান্য সব মানের জন্য — public static final (Java) বা const val (Kotlin) মডিফায়ার সহ নামযুক্ত কনস্ট্যান্ট প্রয়োজন।
CI-স্তরের বিশ্লেষণের জন্য, একটি সতর্কতা হিসেবে জাদু পরীক্ষা করে কিন্তু বিল্ড ব্লক না করে এমন একটি ধাপ যোগ করুন। প্রথম রান লিগ্যাসি কোডে শত শত সতর্কতা দেখাবে। ধীরে ধীরে, টিকিটের পর টিকিট, কোড কনস্ট্যান্টে স্থানান্তর করুন এবং গুণমানের সীমা বাড়ান। যখন ম্যাজিক নাম্বারের সংখ্যা ১০-এর নিচে নেমে আসে — নিয়মটি বিল্ড ত্রুটি হিসেবে সক্রিয় করুন।
জাদুর রিফ্যাক্টরিং সবচেয়ে নিরাপদ অপারেশনগুলির মধ্যে একটি: লিটারেলকে কনস্ট্যান্ট দিয়ে প্রতিস্থাপন কোড আচরণ পরিবর্তন করে না। তবুও, পদ্ধতিটি সুশৃঙ্খল হতে হবে যাতে লুকানো নির্ভরতা মিস না হয় (উদাহরণস্বরূপ, যদি একই ম্যাজিক নাম্বার সম্পর্কহীন প্রসঙ্গে ব্যবহৃত হয় কিন্তু ঘটনাক্রমে মান সমান হয়)।
ধাপে ধাপে প্রক্রিয়া: জাদুকরী মানের সমস্ত অবস্থান খুঁজুন, প্রতিটির প্রসঙ্গ বুঝুন, বিভিন্ন কনস্ট্যান্টে বিভক্ত করুন (এমনকি মান সমান হলেও — প্রসঙ্গ ভিন্ন, এবং কনস্ট্যান্টের নাম ভিন্ন হওয়া উচিত), লিটারেলকে কনস্ট্যান্ট দিয়ে প্রতিস্থাপন করুন, পরীক্ষার মাধ্যমে যাচাই করুন। ধাপ ২-এ ভুল সবচেয়ে সাধারণ: দুটি ভিন্ন ধারণা (মিলিসেকেন্ডে টাইমআউট এবং বাইটে সীমা) সংখ্যাগতভাবে সমান হতে পারে (উদাহরণস্বরূপ, 5000), কিন্তু শব্দার্থগতভাবে এরা ভিন্ন রাশি এবং এদের এক কনস্ট্যান্টে একত্রিত করা যায় না।
// before - same number in different contexts
public class Config {
public void setupCache() {
cache.setMaxSize(5000); // 5 MB
}
public void setupTimeout() {
client.setReadTimeout(5000); // 5 seconds
}
}
// after - different constants for different contexts
public class Config {
private static final int CACHE_MAX_SIZE_MB = 5;
private static final int READ_TIMEOUT_SECONDS = 5;
public void setupCache() {
cache.setMaxSize(CACHE_MAX_SIZE_MB * 1024 * 1024);
}
public void setupTimeout() {
client.setReadTimeout(
READ_TIMEOUT_SECONDS * 1000
);
}
}
নতুন কোডের জন্য, নিয়ম সহজ: 0, 1, -1, true, false, null এবং খালি স্ট্রিং ছাড়া যেকোনো লিটারেল কনস্ট্যান্টে নেওয়া হয়। ব্যতিক্রম: গাণিতিক ধ্রুবক (সর্বদা স্ট্যান্ডার্ড লাইব্রেরি ব্যবহার করুন), পরীক্ষার ডেটা (লিটারেল পরীক্ষায় থাকতে পারে কিন্তু ব্যাখ্যামূলক ভেরিয়েবল নাম সহ), এবং বৃদ্ধির জন্য সীমানা মান (লুপে i + 1 ঠিক আছে)।
সচরাচর জিজ্ঞাসিত প্রশ্ন
হ্যাঁ, 100 ও ম্যাজিক নাম্বার যদি প্রসঙ্গ ছাড়া ব্যবহৃত হয়। 100-এর পরিবর্তে, MAX_PERCENT বা PROBABILITY_SCALE লিখুন। ব্যতিক্রম: যখন 100 প্রসঙ্গে স্পষ্টভাবে শতাংশ (উদাহরণস্বরূপ, শতাংশ গণনা সূত্রে), কিন্তু এই ক্ষেত্রেও কনস্ট্যান্ট পাঠযোগ্যতা উন্নত করে।
পরীক্ষায়ও নামযুক্ত ভেরিয়েবল ব্যবহার করা ভাল। assertEquals(42, result)-এর পরিবর্তে, val expected = 42; assertEquals(expected, result) লিখুন। ব্যতিক্রম: সীমানা মানের পরীক্ষা (0, null, খালি স্ট্রিং) — এগুলো লিটারেল হিসেবে থাকতে পারে কারণ এরা পরীক্ষার প্রসঙ্গে পাঠযোগ্য।
হ্যাঁ, UI-সম্পর্কিত সংখ্যা (আকার, মার্জিন, অ্যানিমেশন সময়কাল) রিসোর্সে (dimens.xml, integers.xml) থাকা উচিত। ব্যবসায়িক কনস্ট্যান্ট (টাইমআউট, সীমা) — companion object বা কনফিগারেশন ফাইলে। মূল মানদণ্ড: যদি সংখ্যাটি যুক্তি পরিবর্তন না করে বদলাতে পারে — তাহলে এটি একটি রিসোর্স।
MagicNumber নিয়ম সহ SonarQube বা no-magic-numbers সহ ESLint চালান। রিপোর্ট নিন, ব্যবহারের ফ্রিকোয়েন্সি অনুসারে সাজান, এবং তিন বা তার বেশি জায়গায় দেখা সংখ্যা দিয়ে শুরু করুন। এরা কনস্ট্যান্টে নেওয়ার জন্য সবচেয়ে সম্ভাব্য প্রার্থী।
না। গ্রহণযোগ্য লিটারেল: 0, 1, -1 (বৃদ্ধি/হ্রাস, খালি চেক), true, false, null, খালি স্ট্রিং। বাকি সবগুলির নামকরণ প্রয়োজন। যদি সংখ্যা 0 খালি চেক হিসেবে ব্যবহৃত না হয় (উদাহরণস্বরূপ, 0 মূল ক্যাটাগরি ID), তাহলে 0-ও কনস্ট্যান্ট হওয়া উচিত: ROOT_CATEGORY_ID = 0।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন