Programlamada sihir bir metafor değil, anlamı bağlamdan belli olmayan ve anlaşılması için dış bilgi gerektiren değerleri (sayılar, dizeler, bayraklar) belirten kesin bir terimdir. Sihrin en yaygın türü sihirli sayılardır: neden bu belirli değerin seçildiğine dair bir açıklama olmadan doğrudan koda yazılan sayısal sabitler. SonarSource Kod Kalitesi Raporu'na (2025) göre, tüm statik analizör uyarılarının yaklaşık yüzde 8'i açıklanmayan değişmez değerlerle ilgilidir. Sihirli değerler kodu kırılgan yapar: değişiklik tüm oluşumların bulunmasını gerektirir ve yeni bir geliştirici sayıya dokunulup dokunulamayacağını veya sistemin çalışması için kritik olup olmadığını anlayamaz.
Önemli Noktalar
Sihir, ek alan bilgisi olmadan anlamı belli olmayan kaynak koddaki herhangi bir değerdir. Terim toplulukta yerleşmiştir: bir geliştirici bir sayıya bakar ve nereden geldiğini söyleyemezse — işte bu sihirdir.
Sihir birkaç türde gelir: sayısal (sihirli sayılar), dize (sihirli dizeler), boolean (sihirli bayraklar) ve yapılandırma (ayarlarda olması gereken sabit kodlanmış parametreler). Dört türün ortak bir sorunu vardır: bir gereksinim değiştiğinde, geliştirici değerin kullanıldığı her yeri bulmalı ve bunları manuel olarak değiştirmelidir. Tek bir oluşumun atlanması hataya yol açar.
JetBrains Kod Kalitesi Anketi'ne (2025) göre, geliştiricilerin yüzde 73'ü sihirli sayıları düşük kod kalitesinin bir göstergesi olarak kabul ederken, yüzde 41'i ara sıra onları bıraktıklarını itiraf ediyor. Ana neden acele etmektir: “Sabiti sonra eklerim” — ama o sonra asla gelmez ve bir ay sonra 0.85 sayısı açıklama olmadan bir metodun ortasında kalır.
Temel kural: 0, 1, true, false ve boş dize dışındaki her değişmez değer adlandırılmış bir sabite çıkarılmalıdır. İstisnalar: sayaç artırımı (i + 1), matematiksel sıfırlar (0 kontrolü) ve toplayıcıların başlangıç değerleri. Geri kalan her şey adlandırma için adaydır.
Sihirli sayı, değeri bağlamdan belli olmayan sayısal bir değişmez değerdir. Klasik bir örnek: zaman aşımıyla ilgili kodda 86400. Geliştirici sayıyı görür ve bunun bir gündeki saniye sayısı olduğunu tahmin etmelidir. Hata yapıp 84600 yazarsa, hata yakalanması zor olacaktır çünkü zaman aşımı 18 dakika erken tetiklenecektir.
Sihirli sayılar neden tehlikelidir: birincisi, okunabilirliği bozarlar. 1024 sayısı bir kilobayt boyutu, sayfalama eşiği veya maksimum öğe sayısı anlamına gelebilir. Bağlam olmadan — sadece bir sayıdır. İkincisi, tekrar oluştururlar: 1024 beş yerde kullanılıyorsa, eşik 2048'e değiştiğinde geliştirici beşini de bulup değiştirmelidir. Bir yer atlanırsa, sistem açık bir hata olmadan yanlış çalışır.
// 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
}
Üçüncü tehlike test edilemezliktir. Bir eşik değeri değişmez değer olarak sabit kodlanmışsa, test sınır koşullarını doğrulamak için bunu geçersiz kılamaz. Bir companion object veya yapılandırma dosyasına çıkarılan bir sabit, kodu test edilebilir hale getirir: test farklı bir değer koyar ve sınırdaki sistem davranışını doğrular.
Bir alışkanlık geliştirin: 0, 1, 100 veya 2 dışında bir sayı yazdığınızda — durun ve bir sabite çıkarılması gerekip gerekmediğini düşünün. Sayı iş mantığıyla (limit, eşik, zaman aşımı, boyut) ilgiliyse — tereddüt etmeden çıkarın. Sayı matematiksel bir sabitse (pi, e) — standart kütüphaneyi (Math.PI, Math.E) kullanın.
Sihirli dizeler, sabitlere veya kaynaklara çıkarılmadan koda gömülü dize değişmez değerleridir. Tipik örnekler: uç nokta URL'leri, SharedPreferences anahtar adları, Intent Action'lar, bundle anahtarları, dosya adları ve SQL sorguları.
Sihirli dizelerin tehlikesi derleme zamanı denetimi eksikliğidir. “user_prefs” dizesindeki bir yazım hatası çalışma zamanına kadar yakalanmaz. Dize on yerde kullanılıyorsa ve geliştirici bunlardan birinde “user_pref” (s'siz) yazarsa — uygulama çökmez, ancak veriler kaydedilmez. Böyle bir hata, çökmeye neden olmadığı için aylarca üretimde kalabilir.
Android projeleri için sihirli dizeler kaynaklara (strings.xml, arrays.xml) veya bir companion object'teki sabitlere çıkarılmalıdır. iOS için — dize kaynaklarına (Localizable.strings) veya enum sabitlerine. Backend için — yapılandırma dosyalarına (.env, application.properties). Hiçbir anahtar, URL veya yol, kodda dize değişmez değeri olarak görünmemelidir.
// 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)
Tekrarlanan dizelere özellikle dikkat edin. Aynı “user_settings” anahtarı üç dosyada görünüyorsa — yüzde 99 olasılıkla sonunda bunlardan birinde yazım hatası oluşacaktır. Bir enum veya sabite çıkarmak, tüm başvuruların aynı değeri kullanmasını garanti eder.
Sihirli bayraklar, çağrı bağlamından anlamı belli olmayan boolean parametrelerdir. Klasik bir anti-desen: bir bayrağın tam olarak neyi etkinleştirdiğini veya devre dışı bıraktığını açıklamadan bir metoda true veya false geçirmek.
Örnek: userDao.fetch(includeDeleted = false). Bir geliştirici false'u görür ve “silinenleri dahil etme” mi yoksa “aktifleri dahil etme” mi anlamına geldiğini söyleyemez. Bir ay sonra, false true'ya dönüşür ve silinen kayıtlar çıktıda görünmeye başlar. Hata yalnızca üretimde keşfedilir.
Çözüm, boolean bayrakları bir enum veya sealed sınıfla değiştirmektir. Boolean parametresi yerine UserFilter.includeDeleted veya UserFilter.activeOnly kullanın. Böylece kod niyetini belgeler ve IDE otomatik tamamlama sırasında mevcut seçenekleri önerir.
Bir boolean bayrağı birden çok katmandan geçiriliyorsa — bu, soyutlamanın yanlış olduğunun bir başka işaretidir. Bir bayrağı üç seviye çağrı boyunca sürüklemek yerine, filtre seçiminin üst seviyede yapılıp hazır bir yapılandırma olarak iletilip iletilmeyeceğini düşünün. Kodda ne kadar az boolean bayrak varsa — o kadar az sihir vardır.
Bir kural benimseyin: hiçbir boolean parametre, adlandırılmış argüman olmadan bir metoda geçirilmez (dil adlandırılmış argümanları destekliyorsa). Kotlin ve Swift'te bu gereksinim otomatiktir. Java'da true/false yerine Builder veya enum sabitleri kullanın.
Sihirli değerlerin aranması, beklenmeyen yerlerde değişmez değerleri algılayacak şekilde yapılandırılmış statik analizörler tarafından otomatikleştirilir. Her dil, özelleştirilebilir istisnalarla kendi araçlarını sunar.
| Araç | Diller | Kural |
|---|---|---|
| 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 (yapılandırılabilir izin verilen liste) |
| PhpStorm Denetimleri | PHP | NumericLiteralWithContext (yerleşik denetim) |
İstisnaları yapılandırmak çok önemlidir — aksi takdirde analizör her artırımı (-1, +1) ve matematiksel sıfırı işaretler. SonarQube için izin verilen sayı listesi: 0, 1, -1, 2 (ikiye katlama için), 100 (yüzdeler), 60 ve 24 (zaman). Diğer tüm değerler için — public static final (Java) veya const val (Kotlin) değiştiricisiyle adlandırılmış bir sabit gerektirin.
CI düzeyinde analiz için, sihri uyarı olarak denetleyen ancak derlemeyi engellemeyen bir adım ekleyin. İlk çalıştırma, eski kodda yüzlerce uyarı gösterecektir. Kademeli olarak, ticket ticket, kodu sabitlere taşıyın ve kalite eşiğini yükseltin. Sihirli sayıların sayısı 10'un altına düştüğünde — kuralı derleme hatası olarak etkinleştirin.
Sihrin yeniden düzenlenmesi en güvenli işlemlerden biridir: bir değişmez değeri sabitle değiştirmek kod davranışını değiştirmez. Yine de, gizli bağımlılıkları gözden kaçırmamak için yaklaşım sistematik olmalıdır (örneğin, aynı sihirli sayı ilgisiz bağlamlarda kullanılıyorsa ancak tesadüfen aynı değere sahipse).
Adım adım süreç: sihirli değerin tüm oluşumlarını bulun, her birinin bağlamını anlayın, farklı sabitlere ayırın (değerler eşleşse bile — bağlamlar farklıdır ve sabitler farklı adlandırılmalıdır), değişmez değerleri sabitlerle değiştirin, testlerle doğrulayın. 2. adımdaki hata en yaygın olanıdır: iki farklı kavram (milisaniye cinsinden zaman aşımı ve bayt cinsinden eşik) sayısal olarak eşleşebilir (örneğin, 5000), ancak anlamsal olarak farklı miktarlardır ve tek bir sabitte birleştirilemezler.
// 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
);
}
}
Yeni kod için kural basittir: 0, 1, -1, true, false, null ve boş dize dışındaki herhangi bir değişmez değer bir sabite çıkarılır. İstisnalar: matematiksel sabitler (her zaman standart kütüphaneyi kullanın), test verileri (değişmez değerler testlerde kalabilir ancak açıklayıcı bir değişken adıyla) ve artırım için sınır değerleri (bir döngüde i + 1 sorun değildir).
Sıkça Sorulan Sorular
Evet, 100 de bağlam olmadan kullanılırsa sihirli sayıdır. 100 yerine MAX_PERCENT veya PROBABILITY_SCALE yazın. İstisna: 100'ün bağlamda açıkça bir yüzde olduğu durumlar (örneğin, yüzde hesaplama formülünde), ancak bu durumda bile sabit okunabilirliği artırır.
Testlerde de adlandırılmış değişkenler kullanmak daha iyidir. assertEquals(42, result) yerine val expected = 42; assertEquals(expected, result) yazın. İstisna: sınır değer testleri (0, null, boş dize) — test bağlamında okunabilir oldukları için değişmez değer olarak kalabilirler.
Evet, kullanıcı arayüzüyle ilgili sayılar (boyutlar, kenar boşlukları, animasyon süresi) kaynaklarda (dimens.xml, integers.xml) olmalıdır. İş sabitleri (zaman aşımları, limitler) — bir companion object veya yapılandırma dosyasında. Ana kriter: bir sayı mantığı değiştirmeden değişebiliyorsa — bu bir kaynaktır.
MagicNumber kuralıyla SonarQube'ü veya no-magic-numbers ile ESLint'i çalıştırın. Raporu alın, kullanım sıklığına göre sıralayın ve üç veya daha fazla yerde görünen sayılarla başlayın. Bunlar sabitlere çıkarmak için en olası adaylardır.
Hayır. Kabul edilebilir değişmez değerler: 0, 1, -1 (artırma/azaltma, boş kontrol), true, false, null, boş dize. Diğerlerinin tümü adlandırma gerektirir. 0 sayısı boş kontrol olarak kullanılmıyorsa (örneğin, 0 kök kategori kimliğiyse), o zaman 0 da bir sabit olmalıdır: ROOT_CATEGORY_ID = 0.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun