500K Kod Satırı Monolitten Fonksiyonlara: Aklımı Kurtaran 2.3M Dolarlık Mimari Evrim
500K satır kodlu Node.js MVC monolitten event-driven serverless fonksiyonlara nasıl geçtik, maliyetleri 65% düşürüp deploy sürelerini 45 dakikadan 2 dakikaya indirdik. Gerçek sayılar, gerçek hatalar, gerçek çözümler.
45 Dakikalık Deploy Deveyi Hörgücün Üstünde Kırdı#
Mart 2022. Sabah 03:47. Production yine çökmüştü. 500,000 satır kodlu Node.js monolitimiz - "kurumsal kalitede" MVC kod yığını - Black Friday trafiği altında çökmüştü. Kritik bir bug'ı düzeltmek için deploy süresi? 45 dakika. O hafta aldığım uyku? Belki toplamda 12 saat.
Sabah 04:30'da ofis önünde arabamda oturarak, deployment'ın bitmesini beklerken, tüm mühendislik kültürümüzü dönüştürecek ve 2 yıl boyunca 2.3 milyon dolar tasarruf sağlayacak bir karar aldım: bu monoliti öldürecektik.
Bu yazı, legacy MVC canavarından event-driven serverless fonksiyonlara nasıl geçtiğimizi, bizi neredeyse öldüren mimari kararları ve nihayetinde hem aklımızı hem işimizi kurtaran basit ilkeleri anlatıyor.
Manhattan'ı Yutan Monolit#
E-ticaret platformumuz 2018'de masum bir şekilde başlamıştı. "Basit" bir Node.js Express uygulaması:
// Alçakgönüllü başlangıç - çok masum görünüyordu
app.use('/api/users', userController);
app.use('/api/products', productController);
app.use('/api/orders', orderController);
app.use('/api/inventory', inventoryController);
app.use('/api/payments', paymentController);
// ... 47 controller daha
2022'ye gelindiğinde, bu "basit" uygulama bir canavara dönüşmüştü:
- 500,000 satır kod 2,847 dosyaya yayılmış
- 52 farklı business domain tek repo'da
- Deploy süresi: 45 dakika (15 dakika "güvenlik" smoke test'i dahil)
- Takım hızı: ayda 2.3 feature (2019'da 12'den düştü)
- Altyapı maliyeti: ayda 23,000$ tek EC2 deployment için
- Debug süresi: geliştirme saatlerinin 67%'si (evet, ölçtük)
Asıl Sorun: Teknik Borç Değil, Bilişsel Yük#
Herkes monolitlerden bahsederken "teknik borç"tan söz eder, ama asıl katil bilişsel yüktü. Tipik bir "basit" feature şöyle görünüyordu:
// "Ürün önerisi" feature'ı eklemek için anlamam gerekenler:
// 1. User service (authentication + preferences)
class UserService {
async getUserPreferences(userId: string) {
// 347 satır business logic
// + 12 farklı database çağrısı
// + 4 external service entegrasyonu
}
}
// 2. Product service (catalog + inventory + pricing)
class ProductService {
async getRecommendations(userId: string, context: string) {
// 6 farklı öneri stratejisi boyunca 892 satır
// + ML model entegrasyonu
// + A/B test framework'ü
// + Cache invalidation logic (en zor kısım)
}
}
// 3. Order service (satın alma geçmişi analizi için)
class OrderService {
async getUserOrderHistory(userId: string, limit?: number) {
// 234 satır karmaşık SQL join
// + Veri gizliliği uyum logic'i
// + "VIP" kullanıcılar için performans optimizasyonları
}
}
// 4. Analytics service (önerileri takip etmek için)
class AnalyticsService {
async trackRecommendationEvent(event: RecommendationEvent) {
// 156 satır event işleme
// + GDPR uyumu
// + Rate limiting
// + Queue management
}
}
Tek bir öneri endpoint'i eklemek için 4 service boyunca 1,629 satır kodu, 23 database tablosunu ve 7 external API'yi anlamam gerekiyordu. 8 yıl Node.js deneyimi olan senior bir mühendis, tek satır kod yazmadan önce mevcut kodu anlamak için 3 hafta gerekiyordu.
Kırılma Noktası: Akıllı İnsanlar Feature Ship Edemediğinde#
Son damla 2021 Q4 planlamamız sırasında geldi. Product head'imiz "basit" bir feature request sundu: "Birisi sepete ürün eklediğinde ilgili ürünleri gösterebilir miyiz?"
2019'da bu 2 günlük bir iş olurdu. 2021'de gerçekte ne gerektiriyordu:
- Hafta 1-2: Cart service mimarisini anla
- Hafta 3: Checkout flow'u bozmadan öneri motoru ile nasıl entegre edileceğini çöz
- Hafta 4: Mevcut 14,000 test case ile karışmayacak testler yaz
- Hafta 5: Deploy et ve dua et (spoiler: mobile app'i bozdu)
- Hafta 6: Mobile app'i düzelt, admin panel'i boz
- Hafta 7: Admin panel'i düzelt, öneri email sistemini boz
- Hafta 8: Vazgeç ve basitleştirilmiş versiyonu ship et
Sonuç: 2 gün sürmesi gereken feature için 8 hafta mühendislik zamanı.
Hız metriklerimiz acımasız hikayeyi anlatıyordu:
// Zaman içindeki mühendislik hızı
const velocityData = {
'2019': {
featuresPerMonth: 12,
avgDeployTime: '8 dakika',
hotfixTime: '15 dakika',
engineerHappiness: 8.2
},
'2021': {
featuresPerMonth: 2.3,
avgDeployTime: '45 dakika',
hotfixTime: '2.5 saat',
engineerHappiness: 4.1
},
'2022-Q1': {
featuresPerMonth: 1.1,
avgDeployTime: '67 dakika',
hotfixTime: '4.7 saat',
engineerHappiness: 2.8 // Bu çeyrekte iki mühendis istifa etti
}
};
2.3 Milyon Dolarlık Karar: Microservice'ler ya da Ölüm#
"Her şeyi yeniden yaz" ve "karmaşıklığı yönetmek için 20 mühendis daha işe al" arasında seçim yapmakla karşı karşıya kalınca, üçüncü bir seçenek belirledik: microservice'lere stratejik ayrıştırma.
Ama çoğu şirketin yaptığı gibi değil. Domain-Driven Design kitaplarını takip etmedik ya da beyaz tahtaya güzel service sınırları çizmedik. Acıyı takip ettik.
Acı-Odaklı Service Extraction Metodu#
Akademik domain modeling yerine, deployment acısına göre service'leri belirledik:
- Birlikte değişen service'ler, birlikte deploy ediliyor
- Birlikte bozulan service'ler, birlikte debug ediliyor
- Birlikte scale olan service'ler, birlikte acı çekiyor
İlk extraction adaylarımızı nasıl belirlediğimiz:
// 3 ay boyunca deployment korelasyonunu takip ettik
const deploymentCorrelation = {
'user-service': ['auth-service', 'notification-service'],
'product-service': ['inventory-service', 'pricing-service'],
'order-service': ['payment-service', 'shipping-service'],
// Yüksek acı korelasyonu = birlikte çıkar
'recommendation-service': [], // Hep tek başına bozuluyordu = tek başına çıkar
'admin-service': [], // Farklı release cycle = tek başına çıkar
'analytics-service': [] // Farklı scaling ihtiyacı = tek başına çıkar
};
Mimari Evrim: Üç Aşamalı Yolculuk#
Aşama 1: Bariz Kazançları Çıkar (Aylar 1-3)#
Şu özelliklere sahip service'lerle başladık:
- Zaten izole (minimum database paylaşımı)
- Yüksek acı, düşük risk (analytics, admin araçları)
- Farklı scaling karakteristikleri (ML önerileri)
Loading diagram...
3 ay sonraki sonuçlar:
- Deploy süresi: 45 dakika → 28 dakika
- Analytics sorguları: Artık ana uygulamayı bloklamıyor
- Admin feature'ları: 3x hızlı geliştirme
- Altyapı maliyeti: 23K$ → 19K$/ay
Aşama 2: Korkutucu Orta Kısım (Aylar 4-8)#
Bu aşama korkunçtu çünkü gelir getiren çekirdekimizi içeriyordu: ürünler, siparişler ve ödemeler.
Loading diagram...
Event-Driven Atılım:
Oyunu değiştiren, service iletişim omurgamız olarak AWS EventBridge'i tanıtmakmydı. Service'ler arası HTTP çağrıları yerine, event-driven mimariye geçtik:
// Önce: Synchronous kabus
async function processOrder(orderData) {
// Bu HERHANGI BİRİ başarısız olursa, tüm sipariş başarısız olur
const user = await userService.validateUser(orderData.userId);
const inventory = await inventoryService.reserveItems(orderData.items);
const payment = await paymentService.processPayment(orderData.payment);
const shipping = await shippingService.calculateShipping(orderData.address);
// 4 service, 4 başarısızlık noktası, sabah 3'te çağrılma için 4 sebep
return await orderService.createOrder({ user, inventory, payment, shipping });
}
// Sonra: Event-driven dayanıklılık
async function processOrder(orderData) {
// Siparişi hemen oluştur
const order = await orderService.createOrder(orderData);
// Event publish et, diğer service'lerin asynchronous tepki vermesine izin ver
await eventBridge.publish('order.created', {
orderId: order.id,
userId: orderData.userId,
items: orderData.items,
timestamp: Date.now()
});
// Coupling yok, cascade failure yok, sabah 3'te çağrı yok
return order;
}
Aşama 3: Serverless Sıçrayışı (Aylar 9-12)#
- ay itibarıyla, service sınırlarımız hakkında AWS Lambda fonksiyonlarına geçiş yapmak için yeterince şey öğrenmiştik:
Loading diagram...
Serverless Dönüşüm:
Her microservice, odaklanmış Lambda fonksiyonlarının bir koleksiyonu haline geldi:
// product-service/functions/get-product.ts
export const handler = async (event: APIGatewayEvent) => {
const { productId } = event.pathParameters;
// Tek sorumluluk: Ürün verisini al
const product = await dynamodb.get({
TableName: 'Products',
Key: { id: productId }
}).promise();
return {
statusCode: 200,
body: JSON.stringify(product.Item)
};
};
// product-service/functions/inventory-updated.ts
export const handler = async (event: EventBridgeEvent) => {
const { productId, newQuantity } = event.detail;
// Tek sorumluluk: Envanter değişikliklerine tepki ver
await dynamodb.update({
TableName: 'Products',
Key: { id: productId },
UpdateExpression: 'SET inventory = :qty',
ExpressionAttributeValues: { ':qty': newQuantity }
}).promise();
// Gerekirse downstream event publish et
if (newQuantity === 0) {
await eventBridge.putEvents({
Entries: [{
Source: 'product-service',
DetailType: 'Product Out of Stock',
Detail: JSON.stringify({ productId })
}]
}).promise();
}
};
Sonuçlar: Sayılar Yalan Söylemez#
12 aylık evrim sonrasında, dönüşüm tamamlanmıştı:
Performans Metrikleri#
Metrik | Monolit (2021) | Final Mimari (2023) | İyileştirme |
---|---|---|---|
Deploy Süresi | 45 dakika | 2.3 dakika | 95% daha hızlı |
Hotfix Süresi | 4.7 saat | 8 dakika | 97% daha hızlı |
Feature/Ay | 1.1 | 8.7 | 690% artış |
P99 Response Süresi | 2,300ms | 340ms | 85% daha hızlı |
Maliyet Analizi#
// Maliyet karşılaştırması
const costAnalysis = {
monolit2021: {
infrastructure: 23000, // EC2 instance'ları + load balancer'lar
monitoring: 1200, // Custom monitoring stack
deployment: 3400, // CI/CD altyapısı
total: 27600
},
serverless2023: {
infrastructure: 8900, // Lambda + DynamoDB + EventBridge
monitoring: 340, // CloudWatch (çok daha basit)
deployment: 0, // Native AWS deployment
total: 9240
},
aylikTasarruf: 18360, // Yılda 220K$ tasarruf
toplamTasarruf: 2300000 // 24 ay boyunca
};
Developer Experience#
En önemli metrik maliyet ya da performans değildi - developer mutluluğuydu:
- Onboarding süresi: 3 hafta → 2 gün
- Bug isolasyonu: 4.7 saat → ortalama 12 dakika
- Feature geliştirme: Artık cross-service arkeolojisi yok
- On-call incident'ları: 23/ay → 2/ay
Kilit Dersler: Gerçekte Ne İşe Yaradı#
Bu dönüşümü yönettikten sonra, gerçekte işe yarayan dersler:
1. Acı-Odaklı Mimari Akademik Modellemeyi Geçer#
Domain model'lerle başlamayın. Deployment acısı, debug acısı ve geliştirme acısıyla başlayın. Service'ler sadece business domain'lerle değil, takım bilişsel sınırları ile hizalanmalı.
2. Service İletişimi İçin Event'ler > HTTP#
Event-driven mimari sadece decoupling hakkında değil - dayanıklılık hakkında. Service'ler event'ler aracılığıyla iletişim kurduğunda, başarısızlıklar cascade felaketler yerine izole incident'lar haline gelir.
3. Çoğu Kullanım Durumu İçin Fonksiyonlar > Service'ler#
Çoğu business logic için microservice'lerin karmaşıklığına ihtiyacınız yok. Fonksiyonların basitliğine ihtiyacınız var. Bir fonksiyon = bir sorumluluk = bir deploy = bir debug session.
4. Monitoring Pazarlık Konusu Değil#
40+ Lambda fonksiyonuyla, distributed tracinge ilk günden ihtiyacınız var:
// Her fonksiyonun başından beri buna ihtiyacı var
import { captureAWS, captureHTTPsGlobal } from 'aws-xray-sdk';
import AWS from 'aws-sdk';
// Tüm AWS çağrılarını trace et
captureAWS(AWS);
captureHTTPsGlobal(require('https'));
export const handler = async (event, context) => {
// X-Ray otomatik olarak bu fonksiyonun çalıştırılmasını trace edecek
// ve downstream AWS service çağrılarıyla korelasyona sokacak
};
Devamı: Microservice'lerden Saf Fonksiyonlara#
Monolitten microservice'lere olan bu yolculuk bize ihtiyacımız olduğunu düşündüğümüz şeylerin çoğuna ihtiyacımız olmadığını öğretti. Dependency injection framework'leri yok. Service registry'leri yok. Karmaşık orkestrasyon yok.
Sadece event'lere tepki veren basit fonksiyonlar.
2. Bölüm'de son sıçrayışı nasıl yaptığımızı göstereceğim: microservice'lerden saf, stateless fonksiyonlara. Node.js'in tasarlandığı fonksiyonel programlama ilkelerini benimser factory'lere, service'lere ve karmaşık dependency ağaçlarına olan ihtiyacı nasıl ortadan kaldırdığımızı.
Şunları ele alacağız:
- Factory'ler ve service'ler çoğu kullanım durumu için neden aşırı mühendislik
- Saf fonksiyonlarla event-driven sistemler inşa etme
- Milyonlarca isteğe ölçeklenen AWS Lambda pattern'leri
- Sizi akıllı tutan monitoring ve debugging stratejileri
Serverless devrimi altyapı hakkında değil - basitlik hakkında.
Yorumlar (0)
Sohbete katıl
Düşüncelerini paylaşmak ve toplulukla etkileşim kurmak için giriş yap
Henüz yorum yok
Bu yazı hakkında ilk düşüncelerini paylaşan sen ol!
Yorumlar (0)
Sohbete katıl
Düşüncelerini paylaşmak ve toplulukla etkileşim kurmak için giriş yap
Henüz yorum yok
Bu yazı hakkında ilk düşüncelerini paylaşan sen ol!