Preview Kodunu Temizlemek: Prototip Karmaşasından Modüler Mimariye Geçiş
Prototip borcunu temizlemenin amacı kodu güzelleştirmek değil, sistemin değiştirilebilirliğini geri kazanmaktır. Büyük rewrite yerine sınırları netleştirip riskli bölgeleri sırayla küçültmek daha güvenli sonuç verir.
Hızlı prototipleme araçlarının en büyük avantajı aynı zamanda en büyük riski: ürün çok erken 'bitmiş' gibi görünür. Ekranlar çalışır, butonlar tıklar, auth görünür ve birkaç gerçek kullanıcı bile sistemi kullanabilir. Fakat kod tabanının içinde aynı component'in üç varyasyonu, birbirinden habersiz state kaynakları, API çağrılarının UI içine gömülmesi ve any ile kapatılmış tip boşlukları birikmiş olabilir.
Production refactor'un amacı temiz kod estetiği değil; değişiklik yaparken neyin kırılacağını öngörebilmektir.
1. Önce refactor değil, haritalama
İlk işim dosya taşımak veya component bölmek değil. Sistemin gerçek akışını çıkarıyorum: route'lar, kritik kullanıcı akışları, veri kaynakları, mutation noktaları, global state, local state, cache ve platforma özel davranışlar. Bu harita olmadan yapılan refactor çoğu zaman sadece karmaşayı başka klasöre taşır.
type RefactorMap = { criticalFlows: string[]; stateOwners: Record<string, string>; dataSources: string[]; mutationPoints: string[]; duplicatePatterns: string[]; riskyModules: string[];};2. Component tekrarını görsel benzerlikten değil sorumluluktan çözmek
AI araçları aynı problemi farklı ekranlarda yeniden çözmeye eğilimlidir. Benzer görünen üç kartı tek dev component altında birleştirmek de yanlış olabilir. Önce ortak sorumluluğu ararım: veri gösterimi mi, etkileşim mi, durum yönetimi mi? Ortak olan davranışı çıkarır, ekranın bağlama özel tarafını dışarıda bırakırım.
3. State sahipliğini tekilleştirmek
Prototiplerde aynı veri URL state, component state, context ve remote cache içinde aynı anda tutulabilir. Bu yapı ilk başta hızlıdır; sonra 'hangi değer doğru?' sorusunu doğurur. Her state için tek bir sahip belirlerim: server state cache'de, form draft'ı local state'te, kalıcı kullanıcı tercihi storage veya backend'de, route'a ait seçim URL'de yaşar.
| State türü | Tercih edilen sahip | Kaçınılacak durum |
|---|---|---|
| Server data | Query/cache katmanı | Aynı veriyi Context + local state ile kopyalamak |
| Form draft | Form/local state | Her tuşta global store'a yazmak |
| Navigation | URL / router | Route bilgisini ikinci kez store'da tutmak |
| User preference | Persisted preference | Component mount oldukça varsayılana dönmek |
4. Veri erişimini UI'dan ayırmak
Component'in içinde doğrudan Supabase, fetch veya SDK çağrısı varsa UI; auth, veri şekli, hata modeli ve network davranışının tamamını bilmek zorunda kalır. Production'a geçerken bu sınırı servis/query katmanına taşırım. UI yalnızca ne istediğini söyler; verinin nereden ve nasıl geldiğini bilmez.
// UIconst { data, status } = useProject(projectId);
// data boundaryexport async function getProject(id: string): Promise<Project> { const row = await projectRepository.findById(id); return mapProject(row);}5. TypeScript'i susturmak yerine sözleşme olarak kullanmak
Prototipte any ve geniş optional tipler akışı hızlandırabilir. Production'da ise tiplerin görevi editörü memnun etmek değil, sistem sınırlarını belgelemektir. API response, domain entity ve UI view model aynı tip olmak zorunda değildir. Bu ayrım özellikle migration, feature flag ve geriye dönük uyumlulukta büyük fark yaratır.
6. Büyük rewrite yerine strangler refactor
Çalışan prototipi tek seferde yeniden yazmak çoğu solo ürün için gereksiz risk üretir. Önce yeni kuralları bir modülde uygular, eski akış ile arasında adapter kurar, davranışı doğrular ve sonra sıradaki modüle geçerim. Böylece ürün çalışmaya devam ederken mimari kademeli olarak değişir.
- Kritik kullanıcı akışlarını önce test veya gözlemlenebilir davranışla sabitle.
- En çok değişen veya en fazla hata üreten modülü seç.
- State ve data boundary'lerini tanımla.
- Yeni modülü eski davranışın arkasına yerleştir.
- Eski yolu kaldırmadan önce production davranışını karşılaştır.
- Her adımda bundle, network ve error yüzeyini yeniden ölç.
7. Ne zaman refactor tamamlanmış sayılır?
Dosya sayısı azaldığında değil; yeni bir özellik eklemek için beş farklı state kaynağını anlamak gerekmiyorsa, kritik mutation'ın nerede olduğu belliyse, veri erişimi tekrar etmiyorsa ve bir agent'a modülün sınırlarını kısa biçimde anlatabiliyorsam refactor amacına ulaşmıştır.
const modularEnough = singleStateOwnership && explicitDataBoundaries && typedContracts && predictableSideEffects && replaceableModules;Prototipten Production'a
Çalışan bir MVP'yi güvenilir ürüne dönüştürmek: production kontrolü, modüler refactor, veri migration'ı, audit, rewrite kararı ve release gate'leri.