# Kritik Mikroservisleri Rust ile Modernize Etmek: Strangler Fig Deseni ve Memory Safety’nin (Bellek Güvenliği) Finansal Getirisi (ROI)

Gece 02.17’de ödeme servisi alarm veriyor. Ortalama yanıt süresi hâlâ iyi; p99 yükselmiş, instance sayısı artırılmış, fakat aynı belirti geri geliyor. Ekip “Rust’a geçelim” diyor. Bu önerinin değeri, Rust’ın ne kadar sevildiğiyle değil, gecikmenin hangi bölümünü ortadan kaldıracağı ve geçiş boyunca paranın hangi sistem tarafından tahsil edileceğiyle anlaşılır.

Kritik mikroservisleri Rust ile modernize etmek, çalışan sistemi başka sözdizimiyle yeniden yazmak değildir. CPU ve allocation profiliyle bir sınır seçmek, Strangler Fig ile o sınırı kademeli değiştirmek, iş kurallarını korumak ve kazanımı gerçek faturaya çevirebilmektir. Bu rehber ödeme akışı üzerinden ilerliyor; parser, fiyatlandırma, fraud değerlendirme ve medya işleme gibi dar sıcak yollar için de aynı karar yöntemi uygulanabilir.

Önce iki sınırı koyalım: Rust, bellek sızıntısını veya tüm çökmeleri ortadan kaldırmaz. Go ve Java da normal güvenli kullanımda C++ ile aynı bellek bozulması risk modeline sahip değildir. Dolayısıyla “Rust’a geçince TCO sıfır olur” veya “GC yoksa latency düzdür” önermeleriyle yatırım hesabı yapılmaz. Aşağıda güvenlik garantisini, performans ölçümünü ve finansal varsayımı ayrı ayrı göstereceğiz.

## 1. Memory safety’nin finansal karşılığı: hangi arıza sınıfı azalıyor?

C/C++ servisinde use-after-free, double-free veya geçersiz pointer erişimi; crash dump incelemesi, acil yama, güvenlik değerlendirmesi ve on-call kesintisi doğurabilir. Rust’ın ownership/borrowing modeli, sound bağımlılıklar ve doğru unsafe sınırları koşuluyla safe kodda bu sınıfların oluşmasını engellemeyi hedefleyen bir dil sözleşmesidir. [Rust Reference](https://doc.rust-lang.org/reference/behavior-considered-undefined.html) hangi davranışların undefined behavior olduğunu tanımlar. Bu, yalnız “null kontrolü ekledik” türünde yerel bir önlemden daha güçlü bir sınırdır.

Ancak ownership bir gelir tablosu değildir. [Rust kitabı](https://doc.rust-lang.org/book/ch15-06-reference-cycles.html) referans döngülerinin bellek sızdırabileceğini açıkça anlatır. Sınırsız cache, unutulan kaynak, OOM, panic, deadlock, yanlış fiyat hesabı ve ağ kesintisi hâlâ mümkündür. [Unsafe/FFI sınırında](https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html) ek doğruluk yükümlülükleri vardır. Rust, C kütüphanesine geçirilen pointer’ın ömrünü karşı taraf adına doğrulamaz.

Java/Go’dan Rust’a geçişte kazanç hipotezi çoğu zaman başka yerdedir: allocation baskısı, heap tarama işi, temsil maliyeti ve sıcak yoldaki CPU tüketimi. Modern GC’ler yalnız uzun stop-the-world duraklamalarından ibaret değildir. [Go GC rehberi](https://go.dev/doc/gc-guide), CPU/bellek dengesini ve GOMEMLIMIT’in soft limit oluşunu açıklar. [G1 tuning rehberi](https://docs.oracle.com/en/java/javase/21/gctuning/garbage-first-garbage-collector-tuning.html) da throughput/latency tercihlerini ele alır. Collector ve runtime sürümü yazılmadan yapılan “Java yavaş” genellemesi teknik kanıt sayılmaz.

Güvenlik ve maliyet sınırları

| Sorun | Rust’ın etkisi | Kalan mühendislik işi |
| --- | --- | --- |
| Use-after-free / double-free | Safe Rust ve sound soyutlamalarla bu sınıflara karşı dil düzeyinde koruma | Unsafe, FFI ve bağımlılık incelemesi |
| Data race | Safe Rust bu UB sınıfını dışlar | İş akışı race condition’ları ve distributed tutarlılık |
| GC işi | Tracing GC ihtiyacını kaldırır | Allocator, Drop, lock contention ve scheduler ölçümü |
| Bellek sızıntısı / OOM | Sıfırlamaz | Cache/admission sınırları, RAM headroom ve soak test |
| Yanlış veya tekrarlı ödeme | Önlemez | Domain doğrulaması, idempotency, transaction ve reconciliation |

[Rustonomicon’un ayrımı](https://doc.rust-lang.org/nomicon/races.html) önemlidir: data race ile genel race condition aynı şey değildir. İki ayrı servis aynı ödeme niyetini iki kez işlediğinde borrow checker hata vermez. TCO’daki güvenilirlik kazanımı, geçmiş olayların hangi kısmının gerçekten bellek güvenliğiyle ilişkili olduğunu sınıflandırarak tahmin edilmelidir; tüm incident bütçesini “Rust tasarrufu” hanesine yazamayız.

## 2. Önce profil: hangi endpoint Rust için aday?

Ödeme endpoint’i 240 ms sürüyorsa bunun 190 ms’si sağlayıcı, 35 ms’si veritabanı, 15 ms’si yerel iş olabilir. Bu açıklayıcı varsayımda yerel işi üç kat hızlandırmak toplamı yaklaşık 230 ms’ye indirir: 240/(225+15/3) ≈ 1,043 kat. Yüzde 67 yerel hızlanma, uçtan uca yaklaşık yüzde 4,2 süre azalmasıdır. Amdahl hesabı bile “tam rewrite” teklifinden önce yapılabilecek ucuz bir kontroldür.

Flamegraph, allocation profili ve trace ile CPU’ya gerçekten harcanan süreyi kuyruk beklemesinden ayırın. Aynı trafik diliminde request/s, CPU-saniye/request, allocation byte/request, RSS ve p50/p95/p99 alın. DB pool kuyruğu, DNS/TLS, kilit ve cgroup throttling ayrı etiketlensin. GC olayıyla gecikme yan yana geldiğinde korelasyon bulunur; kök neden iddiası için runtime logları ve kontrollü karşılaştırma gerekir.

İlk taşınacak sınırın seçimi

| Belirti | İlk denenecek müdahale | Rust kararı |
| --- | --- | --- |
| CPU-bound parser/codec veya fiyat motoru | Temsil, kopya ve algoritma profilini çıkar | Dar ve saf fonksiyon sınırı güçlü aday |
| DB/network beklemesi baskın | Sorgu, indeks, cache ve timeout bütçesi | Dil değişimi beklemeyi çözmez |
| Büyük heap / allocation baskısı | Allocation azalt, collector ve heap ayarını ölç | Kazanç sürüyorsa kontrollü Rust deneyi |
| C/C++ bellek bozulması geçmişi | Riskli bileşeni ve FFI yüzeyini daralt | Memory safety önemli bağımsız gerekçe |
| Küçük trafik, güçlü mevcut ekip | Mevcut runtime’ı koru ve sadeleştir | İkinci dilin işletim maliyeti ağır basabilir |

Discord’un [2020 Read States geçişi](https://discord.com/blog/why-discord-is-switching-from-go-to-rust) büyük canlı veri kümesi ve GC taraması için değerli bir vaka. Ekip Rust’a geçişin yanında veri yapısı ve kopya sayısını da değiştirdi. Bu, dar bir servis üzerinden profil ve canary ile ilerleme örneğidir; 2026 Go sürümlerinin tamamı için geçerli bir benchmark veya sizin ödeme servisinizin tasarruf oranı değildir.

## 3. Strangler Fig: iş sözleşmesi kalır, uygulama parça parça değişir

[Strangler Fig](https://martinfowler.com/bliki/StranglerFigApplication.html), legacy sistemin çevresine bir geçiş sınırı koyarak yetenekleri aşamalı değiştirmeyi sağlar. Geçici router, karşılaştırma hattı ve çift şema desteği ücretsiz değildir; bunların kaldırılma koşulu planlanır. [Eski yazılım modernizasyonu rehberimizde](/tr/blog/eski-yazilim-modernizasyonu-veri-tasima-gecis-plani) anlattığımız veri sahipliği ve geri dönüş planı burada da geçerlidir.

İlk sınır doğrudan karttan tahsilat olmak zorunda değil. /api/v1/process-payment içindeki saf risk skoru veya komisyon hesabı önce çıkarılabilir. Rust aynı versiyonlu girdiyi alır, yan etkisiz karar üretir; mevcut servis tahsilatın sahibi kalır. Bu daha küçük seam, gerçek para üzerinde iki ayrı writer oluşturmadan doğruluk ve kaynak tüketimini karşılaştırmayı sağlar.

```mermaid
flowchart LR
    C["Client: stable payment intent"] --> G["Authenticated API gateway"]
    G --> R["Pinned owner registry / deadline / admission"]
    R --> L["Legacy payment owner"]
    R --> N["Rust payment owner"]
    L --> D["Shared acceptance / idempotency ledger"]
    N --> D
    D --> O["Transactional outbox"]
    O --> P["Payment provider: native idempotency"]
    D --> Q["Status / reconciliation"]
    L -. "Redacted immutable evaluation input" .-> S["Rust shadow evaluator: NO writes"]
    S --> X["Decision diff / business invariants"]
```

Tam endpoint taşınacaksa ortak sözleşme; tenant, payment_intent_id, currency, amount_minor, quote/policy sürümü, idempotency key, deadline ve response status modelini kapsar. JSON number ile kayan noktalı para taşımayın. Currency exponent, yuvarlama ve overflow davranışı açık olsun. Bu alanların aynı isimde olması semantik eşdeğerlik demek değildir: “kabul edildi” ile “tahsil edildi” ayrımı da korunur.

```json
{
  "contract_version": 3,
  "payment_intent_id": "00000000-0000-4000-8000-000000000017",
  "amount_minor": 125050,
  "currency": "TRY",
  "quote_version": 17,
  "policy_version": "risk-2026-11",
  "deadline_at": "2026-11-05T09:00:02Z"
}
```

Tenant kimliği doğrulanmış erişim bağlamından gelir; payload içindeki müşteri iddiasına güvenilmez. Request hash’i canonical domain verisinden hesaplanır. Aynı intent farklı idempotency key ile gelse bile domain uniqueness korunur. Önceki yanıtın belirsiz olması yeni ödeme niyeti oluşturmak için gerekçe değildir. Bunun transaction/outbox tarafını [idempotent kurumsal API blueprint’imiz](/tr/blog/ai-ajanlarini-erp-crm-sistemlerine-guvenle-baglamak-idempotent-api-outbox) ayrıntılandırıyor.

Wire format da sözleşmenin parçasıdır. JSON’daki örnek tutar JavaScript’in safe integer aralığında; i64’ün tamamı bu aralığa sığmaz. API ya daha dar bir tutar sınırını doğrulamalı ya da canonical decimal string ve kontrollü integer parse kullanmalıdır. Aşağıdaki Rust fonksiyonunun i64::MAX testi, bir JavaScript istemcisinin o değeri JSON number olarak kayıpsız taşıdığını kanıtlamaz.

## 4. Canary routing: aynı ödeme denemesi farklı writer’a kaymamalı

Her isteği rastgele yüzde 1 Rust’a göndermek, salt okunur hesaplama için makul bir başlangıç olabilir; tahsilat için yeterli değildir. Retry, rollout yüzdesi veya gateway restart’ı aynı iş niyetini başka uygulamaya taşıyabilir. Yeni intent için deterministik cohort seçilir; seçilen owner kabul kaydında pinlenir. Mevcut intent aynı owner ve sonuç kaydından devam eder. Tenant ve iş kimliği gateway tarafından doğrulanır; dışarıdan gelen X-Route: rust başlığı yetki sayılmaz.

```sql
-- Illustrative routing registry; NOT the full payment/idempotency transaction.
CREATE TABLE payment_route (
    tenant_id uuid NOT NULL,
    payment_intent_id uuid NOT NULL,
    owner text NOT NULL CHECK (owner IN ('legacy-v3', 'rust-v3')),
    contract_version integer NOT NULL,
    PRIMARY KEY (tenant_id, payment_intent_id)
);

BEGIN ISOLATION LEVEL READ COMMITTED;
INSERT INTO payment_route (tenant_id, payment_intent_id, owner, contract_version)
VALUES ($1, $2, $3, 3)  -- $3 chosen by trusted rollout policy for NEW intent
ON CONFLICT (tenant_id, payment_intent_id) DO NOTHING;

SELECT owner, contract_version FROM payment_route
WHERE tenant_id = $1 AND payment_intent_id = $2;
COMMIT;
-- Gateway forwards to the persisted owner, never to the proposed $3 blindly.
-- Acceptance still validates canonical payload, identity and business uniqueness.
```

Registry’nin varlığı tek başına exactly-once sağlamaz. İki uygulama aynı domain kabul/sonuç sözleşmesini uygulamalı; transaction, outbox ve sağlayıcı idempotency sınırı korunmalıdır. Kaydı kabul eden writer’ın crash sonrası lease/fencing ve recovery davranışı da tanımlanır. Router veritabanına her seferinde yeni darboğaz eklememek için cache uygulanabilir; ancak cache miss, stale owner ve invalidation kuralları doğruluk sözleşmesinin parçasıdır.

```nginx
# Architecture example only; these are dedicated internal application ports.
# payment-router authenticates, enforces limits, resolves pinned ownership,
# and dispatches to legacy or Rust. It is NOT a second payment writer.
upstream payment_router {
    server 127.0.0.1:18081;
    keepalive 16;
}

location = /api/v1/process-payment {
    client_max_body_size 16k;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header Idempotency-Key $http_idempotency_key;
    proxy_connect_timeout 200ms;
    proxy_read_timeout 2s;
    proxy_next_upstream off;
    proxy_pass http://payment_router;
}
# All other routes retain their existing upstream.
# Forward only verified identity; strip/overwrite client-supplied internal headers.
```

[NGINX proxy belgesi](https://nginx.org/en/docs/http/ngx_http_proxy_module.html) açısından proxy_read_timeout bir toplam iş deadline’ı değil, ardışık okuma işlemleri arasındaki süredir. Toplam deadline uygulamada taşınır ve kalan bütçe alt çağrılara verilir. Örnekte kör upstream retry kapalıdır. Yanıt beklerken timeout oluştu diye kabul edilmiş tahsilat başka writer’a tekrar gönderilmez; aynı intent’in durumu sorgulanır veya reconciliation başlatılır.

## 5. Shadow, sözleşme testi ve rollback’in gerçek sınırı

Shadow trafik gerçek ödeme sağlayıcısına ikinci çağrı yapmaz. Yan etkisiz Rust evaluator’a maskelenmiş ve versiyonlanmış girdiler gönderilir; legacy kararlarıyla reason code, amount, rounding ve policy sonucu karşılaştırılır. Saat, kur, cache ve rastgelelik sabitlenmeden çıkan farklar anlamlı değildir. Ödeme token’larını, kişisel veriyi veya üretim sırlarını debug amaçlı ikinci bir depoya kopyalamayın.

Aşamalı geçiş için örnek kabul kapıları

| Aşama | Kanıt | Geri dönüş sınırı |
| --- | --- | --- |
| Offline replay | Versiyonlu corpus; overflow, rounding ve sınır değerleri | Üretim etkisi yok |
| Shadow evaluator | Semantik fark incelemesi; hiçbir dış write yok | Shadow tüketicisini kapat |
| Yeni intent’lerde %1 canary | Aynı yükte p99, hata ve business invariant; duplicate=0 hedefi | Yeni atamayı durdur; mevcut intent owner’ı korunur |
| %5 → %25 → %100 | Temsili peak, soak, incident/recovery denemesi | Yeni atamayı geri çevir; kabul edilmiş işi reconcile et |
| Legacy kapatma | Retention/replay penceresi, rollback ve support kararı | Eski writer ancak açık migration prosedürüyle devreye alınır |

Bu yüzdeler ve sıfır duplicate hedefi bir ürün garantisi veya ölçülmüş başarı oranı değildir; rollout politikası örneğidir. Trafik hacmi düşükse yüzde 1 dilimi istatistiksel karşılaştırma için yetersiz kalır. Hata bütçesi, örnek sayısı ve gözlem süresi servis SLO’suna göre seçilir. Bir tahsilatı geri almak deploy rollback’i değildir; gerektiğinde ayrı, denetlenebilir refund/void işlemi gerekir. Şema değişiminde expand-contract uygulanır, eski reader desteği geçiş bitmeden kaldırılmaz.

## 6. Rust servisinde de kuyruk, bellek ve CPU sınırı gerekir

Tokio’ya CPU-bound bir döngüyü async fn içine koymak onu non-blocking yapmaz. Uzun hesap runtime worker’ını meşgul eder. [spawn_blocking belgesi](https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html), CPU işleri için paralellik sınırı gerektiğini ve başlayan blocking işin abort ile durdurulamayacağını belirtir. Sınırlı worker havuzu/semaphore, admission control ve cooperative cancellation tasarlanır; timeout yalnız bekleyen future’ı bırakıyorsa CPU işi arkada devam edebilir.

```text
Request budget example — design assumptions, not measured SLOs
  total deadline:          2,000 ms
  authentication/routing:    50 ms budget
  local evaluation:         100 ms budget, bounded queue
  acceptance transaction:   150 ms budget
  provider interaction:     remaining budget, or async status contract

Bound every resource, not just request count:
  payload bytes, decompressed bytes, in-flight requests,
  queue length + wait age, cache entries/bytes, DB connections,
  CPU tasks, downstream concurrency and response bytes.

Never free an admission permit before the actual CPU job stops.
An HTTP disconnect does not cancel an accepted financial operation.
```

Rust’ın deterministic destruction modeliyle büyük bir ağacı veya cache segmentini tek anda drop etmek de latency üretebilir. Allocator kilidi, page fault, disk, kernel scheduling ve CPU throttling GC olmasa da vardır. “Derleniyorsa çalışır” hissi, güvenli refactor için yararlıdır; fiyatın doğru, yetkinin geçerli veya hizmetin yüksek erişilebilir olduğunu kanıtlamaz. Production kabulü hâlâ test, gözlem ve hata kurtarma ister.

Canary dashboard’unda route owner, contract/policy sürümü ve rollout cohort’u ayrı boyutlar olsun; intent veya müşteri ID’si metrik label’ı yapılmasın. Trace bağlantısı kontrollü logda tutulabilir. p99’un yanında queue age, rejected admission, CPU throttling, RSS/allocator farkı, panic ve reconciliation backlog izlenir. Tek bir error rate, 500 yanıtıyla sağlayıcıda sonucu belirsiz ödeme arasındaki farkı göstermez.

İkinci dilin güvenlik ve bakım yükü de vardır: compiler/MSRV ve lockfile politikası, bağımlılık güncellemeleri, SBOM, unsafe/FFI envanteri ve native kütüphane sahipliği belirlenir. Runtime patch’lerini takip edecek reviewer yoksa daha küçük binary tek başına işletim avantajı değildir. Panic stratejisi, graceful shutdown ve kabul edilmiş işin restart sonrası devamı deploy sözleşmesinde açık olmalıdır.

## 7. Rust vs. Go vs. Java: TankDev’in kontrollü HTTP deneyi

Rakamları bir dil sıralaması tablosundan almadık. Kendi VPS’imizde üç küçük referans uygulamayı aynı deterministik iş yüküyle çalıştırdık. Amaç; idle RSS, kaynak tüketimi, latency dağılımı ve build kapsamını birlikte okuyabilmek. Bu, gerçek ödeme servisi veya framework karşılaştırması değildir; ölçümün sınırlarını sonuç kadar önemli görüyoruz.

Aynı sentetik HTTP işi: 800 request/s, üç tekrar

| Uygulama | Idle RSS medyanı (MiB) | Yükte en yüksek örnek RSS (MiB) | Pooled p50 / p95 / p99 (ms) | Hata / sunulan istek | Build medyanı (s) |
| --- | --- | --- | --- | --- | --- |
| Rust | 2.082 | 2.094 | 1.214 / 1.802 / 2.047 | 0 / 36,000 | 0.496 |
| Go | 3.957 | 8.629 | 1.219 / 1.814 / 2.101 | 0 / 36,000 | 0.188 |
| Java | 40.629 | 58.996 | 1.198 / 1.783 / 2.034 | 0 / 36,000 | 0.573 |

Tablo 800 request/s dilimindeki üç koşunun toplam 36.000 isteğini her uygulama için birleştirir. Percentile, ham sürelerin birleşiminden nearest-rank ile hesaplandı; üç p95’in ortalaması alınmadı. Idle RSS, yeni süreç hazır olduktan yaklaşık iki saniye sonra ve warm-up öncesinde /proc/PID/status üzerinden ölçüldü. Yük RSS’i 100 ms aralıkla örneklendi; anlık zirveyi kaçırabilir. Java heap sınırı RSS sınırı değildir.

Tekrarlanabilir test sözleşmesi

| Alan | Koşul |
| --- | --- |
| Tarih / host | 5 Kasım 2026 Türkiye saati; aynı 2 logical CPU VPS; paylaşımlı komşu servisler mevcut |
| Toolchain | rustc 1.75.0 (82e1608df 2023-12-21) (built from a source tarball); go version go1.22.2 linux/amd64; openjdk version "21.0.12.1" 2026-08-18 |
| İş | 4096 byte payload; 64 tur FNV-1a 64-bit wrapping checksum; kriptografi veya ödeme hesabı değil |
| HTTP yolu | Loopback HTTP/1.1; her istekte yeni TCP bağlantısı; tek seri request handler; TLS yok |
| Sınırlar | Her server ayrı cgroup: 0,75 CPU çekirdeği kotası; 512 MiB MemoryMax |
| Runtime ayarı | Go: GOMAXPROCS=1, GOGC=100, GOMEMLIMIT=128MiB; Java: G1, Xms16m, Xmx128m, ActiveProcessorCount=1 |
| Trafik | 15 s × 100 request/s warm-up; 100/400/800 request/s için 15’er s; üç tekrar; uygulama sırası döndürülmüş |
| Load generator | Aynı hostta Python asyncio; server cgroup’u dışında; planlı açık çevrim geliş; en çok 128 aktif client |
| Latency sınırı | Planlı gelişten doğrulanmış yanıt ve bağlantı kapanmasına kadar; dispatch ve client kuyruk gecikmesi dahil |

Dağıtımın sağladığı toolchain sürümlerini kullandık; bunlar “2026’nın en güncel Rust/Go sürümleri” diye sunulmuyor. Üç server’ın hesap döngüsü aynı, ancak TCP/buffer/parser, allocator ve runtime uygulamaları farklı. Rust stdlib TCP, Go stdlib TCP ve Java ServerSocket kullandık; Axum, net/http, Spring veya production observability middleware’i yok. Bu farklar nedeniyle sonuç, dili tek başına izole eden bir deney değildir.

Build sütununda önce cache’leri ısıtan bir build, sonra çıktı dosyasını kaldırarak üç kaynak derlemesi var. Rust: rustc -C opt-level=3; Go: go build; Java: javac. Java sütunu bytecode üretimidir, JIT maliyeti çalışma sırasında oluşur. Dependency indirmesi, Cargo clean build, framework derlemesi ve CI kuyruğu yoktur. Tek dosyanın bu sonucu üzerinden büyük Rust workspace’inin build süresi tahmin edilemez.

Önemli sonuç: düşük idle RSS, otomatik olarak daha düşük HTTP p99 demek değil. Bu allocation-light, kısa koşuda latency değerleri birbirine yakın; client dispatch gecikmesi de ölçümün kayda değer bir parçası. Buradan “Go/Java GC nedeniyle sıçradı” veya “Rust düz çizgi verdi” sonucu çıkarmıyoruz. Ne doygunluk kapasitesi ne de saatler süren heap davranışı ölçüldü. GC etkisini incelemek için allocation/live-set profili, daha uzun soak, GC/JFR izleri ve ayrı host load generator gerekir.

Minimal Rust server’ın birkaç MiB ile başlaması, gerçek mikroservisinizin 10–15 MB’ta kalacağı anlamına gelmez. TLS, connection pool, cache, telemetry, veri kümesi ve framework bütçesi eklenir. Ham veride 100 ve 400 request/s koşulları, CPU-saniye ve throttling sayaçları da bulunuyor. Bu ölçümden sunucu sayısı azaltma yüzdesi türetmedik.

## 8. Küçük bir Rust çekirdeği: doğruluğu bellek güvenliğinden ayrı test etmek

Saf komisyon hesabı, shadow karşılaştırması için iyi bir ilk sınırdır. Aşağıdaki örnekte yalnız negatif olmayan minor-unit tutarlar ve 0–10.000 basis point kabul ediliyor; politika half-up yuvarlama. Bu bir evrensel finans standardı veya tüm para birimleri için fiyatlandırma kuralı değil, açıkça tanımlanmış örnek sözleşmedir. Vergi, kur, refund ve sağlayıcıya özgü davranış eklenirse yeni testler ve policy sürümü gerekir.

```rust
pub fn fee_minor(amount_minor: i64, bps: u32) -> Result<i64, &'static str> {
    if amount_minor < 0 || bps > 10_000 { return Err("invalid input"); }
    let product = i128::from(amount_minor) * i128::from(bps);
    let rounded = (product + 5_000) / 10_000; // Nonnegative half-up only.
    i64::try_from(rounded).map_err(|_| "overflow")
}

#[cfg(test)]
mod tests {
    use super::fee_minor;
    #[test] fn rounds_half_up() { assert_eq!(fee_minor(50, 100), Ok(1)); }
    #[test] fn normal_fee() { assert_eq!(fee_minor(125_050, 175), Ok(2_188)); }
    #[test] fn rejects_negative() { assert!(fee_minor(-1, 100).is_err()); }
    #[test] fn rejects_rate() { assert!(fee_minor(100, 10_001).is_err()); }
    #[test] fn preserves_upper_bound() {
        assert_eq!(fee_minor(i64::MAX, 10_000), Ok(i64::MAX));
    }
}
```

Bu çekirdek için testleri çalıştırdık; HTTP karşılaştırmasındaki checksum işi ise ayrı sentetik fixture’dır. İkisini aynı benchmark gibi göstermiyoruz. Safe Rust’ta fiyatın yanlış hesaplanması hâlâ mümkün olduğu için domain testleri, legacy corpus replay ve property-based invariant’lar bellek güvenliğinin yanına eklenir. Bu politika için fee ≥ 0 ve fee ≤ amount gibi kurallar, geçerli input aralığında kontrol edilebilir.

## 9. Öğrenme eğrisi, build süresi ve ekibin teslim kapasitesi

“İlk üç ay ekip verimsizdir” evrensel bir veri değil. Google’ın [2022 deneyimini raporlayan 2023 anketinde](https://opensource.googleblog.com/2023/06/rust-fact-vs-fiction-5-insights-from-googles-rust-journey-2022.html) katılımcıların yaklaşık üçte biri iki ay içinde, yarıdan fazlası dört ay içinde diğer dillerdeki kadar üretken hissettiğini bildirdi. Yavaş build başlıca şikâyetti. Bunlar Google’daki profesyonellerin öz değerlendirmeleri; sizin ekibiniz için süre garantisi veya objektif sprint verim ölçümü değil.

Ekip planında ownership/borrowing, error model, async ve review becerisi ayrı ele alınır. İlk servis için deneyimli bir reviewer ve dar bir domain seçmek, herkesi aynı anda tüm platformu yeniden yazmaya zorlamaktan daha ölçülebilir bir yatırımdır. Eğitim takvimi için tahmin yazabilirsiniz; kanıt olarak issue lead time, review çevrimi, escaped defect ve incident yükünün başlangıç seviyesine göre değişimini izleyin.

Borrow checker ile uğraşırken her yeri Arc<Mutex<_>> ile çevirmek veya gereksiz clone yapmak derlemeyi kolaylaştırıp tasarımı pahalılaştırabilir. Verinin sahibi kim, task ne kadar yaşıyor, hangi alan paylaşılmalı soruları tasarım aşamasında cevaplanır. “Derlendi, o hâlde doğrudur” yerine “hangi arıza sınıfları derleme öncesinde yakalandı?” diye sorun. Derleyici iş kuralınızın doğruluğunu bilmez.

Build bütçesini de ayrı ölçün: cargo check döngüsü, incremental test, release clean build ve CI cache miss farklı metriklerdir. Dev/prod profilleri, crate sınırları, proc macro kullanımı, linker ve cache politikası değerlendirilir. PGO/LTO gibi release ayarları throughput kazandırabilir ama build maliyeti ekler; hedef binary’nin gerçek iş yükünde etkisi ölçülmeden açık bırakılmaz. Birkaç saniyelik mikro build, günlük geliştirici bekleme toplamının yerine geçmez.

## 10. ROI hesabı: daha az RAM ne zaman gerçek tasarrufa dönüşür?

Bu VPS’in işletme tarafından bildirilen faturası 100 USD/ay. Aynı sunucu çalışmaya devam ediyorsa RSS’in düşmesi faturayı otomatik azaltmaz. Headroom, daha fazla tenant taşıma veya bir sonraki scale-up’ı geciktirme şeklinde değer üretebilir; ama gerçekleşmemiş tasarrufu nakit kazanç diye yazmayız. Kubernetes request azaltmak da node/instance sayısı veya faturalı kaynak gerçekten düşmüyorsa aynı ayrımı gerektirir.

```text
One-time migration investment M:
  implementation + training + parallel operation + validation + tooling

Monthly net benefit B:
  realized infrastructure savings
  + evidence-backed expected incident/risk reduction
  - added operating/maintenance cost

Simple payback (months) = M / B, only when B > 0
12-month simple ROI = (12 * B - M) / M

Do not count the same engineer-hours in both incident savings and staffing savings.
These are undiscounted planning formulas, not a prediction or financial valuation.
```

Örnek yatırım senaryosu: 240 mühendislik saati × 60 USD/saat + 1.600 USD geçiş/araç gideri = 16.000 USD. Varsayımsal 40 sunuculuk bir filo 100 USD/ay birim bedelle 4.000 USD/ay eder. Temsili yük testleri ve SLA kontrolünden sonra gerçekten kaldırılabilen yüzde 30 kapasite 1.200 USD/ay kazandırır; ek ikinci dil işletimi 200 USD/ay ise net 1.000 USD/ay kalır. Basit geri ödeme 16 ay, 12 aylık basit ROI −%25’tir. Bu filo TankDev’in mevcut altyapısı değildir; testimiz de yüzde 30 kapasite azalmasını kanıtlamaz.

Aynı 16.000 USD yatırım için hassasiyet analizi — tamamen varsayımsal

| Senaryo | Gerçekleşen brüt aylık kazanç | Ek aylık işletim | Net aylık kazanç | Basit geri ödeme |
| --- | --- | --- | --- | --- |
| Düşük kazanım | 400 USD | 200 USD | 200 USD | 80 ay |
| Orta varsayım | 1.200 USD | 200 USD | 1.000 USD | 16 ay |
| Yüksek kazanım | 1.800 USD | 250 USD | 1.550 USD | 10,32 ay |
| Fatura değişmiyor | 0 USD | 200 USD | −200 USD | Bu varsayımla yok |

Reliability değerini eklerken geçmiş incident frekansı × beklenen azalma × olay başına kayıp yaklaşımı kullanılabilir. Beklenen azalma ölçülmemişse aralık ve güven düzeyi açık yazılır. Özellikle C/C++ bellek güvenliği riski ile Java/Go GC optimizasyonu aynı tasarruf kalemi değildir. Finansal raporda güvenlik riskinin azalması ile bugünkü nakit fatura ayrı kalmalıdır. Mühendislik kararını güçlendiren şey büyük bir ROI yüzdesi değil, hangi varsayım değişince kararın döneceğini bilmektir.

## 11. Karar kaydı ve indirilebilir kanıtlar

```yaml
decision: extract one CPU-heavy evaluation boundary into Rust
status: proposed-until-production-gates-pass
alternatives:
  - tune existing runtime, queries, allocation and data structures
  - keep Go/Java and scale within current SLO
  - Rust sidecar/service for pure evaluation
  - full endpoint migration after contract and ownership validation
acceptance:
  - semantic parity on versioned replay corpus
  - bounded resources and representative peak/soak validation
  - measured p99 and error budget at comparable offered load
  - no duplicate financial side effects; reconciliation drill completed
  - realized savings exceed additional operations cost
rollback:
  - stop assigning NEW intents to Rust
  - preserve ownership and recovery of already accepted intents
retirement:
  - remove transitional routing only after retention/replay/support gates
```

[Ham benchmark JSON](/uploads/blog/rust-modernization-benchmark-20261105.json), [istek bazlı süreler](/uploads/blog/rust-modernization-request-samples-20261105.jsonl.gz) ve [kaynak/test paketi](/uploads/blog/rust-modernization-reference-20261105.zip) ölçümü denetlemek içindir. [Markdown makale](/uploads/blog/rust-modernization-guide.tr.md) tabloları korur; [Mermaid kaynağı](/uploads/blog/rust-modernization-architecture.mmd) mimariyi dokümantasyona taşımak içindir. Referans HTTP parser’ları yalnız kontrollü loopback fixture’ına yöneliktir; internete açık production server olarak kullanılmaz.

TankDev’de [operasyonel sistem modernizasyonunu](/tr/services/operational-modernization) dil değişikliğiyle sınırlamıyoruz. Profil, veri sahipliği, geçiş sınırı, sözleşme testi, canary ve maliyet kabulü aynı işin parçalarıdır. [PoC’den production’a mimari rehberimiz](/tr/blog/pocden-productiona-olceklenebilir-mimari-guvenilirlik-maliyet) kapasite ve SLO tarafını tamamlar. Mevcut sisteminizde ölçülmüş bir sıcak yol varsa [birlikte inceleyebiliriz](/tr/contact); sonuç Rust’a geçmek de, mevcut Go/Java servisini doğru şekilde iyileştirmek de olabilir.

## Sık Sorulan Sorular

### Rust bellek sızıntılarını tamamen engeller mi?
Hayır. Referans döngüleri, bilinçli leak, sınırsız cache ve kaynak tutulması mümkündür. Safe Rust bellek bozulması sınıflarına güçlü koruma sağlar; RAM/admission sınırları ve soak testleri yine gerekir.

### Garbage collector olmaması p99 latency garantisi mi?
Hayır. Allocator, destructor, kilit, page fault, kernel scheduling, DB/ağ beklemesi ve CPU throttling kuyruk gecikmesi yaratabilir. GC etkisi runtime izleriyle ayrıştırılmalıdır.

### Go veya Java’dan Rust’a geçiş her zaman daha güvenli mi?
Bu dillerin normal güvenli kodu zaten memory-safe bir model sunar. Rust’a geçiş gerekçesi allocation, CPU, temsil maliyeti veya belirli native sınırlar olabilir. Yetki, iş kuralı ve distributed tutarlılık dil değişikliğiyle çözülmez.

### Rust mikroservisi gerçekten 10–15 MB RAM kullanır mı?
Evrensel bir sınır yoktur. Bu yazının küçük stdlib fixture’ı ile TLS, cache, pool ve telemetry taşıyan production servis aynı değildir. RSS, heap ve cgroup belleği de farklı metriklerdir.

### Strangler Fig ile ilk hangi parça taşınmalı?
Profilde ölçülmüş, domain sınırı dar ve doğruluk corpus’u oluşturulabilen parça seçilir. Ödeme akışında saf risk/komisyon hesabı, doğrudan tahsilatı iki ayrı writer’a açmaktan daha kontrollü ilk adım olabilir.

### Canary sırasında aynı ödeme iki kez yapılabilir mi?
Kör trafik bölme ve retry ile risk oluşur. Kararlı intent, pinlenmiş owner, ortak kabul/idempotency sözleşmesi, domain uniqueness ve sağlayıcı dedup desteği gerekir. Route registry tek başına exactly-once sağlamaz.

### Rust deploy’unu geri almak tahsilatı geri alır mı?
Hayır. Rollback yeni iş atamasını değiştirebilir; kabul edilmiş iş ve dış etki kalır. Durum sorgulama, reconciliation ve gerekiyorsa ayrı refund/void akışı uygulanır.

### Rust öğrenmek kesin 2–3 ay sürer mi?
Hayır. Deneyim, domain, mentorluk ve async/FFI ihtiyacı süreyi değiştirir. Yazıda kullanılan Google anketi öz değerlendirmedir; tüm ekipler için teslim takvimi garantisi değildir.

### Derleniyorsa doğru çalışır denebilir mi?
Hayır. Derleyici bazı bellek/tip ve data race hatalarını dışlar; yanlış fiyat, eksik yetki, distributed race, panic veya deadlock hâlâ mümkündür. Domain ve sistem testleri gerekir.

### Bu kıyaslama Rust’ın her yükte daha hızlı olduğunu kanıtlıyor mu?
Hayır. Tek hostta küçük allocation-light HTTP fixture’ları, seçilmiş toolchain ve ayarlar ölçüldü. Parser/runtime farkları ve client gecikmesi var; production kapasitesi veya GC kaynaklı tail sıçraması kanıtlanmadı.

### Rust modernizasyonunun ROI’si nasıl hesaplanır?
Tek seferlik yatırım, gerçekleşen aylık tasarruf ve ek işletim maliyeti birlikte hesaplanır. Net aylık kazanım pozitifse basit geri ödeme yatırım/net kazanımdır. Headroom veya risk azalması gerçekleşmiş nakit tasarrufla karıştırılmaz.

### TankDev mevcut servisi tamamen yeniden yazmadan modernize edebilir mi?
Desteklenen erişim ve sistem sözleşmeleri kapsamında profil çıkarma, dar sınır seçimi, Strangler Fig, canary ve recovery planını tasarlayıp geliştirebiliriz. Dil seçimini ölçülen darboğaz, ekip ve işletim maliyeti belirler.