Yazılımda Chesterton Çiti: Önce Neden Diye Sor, Sonra Sil
Chesterton Çiti eski kodu yerinde bırakma gerekçesi sayılır. Hata ayıklama sorusu olarak okunursa ne zaman silineceğini ve cevabın nasıl kaydedileceğini söyler.
Chesterton Çiti, açıklanamayan kodu yerinde bırakmanın gerekçesi olarak anılır; oysa Chesterton’ın kendi metni, amaç anlaşıldığında çitin kaldırılmasına izin verir. Bu yüzden her çiti iki yarısı olan, zaman sınırlı bir hata ayıklama sorusu olarak ele alın: neden kuruldu ve bugün kim ona bağımlı. Bütçe bitince, değişiklik geri alınabilir ve bağımlılar gözlemlenebilir olduğu sürece, varsayılan olarak kademeli kaldırmaya geçin. Çit kalsın ya da gitsin, cevabı yorum, commit gövdesi, ADR ya da flag metadata’sı olarak geride bırakın.
Çit, sizin kurmadığınız bir sistemdeki bir koşul, bir retry, bir sleep(250), bir istek başlığı ya da bir security group kuralı olabilir. Ekipte kimse orada neden bulunduğunu söyleyemez. Silerseniz görünmeyen bir bağımlıyı kırma riski var. Sonsuza kadar tutarsanız kod tabanı kimsenin dokunmaya cesaret edemediği ağırlıkla dolar ve uyuyan bir kod yolu kazayla yeniden canlanabilir.
Özgün Metin Neye İzin Veriyor#
Çit pasajı G. K. Chesterton’ın The Thing (1929) kitabındaki “The Drift from Domesticity” bölümünden geliyor. Dolaşımdaki yarısı, çiti kaldırmadan önce gidip düşünmesi söylenen reformcu. Nadiren alıntılanan yarısı ise birkaç cümle sonra geliyor; Chesterton orada düşünen reformcunun varabileceği sonucu tarif ediyor: “If he knows how it arose, and what purposes it was supposed to serve, he may really be able to say that they were bad purposes, or that they have since become bad purposes, or that they are purposes which are no longer served.” Yani çitin nasıl ortaya çıktığını ve hangi amaca hizmet etmesi gerektiğini bilen kişi, o amaçların kötü olduğunu, sonradan kötüleştiğini ya da artık hizmet edilmeyen amaçlar olduğunu söyleyebilir.
Bütün argüman o cümlede saklı. İlke bir araştırma talep ediyor ve o araştırmanın olası sonuçlarından biri olarak kaldırmaya açıkça izin veriyor. Bir silme PR’ının altına “Chesterton Çiti” yazıp orada duran reviewer, ilk yarıyı çağırıp ikinci yarıyı atlamış olur. Kod sahipliği yazısı aynı pasaja sahiplik tarafından yaklaşıyor.
Farklı Kanıt İsteyen İki Soru#
Chesterton’ın metni amacı soruyor. Yazılım buna metnin öngöremeyeceği ikinci bir soru ekliyor, çünkü onun çitinin bir API’si yoktu. Hyrum Yasası bunu şöyle ifade ediyor: “With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody.” Yeterli sayıda kullanıcısı olan bir API’de sözleşmede ne vaat ettiğiniz fark etmez; sistemin gözlemlenebilir her davranışına birileri bağımlı hale gelir. Özgün yazarın niyeti yalnızca ilk soruyu cevaplar. Bugün birinin o davranışa bağımlı olup olmadığı ayrı bir konudur ve niyet çoktan geçersiz olsa bile cevap evet çıkabilir.
| Soru | Kanıt nerede | Nasıl elde edilir |
|---|---|---|
| Neden kuruldu? | Commit’ler, PR açıklamaları, bağlı ticket’lar, ADR’ler, yorumlar | git log -S, git log -G, git log -L, git blame -w -C -C -C, .git-blame-ignore-revs |
| Bugün kim bağımlı? | Trafik, log’lar, metrikler, çağrı noktaları, repo dışındaki tüketiciler | Kod araması, erişim log’ları, kod yolu üzerinde bir metrik, kademeli bir scream test |
Uygulamada iki sorunun cevabı nadiren örtüşür. Kararsız bir upstream için eklenen retry, upstream artık kararlı olduğu için anlamsız kalmış olabilir; ama aşağı akıştaki bir batch işi, o retry’ın getirdiği ekstra gecikmeye sessizce bağımlı hale gelmiş olabilir. İlk gerçeği arkeoloji bulur; ikincisini yalnızca gözlem bulur.
Gerekçeyi Git Geçmişinden Çıkarmak#
Repo biraz yaşlandıysa düz git blame ilk soru için yanlış araçtır. Bir formatlama taraması, bir dosya taşıması ya da bir squash merge, blame satırını satıra en son dokunan kişiye işaret ettirir ve gerekçe kurtarılabilir olduğu halde kaybolmuş görünür. Git’in tam bu sorun için özel seçenekleri var.
# Bu string'i hangi commit'ler ekledi ya da sildi? (pickaxe)
git log -S 'await sleep(250)' --oneline -- src/
# Aynı fikir, ama eklenen/silinen satırlara regex uygula
git log -G 'retry(Count|Limit)' --oneline -- src/payments/
# Satır numaraları kaysa bile tek bir fonksiyonun tüm geçmişi
git log -L :retryWithBackoff:src/payments/client.ts
# Boşlukları yok sayan, dosyalar arası taşınan/kopyalanan kodu izleyen blame
git blame -w -C -C -C src/payments/client.ts
# Toplu formatlama commit'lerini yerel blame'den çıkar (her klon bunu bir kez çalıştırır)
git config blame.ignoreRevsFile .git-blame-ignore-revs
-S, verilen string’in geçiş sayısını değiştiren her commit’i bulur; böylece satırı ekleyen commit’i de en son silen commit’i de ortaya çıkarır. -G, eklenen ve silinen satırlara bir regex uygular; şekil değiştirmiş tanımlayıcılar için uygundur. -L :<funcname>:<file> tek bir fonksiyonu satır kaymaları boyunca izler. Git’in varsayılan fonksiyon adı desenine dayanır ve üst düzey fonksiyonları bulur; girintili class metotları için genellikle .gitattributes içinde bir diff= sürücüsü ya da -L <start>,<end> satır aralığı gerekir. blame -C üç kez verildiğinde başka dosyalardan kopyalanan kodu da arar. .git-blame-ignore-revs dosyasını repo köküne commit’leyin. GitHub’ın blame görünümü onu otomatik olarak okur; her geliştirici de yukarıdaki config satırıyla kendi klonunda devreye alır.
Ekleyen commit bulunduktan sonra en değerli okuma commit gövdesi ve bağlı PR ya da ticket’tır. “Ledger 409 için retry ekle” diyen bir gövde ile replica gecikmesi olayını anlatan bir ticket, ilk soruyu dakikalar içinde cevaplar. “fix” diyen bir gövde hiçbir şey cevaplamaz.
Bütçeyi Geri Alınabilirliğe Göre Belirlemek#
Araştırmanın bir maliyeti var, dolayısıyla bir sınırı olmalı. Sınır yoksa temizlik tıkanır; her çite aynı küçük sınır uygulanırsa bir veri migrasyonu scream test’e girer. Bütçe değişikliğin iki özelliğine göre ölçeklenmeli; kodun ne kadar tuhaf göründüğü bu özelliklerden biri değil.
İlk özellik geri alınabilirlik: kaldırma işlemi tek bir deploy ya da tek bir flag değişimiyle, veri kaybı olmadan geri alınabiliyor mu? İkincisi bağımlıların gözlemlenebilirliği: çite bağımlı bir şey varsa, kırılma bekleyebileceğiniz bir süre içinde metriklerde, log’larda ya da destek ticket’larında görünür mü? İkisi de sağlanıyorsa geçmişe kısa bir bakış ve ardından kademeli kaldırma orantılı bir adımdır. Makul bir ölçü, git geçmişi ve bağlı ticket’larla bir iki saattir. Biri sağlanmıyorsa (veri silme, şema değişiklikleri, güvenlik kontrolleri, telemetrinizin dışında tüketilen her şey) bütçe, diff’in boyutu ne olursa olsun, sahip onayı ve yazılı kayıt içeren tam bir araştırmaya genişler.
Zaman sınırının bedeli, mühendislik zamanının peşin harcanması ve bütçenin sonucu ağır bir çit için fazla küçük kalabilmesi. Bütçeyi geri alınamazlıkla birlikte ölçeklemek işe yarar, çünkü geri alınamazlık çoğu zaman araştırma başlamadan önce görünür: değişikliğin saklanan veriye ya da bir güvenlik sınırına dokunup dokunmadığını, kodun neden var olduğunu öğrenmeden önce bilirsiniz. Pratik bir biçimi, adı konmuş bir dizin kümesi içindeki (migrasyonlar, IAM, faturalama) hiçbir değişikliğin, diff ne kadar önemsiz görünürse görünsün, kısa yolu kullanmaması kuralıdır.
Araştırmanın İkinci Yarısı: Kademeli Kaldırma#
Scream test ikinci soruyu doğrudan cevaplar: ilgili şeyi kapatın ve kimin fark ettiğine bakın. Microsoft’un Inside Track’teki sunucu yaşam döngüsü yazısı bunun kaba biçimini anlatıyor. Microsoft’ta bir mühendislik ekibi kimsenin sahiplenmediği makineleri kapatmış ve hâlâ kullanan birinden ses gelmesini beklemiş; makinelerin yaklaşık yüzde 15’inin kullanılmadığı ortaya çıkmış. Bu genellikle Chesterton Çiti’nin karşıtı olarak sunulur. Aslında aynı araştırmanın ikinci yarısına daha yakındır, çünkü kimsenin belgelemediği bir Hyrum Yasası bağımlısını arkeoloji bulamaz, bir gözlem penceresi bulabilir.
Sorumlu biçimi kademelidir. Kaldırmayı, bağımlıların okuyacağı yerde duyurun. Bir anahtarın arkasında devre dışı bırakın ya da davranışı düşürün; eski yolu deploy edilebilir tutun. Metrikleri, log’ları ve ticket’ları, makul en yavaş tüketiciye uyan bir pencere boyunca izleyin. Pencere kapandığında kodu, anahtarı ve ölü yolun testlerini birlikte silin.
En güçlü itiraz, kademelendirmenin gereksiz tören olduğudur: kodu silin, bir şey kırılırsa git revert zaten geri dönüş yoludur. Kırılma hızlı ve gürültülüyse bu doğrudur, çünkü aynı gün yapılan bir revert bir flag değişimi kadar ucuzdur. Şikâyet haftalar sonra gelirse işe yaramaz. O noktada revert, silme üzerine inşa edilen her şeyle çakışır; bir bağımlı da aradaki sürede hiçbir revert’ün düzeltemeyeceği yanlış veriler yazmış olabilir. Gözlem penceresi bu aralığı kapsar, eski yol ise o süre boyunca tek bir anahtar uzaklıkta kalır.
Scream test ayrıca yalnızca çığlıkları duyar. Aylık ya da üç aylık çalışan bir bağımlı gözlem penceresini kaçırabilir; müşteriye yansıyan bir olay olarak gelen çığlık gerçek bir maliyettir; sessiz veri bozulması ise hiç bağırmaz. Bu son durum yüzünden kademeli test yalnızca diyagramın geri alınabilir ve gözlemlenebilir dalına aittir. Gözlem penceresi en yavaş bağımlının döngüsü sorularak belirlenmelidir; rahat bir gün sayısı bu sorunun cevabı değildir.
Varsayılanı Ne Zaman Aşmalı#
Varsayılan (zaman sınırlı araştırma, sonra kademeli kaldırma) iki durumda devreden çıkar.
Değişiklik geri alınamazsa ya da başarısızlığı sessiz olacaksa, kademeli adım olmadan önce tam araştırma yapın. Veri silmeleri, şema değişiklikleri, güvenlik kontrolleri ve telemetrinizin dışındaki sistemlerin tükettiği davranışlar buraya girer. SEC’in Knight Capital kararı bu riskin öteki yüzünün yerleşik örneğidir. Karara göre yıllardır kullanılmayan bir bileşen ne silinmiş ne de devre dışı bırakılmış; yeniden amaçlandırılmış bir flag onu deployment’ı kaçıran bir sunucuda yeniden canlandırmıştır. Açıklanamayan bir çiti yerinde tutmak da kendi başarısızlık biçimi olan bir karardır; bu yüzden tam araştırma, amaç ortadan kalktığında yine kaldırmayla bitmelidir.
Değişiklik kolayca geri alınabiliyorsa, bağımlıların tamamı gözlemlenebilirliğinizin içindeyse ve arkeoloji bir kez zaten başarısız olduysa, zaman sınırını geçmişe kısa bir bakışa indirin. Ekleyen commit’i “fix” diyen ve ticket’ı kaybolmuş bir flag makul bir adaydır; yeter ki gözlem penceresi en yavaş tüketiciye göre belirlensin ve geri alma tek bir flag değişimi olsun.
Çit Araştırmalarında Sık Görülen Hatalar#
Tekrarlayan hatalar prosedüreldir ve her birinin doğrudan bir çözümü vardır.
- Çitin review’da veto olarak kullanılması. Çözüm: ilkeyi çağıran reviewer araştırmanın bir kısmını üstlenir ya da geri alma planı olan kademeli bir kaldırmayı kabul eder.
- Düz
git blame’in arkeolojinin tamamı sayılması. Bir formatlama commit’i ya da dosya taşıması yanlış yazara işaret eder. Çözüm:-w -C -C -C, commit’lenmiş bir.git-blame-ignore-revsve ilgili satır ya da fonksiyon üzerindegit log -Sveya-L. - Yalnızca özgün niyetin sorulması. Bugünkü bağımlılar kimsenin amaçlamadığı yan etkilere bağımlı olabilir. Çözüm: geçmişin yanında çağrı noktalarını ve trafiği de kontrol edin.
- Bir flag adının ya da bit’inin yeni davranış için yeniden kullanılması. Çözüm: yeni davranış yeni flag alır; eski flag’ler silinir, geri dönüştürülmez.
- Araştırıp iz bırakmamak. Çözüm: her araştırma, “X nedeniyle tutuldu” sonucu dahil, bir yorum, commit gövdesi ya da ADR ile biter.
Gerekçeyi Geride Bırakmak#
Çit araştırmasına, önceki değişiklik gerekçesini kaydetmediği için gerek duyulur. Kalıcı çözüm araştırmanın öncesindedir: bir çit ekleyen ya da tutan her değişiklik, bir sonraki okuyucunun aramadan bulacağı bir yere nedenini kaydeder. Eski kod üzerine iki klasik görüş burada uzlaşır. Joel Spolsky tüm sistemi baştan yazmaya karşı çıkar, ama argümanı tek bir kod dalına da taşınabilir: eski kodun çetrefilli kısımları aslında bug fix’lerdir ve onları atmak bilgiyi atmaktır. Software Engineering at Google’ın deprecation bölümü de kodun bir yükümlülük olduğu, değerin ise işlevsellikte yattığı konusunda haklıdır. Bilgi kodun şeklinden çıkarılıp bir kayda taşınırsa ikisi de geçerli kalır ve kodun kendisi değişebilir.
Yorumlar yerlerini, kodun neden var olduğunu açıklayarak hak eder. Google’ın code review rehberi tam olarak bunu söylüyor: yorumlar genellikle bir kodun neden var olduğunu açıkladığında faydalıdır ve kodun ne yaptığını anlatmamalıdır. Çitin üstüne yazılmış bir neden-yorumu en ucuz kayıttır ve çitin hangi koşulda gidebileceğini adlandırmalıdır. Doğal olarak bir yoruma sığmayan değişikliklerde aynı bilgiyi commit gövdesi taşır; Chris Beams’in commit mesajı rehberinde bu yedinci kuraldır.
// Neden: upstream ledger API'si, read replica yazmayı yakalayana kadar bir yazma
// sonrası kısa süreliğine 409 döndürüyor. Tek retry'dan önce 250 ms beklemek
// sahte "duplicate" hatasını önlüyor.
// Ledger istemcisi read-your-writes sunduğunda kaldır (bkz. ADR-0031).
await sleep(250);
Birden fazla dosyayı kapsayan kararlar bir mimari karar kaydına (ADR) aittir. Michael Nygard’ın özgün önerisi her ADR’yi küçük tutar (başlık, bağlam, karar, durum, sonuçlar) ve geri alınan kararları “superseded” olarak işaretleyip yerinde bırakır. Sorunu da çitle örtüşen sözlerle tanımlar: bir kararla bağlamı olmadan karşılaşan yeni ekip üyesi onu ya körü körüne kabul edebilir ya da körü körüne değiştirebilir. Bus factor yazısı ADR’lerin ekip bilgi tabanına nasıl oturduğunu, dokümantasyon altyapı olarak yazısı ise bu kayıtların nasıl canlı tutulacağını ele alıyor.
Feature flag’ler, amacını en sık aşan çit türüdür, çünkü rollout biter ve hiçbir şey temizliği zorlamaz. Pete Hodgson’ın feature toggle yazısı toggle’ları taşıma maliyeti olan envanter olarak ele alıyor. Bazı ekiplerin son kullanma tarihi eklediğini ve tarihi geçen toggle için bir testi ya da başlangıç kontrolünü başarısız kıldığını not ediyor. React Native’de feature flag yazısı çalışma zamanı tarafını ele alıyor; aşağıdaki metadata ve test yaşam döngüsü tarafını kapsıyor.
// flags.ts
export const FLAGS = {
newCheckout: { owner: 'payments', reason: 'ADR-0042 staged rollout', expires: '2027-01-15' },
legacyTaxRounding: { owner: 'billing', reason: 'ADR-0017 regulator audit', expires: '2027-03-01' },
} as const;
// flags.test.ts
import { describe, it, expect } from 'vitest';
import { FLAGS } from './flags';
describe('feature flag expiry', () => {
it.each(Object.entries(FLAGS))('%s has not expired', (name, flag) => {
const expires = new Date(flag.expires).getTime();
expect(expires, `${name} expired: remove it or extend it with a reason`).toBeGreaterThan(Date.now());
});
});
Süresi dolan bir flag CI’ı flag’in adıyla ve iki çıkış yolu sunan bir mesajla düşürür: flag’i ve ölü yolunu silin ya da aynı commit’te bir gerekçeyle tarihi uzatın. Aynı şekil firewall ve security group kurallarına da uygulanır; sahip, gerekçe ve gözden geçirme tarihini tutan bir açıklama alanı, çitin tabelasıdır. Değişiklik başına maliyet küçüktür. Kayıtlar kimse gözden geçirmezse çürür; ADR’ler kısa kaldıkça daha az çürür, bu da Nygard’ın küçük belgeler savunusunun bir parçasıdır.
Kaynaklar#
- Taking a Fence Down (The American Chesterton Society) (yeni sekmede açılır) - G. K. Chesterton’ın The Thing (1929) kitabındaki özgün çit pasajı ve çevresindeki bağlam.
- The Drift from Domesticity, in The Thing (Catholic Library) (yeni sekmede açılır) - Bölümün tam metni; amacına artık hizmet edilmeyen çitin kaldırılmasına izin veren cümle dahil.
- Chesterton’s Fence: A Lesson in Thinking (Farnam Street) (yeni sekmede açılır) - İlkenin ve sınırlarının anlaşılır açıklaması; mevcut durumun her zaman doğru olmadığı noktası dahil.
- Wikipedia: Chesterton’s fence (yeni sekmede açılır) - Wikipedia editörlerinin ilkeyi silmelere nasıl uyguladığı; kitap ve bölüm atfıyla birlikte.
- Hyrum’s Law (yeni sekmede açılır) - Yaygın kullanılan bir sistemin gözlemlenebilir her davranışına sonunda birilerinin bağımlı olacağı ifadesi.
- Software Engineering at Google, Chapter 15: Deprecation (yeni sekmede açılır) - Kodun neden bir yükümlülük olduğu ve tavsiye niteliğindeki ile zorunlu deprecation’ın pratikte nasıl ayrıldığı.
- Documenting Architecture Decisions (Michael Nygard) (yeni sekmede açılır) - Özgün ADR önerisi; beş parçalı formatı ve kayıtları küçük tutma gerekçesi.
- Things You Should Never Do, Part I (Joel Spolsky) (yeni sekmede açılır) - Eski kodun tuhaf köşelerinin birikmiş bug fix’ler olduğu argümanı.
- Feature Toggles (aka Feature Flags) (Pete Hodgson, martinfowler.com) (yeni sekmede açılır) - Taşıma maliyeti olan envanter olarak toggle’lar ve testle zorlanan son kullanma tarihleri.
- Moving from a Scream Test to holistic lifecycle management (Microsoft Inside Track) (yeni sekmede açılır) - Microsoft’ta bir mühendislik ekibinin kullanılmayan sunucuları kapatarak nasıl bulduğu ve bu yaklaşımın yerini neyin aldığı.
- SEC Order: Knight Capital Americas LLC, Release No. 34-70694 (yeni sekmede açılır) - Yeniden amaçlandırılmış bir flag ile canlanan uykudaki kodun yol açtığı işlem olayına dair düzenleyici bulgular.
- git-blame documentation (yeni sekmede açılır) -
-w,-M,-C,--ignore-revve--ignore-revs-fileiçin referans. - git-log documentation (yeni sekmede açılır) - Pickaxe seçenekleri
-Sve-Gile-Lsatır ve fonksiyon geçmişi için referans. - Google Engineering Practices: What to look for in a code review (yeni sekmede açılır) - Yorumların kodun neden var olduğunu açıklaması gerektiğine (ne yaptığını anlatmak yerine) dair rehber.
- How to Write a Git Commit Message (Chris Beams) (yeni sekmede açılır) - Commit gövdesini nasıl yerine ne ve neden sorularını açıklamak için kullanma kuralı.
İlgili yazılar
Legacy kodu kimin yazdığını sormayı bırakın. Sorumluluğu, hesap verebilirliği ve suçlamayı ayırın; miras kodu sahipsiz bırakmak yerine sahiplendirin.
engineering-culture · leadership · team-management +3
Bilgi dağıtımı, dokümantasyon stratejileri ve sistematik risk yönetimi ile ekibinizi tek hata noktalarından nasıl koruyacağınızı öğrenin.
team-management · documentation · knowledge-sharing +5
Webpack'ten önce dosyaları Grunt ile birleştirir, jQuery spagettisiyle boğuşurduk. Frontend tooling'in manuel dosya yönetiminden build sistemlerine evrimi.
javascript · build-tools · technical-debt +2
Acele etmek hızlı hissettirir ama yeniden iş, hata ve yangın söndürme yaratır. Refactor, test ve CI bakımı için durmak neden hız kaybı değil, hıza yatırımdır.
technical-debt · testing · ci-cd +2
Suçlu aramak yerine sistemi düzelten bir suçsuz postmortem modeli, kopyalanabilir bir şablon ve bireysel sorumluluğun hâlâ geçerli olduğu sınır.
engineering-culture · incident-response · psychological-safety +4