Proqramlaşdırmada sehr — bu metafora deyil, kontekstdən mənası aydın olmayan və anlaşılması üçün xarici bilik tələb edən dəyərləri (rəqəmlər, sətirlər, bayraqlar) ifadə edən dəqiq termindir. Sehrin ən geniş yayılmış növü magic numbers-dır: birbaşa koda izahsız şəkildə yazılmış rəqəmsal sabitlər. SonarSource Code Quality Report (2025) araşdırmasına görə, statik analizatorların bütün xəbərdarlıqlarının təxminən 8 faizi izah olunmamış literallarla bağlıdır. Sehrli dəyərlər kodu kövrək edir: dəyişiklik bütün halların axtarılmasını tələb edir və yeni tərtibatçı rəqəmə toxunmağın mümkün olub-olmadığını və ya sistemin işləməsi üçün kritik olduğunu başa düşmür.
Əsas məqamlar
Sehr (magic) — mənası mövzu sahəsi haqqında əlavə bilik olmadan aydın olmayan mənbə kodundakı istənilən dəyərdir. Termin icmada kök salıb: əgər tərtibatçı rəqəmə baxıb onun haradan gəldiyini başa düşmürsə — bu sehirdir.
Sehr bir neçə növdə olur: rəqəmsal (magic numbers), sətir (magic strings), məntiqi (magic flags) və konfiqurasiya (parametrlərdə olmalı olan sərt kodlaşdırılmış parametrlər). Bütün dörd növü bir problem birləşdirir: tələb dəyişdikdə tərtibatçı dəyərin istifadə olunduğu bütün yerləri tapmalı və onları əl ilə əvəz etməlidir. Ən azı bir halın buraxılması xətaya səbəb olur.
JetBrains Code Quality Survey (2025) hesabatına görə, tərtibatçıların 73 faizi magic numbers-ı aşağı keyfiyyətli kodun göstəricisi hesab edir, 41 faizi isə etiraf edir ki, özləri də vaxtaşırı onları buraxırlar. Əsas səbəb tələsiklikdir: “Sonra sabit qoyaram” — amma sonra gəlmir və bir aydan sonra 0.85 rəqəmi metodun gövdəsində izahsız qalır.
Əsas qayda: 0, 1, true, false və boş sətirdən başqa hər bir literral dəyər adlandırılmış sabitə çıxarılmalıdır. İstisnalar: sayğacın artırılması (i + 1), riyazi sıfırlar (0-a yoxlama) və akkumulyatorların başlanğıc dəyərləri. Qalan hər şey adlandırma üçün namizəddir.
Magic number — kontekstdən dəyəri aydın olmayan rəqəmsal literraldır. Klassik nümunə: timeout-a cavabdeh olan kodda 86400. Tərtibatçı rəqəmi görür və bunun sutkadakı saniyələrin sayı olduğunu təxmin etməlidir. Səhv edib 84600 qoyarsa — xətanı tutmaq çətin olacaq, çünki timeout 18 dəqiqə tez işə düşəcək.
Magic numbers nə üçün təhlükəlidir: birincisi, onlar oxunaqlılığı pozur. 1024 rəqəmi kilobayt ölçüsü, paginasiya həddi və ya elementlərin maksimum sayı ola bilər. Kontekstsiz — bu sadəcə rəqəmdir. İkincisi, onlar təkrarlanma yaradır: əgər 1024 beş yerdə istifadə olunursa, həddi 2048-ə dəyişdikdə tərtibatçı beşini də tapıb əvəz etməlidir. Bir yer buraxılarsa — sistem səhv işləyir, ancaq aşkar xəta olmaz.
// əvvəl — təmiz formada sehr
fun calculateTimeout(base: Int): Int {
return base * 3 + 5000
}
// sonra — dəyərlər sabitlərlə əvəz edilib
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
}
Üçüncü təhlükə — test edilə bilməmək. Limit dəyəri kodda literral olaraq sərt kodlaşdırılıbsa, test sərhəd şərtlərini yoxlamaq üçün onu ləğv edə bilməz. Companion object və ya konfiqurasiya faylına çıxarılmış sabit kodu test edilə bilən edir: test başqa bir dəyər qoyur və sistemin davranışını sərhəddə yoxlayır.
Vərdiş yaradın: 0, 1, 100 və ya 2-dən başqa bir rəqəm yazdığınız hər dəfə dayanın və onu sabitə çıxarmağın dəyər olub-olmadığını düşünün. Əgər rəqəm biznes məntiqi ilə bağlıdırsa (limit, hədd, timeout, ölçü) — mütləq çıxarın. Əgər rəqəm riyazi sabitdirsə (pi, e) — standart kitabxanadan istifadə edin (Math.PI, Math.E).
Magic strings — sabitlərə və ya resurslara çıxarılmadan koda daxil edilmiş sətir literalları. Tipik nümunələr: endpoint URL-ləri, SharedPreferences açarlarının adları, Intent Actions, bundle keys, fayl adları və SQL sorğuları.
Sehrli sətirlərin təhlükəsi kompilyasiya mərhələsində yoxlamanın olmamasıdır. “user_prefs” sətirindəki səhv runtime-a qədər aşkar edilməyəcək. Əgər sətir on yerdə istifadə olunursa və tərtibatçı birində “user_pref” (s hərfi olmadan) yazıbsa — proqram çökmür, amma məlumatlar saxlanılmır. Belə bir xəta aylarla produksiyada yaşaya bilər, çünki crash-a səbəb olmur.
Android layihələri üçün sehrli sətirlər resurslara (strings.xml, arrays.xml) və ya companion object-dəki sabitlərə çıxarılmalıdır. iOS üçün — mətn resurslarına (Localizable.strings) və ya enum sabitlərinə. Backend üçün — konfiqurasiya fayllarına (.env, application.properties). Heç bir açar, URL və ya yol kodda sətir literralı olaraq olmamalıdır.
// əvvəl — sinif boyu sehrli sətirlər
let prefs = UserDefaults.standard
prefs.set(token, forKey: "auth_token")
prefs.set(userId, forKey: "current_user_id")
// sonra — sətirlər enum-a çıxarılıb
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)
Təkrarlanan sətirlərə xüsusi diqqət yetirin. Əgər eyni “user_settings” açarı üç faylda rast gəlinirsə — 99 faiz ehtimalla gec-tez onlardan birində səhv yaranacaq. Enum və ya sabitə çıxarma bütün istinadların eyni dəyərdən istifadə etməsini təmin edir.
Magic flags — çağırış kontekstindən dəyəri aydın olmayan məntiqi parametrlərdir. Klassik anti-nümunə: metodda bu bayrağın nəyi aktivləşdirdiyini və ya söndürdüyünü izah etmədən true və ya false ötürmək.
Nümunə: userDao.fetch(includeDeleted = false). Tərtibatçı false görür və bunun “silINmişləri daxil etmə” və ya “aktiv olanları daxil etmə” olduğunu başa düşmür. Bir aydan sonra false true-ya çevrilir və nəticələrdə silinmiş qeydlər görünməyə başlayır. Xəta yalnız produksiyada aşkar edilir.
Həll yolu — məntiqi bayraqları enum və ya sealed class ilə əvəz etmək. Boolean parametri əvəzinə UserFilter.includeDeleted və ya UserFilter.activeOnly istifadə edin. Beləliklə, kod özü məqsədi sənədləşdirir və IDE avtomatik tamamlamada mövcud variantları təklif edir.
Əgər məntiqi bayraq bir neçə təbəqədən ötürülürsə — bu, abstraksiyanın səhv olduğuna dair başqa bir siqnaldır. Bayrağı üç səviyyəli çağırışdan keçirmək əvəzinə, filtrasiya seçiminin yuxarı səviyyədə qəbul edilib hazır konfiqurasiya kimi ötürülməli olub-olmadığını düşünün. Kodda nə qədər az məntiqi bayraq varsa — bir o qədər az sehr var.
Bir qayda tətbiq edin: heç bir məntiqi parametr adlandırılmış arqument olmadan metoda ötürülmür (əgər dil named arguments dəstəkləyirsə). Kotlin və Swift-də bu tələb avtomatik yerinə yetirilir. Java-da true/false əvəzinə Builder və ya enum sabitlərindən istifadə edin.
Sehrli dəyərlərin axtarışı gözlənilməz yerlərdə literalları aşkar etmək üçün konfiqurasiya edilmiş statik analizatorlar tərəfindən avtomatlaşdırılır. Hər bir dil öz konfiqurasiya edilə bilən istisnaları olan alətlər təklif edir.
| Alət | Dillər | Qayda |
|---|---|---|
| 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 qaydası) |
| PMD | Java, Apex, PLSQL | MagicNumber (icazə verilənlər siyahısı konfiqurasiya edilə bilər) |
| PhpStorm Inspections | PHP | NumericLiteralWithContext (daxili yoxlama) |
İstisnaların konfiqurasiyası kritik əhəmiyyət daşıyır — onsuz analizator hər bir artırmada (-1, +1) və riyazi sıfırda xəbərdarlıq verəcək. SonarQube üçün icazə verilən rəqəmlərin siyahısı: 0, 1, -1, 2 (ikiqat artırma üçün), 100 (faizlər), 60 və 24 (vaxt). Qalan bütün dəyərlər üçün — public static final (Java) və ya const val (Kotlin) ilə adlandırılmış sabit tələb edin.
CI səviyyəsində analiz üçün sehrin yoxlanılmasını xəbərdarlıq (warning) kimi əlavə edin, ancaq qurğunu bloklamasın. İlk işə salma legacy-kodda yüzlərlə xəbərdarlıq göstərəcək. Tədricən, ticket-ticket, kodu sabitlərə köçürün və keyfiyyət həddini yüksəldin. Magic numbers sayı 10-dan az olduqda — qaydanı qurğu xətası kimi aktivləşdirin.
Sehrin refaktorinqi — ən təhlükəsiz əməliyyatlardan biridir: literralın sabitlə əvəz edilməsi kodun davranışını dəyişmir. Buna baxmayaraq, yanaşma gizli asılılıqları qaçırmamaq üçün sistematik olmalıdır (məsələn, eyni magic number əlaqəsiz kontekstlərdə istifadə olunursa, lakin təsadüfən dəyər üst-üstə düşür).
Addım-addım proses: sehrli dəyərin bütün hallarını tapın, hər birinin kontekstini anlayın, müxtəlif sabitlərə ayırın (dəyərlər eyni olsa belə — kontekstlər fərqlidir və sabitlər fərqli adlandırılmalıdır), literalları sabitlərlə əvəz edin, testlərlə yoxlayın. 2-ci addımdakı səhv ən çox yayılmışdır: iki fərqli anlayış (millisaniyələrdə timeout və baytlarda hədd) rəqəmcə eyni ola bilər (məsələn, 5000), lakin semantik olaraq bunlar fərqli kəmiyyətlərdir və onları bir sabitdə birləşdirmək olmaz.
// əvvəl — müxtəlif kontekstlərdə eyni rəqəm
public class Config {
public void setupCache() {
cache.setMaxSize(5000); // 5 MB
}
public void setupTimeout() {
client.setReadTimeout(5000); // 5 saniyə
}
}
// sonra — müxtəlif kontekstlər üçün fərqli sabitlər
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
);
}
}
Yeni kod üçün qayda sadədir: 0, 1, -1, true, false, null və boş sətirdən başqa hər bir literral sabitə çıxarılır. İstisnalar: riyazi sabitlər (həmişə standart kitabxana vasitəsilə), test məlumatları (testdə literral qala bilər, ancaq izahlı dəyişən adı ilə) və artırma üçün sərhəd dəyərləri (dövrədə i + 1 — normal).
Tez-tez verilən suallar
Bəli, 100 də magic number-dır, əgər kontekstsiz istifadə olunursa. 100 əvəzinə MAX_PERCENT və ya PROBABILITY_SCALE yazın. İstisna: 100 kontekstdə aşkar faiz olduqda (məsələn, faiz hesablama düsturunda), lakin bu halda belə sabit oxunaqlılığı yaxşılaşdırır.
Testlərdə də adlandırılmış dəyişənlərdən istifadə etmək daha yaxşıdır. assertEquals(42, result) əvəzinə val expected = 42; assertEquals(expected, result) yazın. İstisna: sərhəd dəyərləri üçün testlər (0, null, boş sətir) — onları literral olaraq buraxmaq olar, çünki test kontekstində oxunaqlıdırlar.
Bəli, UI ilə bağlı rəqəmlər (ölçülər, boşluqlar, animasiya müddəti) resurslarda olmalıdır (dimens.xml, integers.xml). Biznes sabitləri (timeoutlar, limitlər) — companion object və ya konfiqurasiya faylında. Əsas meyar: əgər rəqəm məntiqi dəyişmədən dəyişə bilərsə — bu resursdur.
SonarQube-u MagicNumber qaydası və ya ESLint-i no-magic-numbers ilə işə salın. Hesabat alın, istifadə tezliyinə görə sıralayın və üç və daha çox yerdə rast gəlinən rəqəmlərdən başlayın. Onlar ən çox ehtimalla sabitə çıxarılmaq üçün namizəddirlər.
Xeyr. İcazə verilən literallar: 0, 1, -1 (artırma/azaltma, boşluq yoxlaması), true, false, null, boş sətir. Qalanların hamısı adlandırılma tələb edir. Əgər 0 rəqəmi boşluq yoxlaması kimi deyil (məsələn, 0 — kök kateqoriyanın ID-sidirsə), onda 0 da sabit olmalıdır: ROOT_CATEGORY_ID = 0.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun