Programlama

Rust Binaries Neden Şişer? 40 MB’dan 400 KB’a Yolculuk

“Rust kullanıyorum, nasıl olsa hafif olur” diye düşünüyorsanız… durun bir dakika. Ben de bir ara aynı hataya düştüm. Geçen yıl, 2024’ün Kasım ayında İstanbul’daki küçük bir SaaS projesinde, tek işi JSON alıp HTTP isteği atmak olan minicik bir CLI aracı yazarken, derlenmiş binary’nin 38-40 MB bandına vurduğunu görünce resmen kaşlarım kalktı. Kâğıt üstünde sade, pratikte ise şişkin bir paket çıkmıştı ortaya.

İşin can sıkıcı kısmı şu: Rust’ta “zero-cost abstraction” lafı kulağa çok hoş geliyor. Her zaman “sıfır boyut” anlamına gelmiyor. En çok da CLI — kendi adıma konuşayım — araçlarında, gereksiz bağımlılıklar ve yanlış varsayımlar yüzünden binary boyutu sessizce kabarıyor. E peki, sonuç ne oldu? Yani mesele dilin kendisi değil; bizim rahatımız için eklediğimiz katmanlar.

Şişkinliğin Kaynağı: Asıl Suçlu Kim?

İşin garibi, Benim ilk kurulumum baya klasikmiş: serde, reqwest, tokio, üstüne biraz da TLS desteği… Hani “modern olsun” diye ne varsa koymuşum (şaşırtıcı ama gerçek). Sonra bir bakıyorsunuz, üç satırlık iş yapan uygulama koskoca mini servis gibi davranıyor. Açık konuşayım — bu noktada suçlu tek bir crate değil; zincirleme şekilde gelen transitive bağımlılıklar.

Bi saniye — Geçenlerde 2025 Mart’ında Ankara’da çalışan bir ekip arkadaşımın test ortamında benzerini gördüm: sadece dosya çekip loglayan araç 12 MB sanılırken 31 MB çıktı. Neden? Çünkü birkaç “kolaylık” özelliği uğruna yarım ekosistem içeri alınmıştı (evet, doğru duydunuz). Mesela HTTP istemcisi yalnızca istek atmak için değil, redirect yönetmekten cookie’ye kadar onlarca şeyle birlikte geliyor — kullanmayacağınız özelliklerin bedelini ödüyorsunuz, ne yazık ki.

Bilmem anlatabiliyor muyum, Dur bir saniye, önce şunu söyleyeyim: sorun yalnızca disk alanı değil. Binary büyüdükçe soğuk başlangıç süresi de uzuyor, Docker imajı kabarıyor, dağıtım yavaşlıyor. CI/CD hattında her build biraz daha can sıkıcı oluyor (ben de ilk duyduğumda şaşırmıştım). Küçük bir startup için bu belki “eh işte” denecek kadar önemsiz görünebilir… ama enterprise tarafta aynı durum maliyet olarak geri dönüyor. Kaçınılmaz biçimde.

Durun, bir saniye.

💡 Bilgi: Binary boyutunu küçültmek sadece depolama alanını azaltmaz; container açılış süresini kısaltır, ağ üzerinden image çekimini hızlandırır ve edge senaryolarda ciddi fark yaratır.

Nereden Başlamalı? Ölçmeden İlerlemek Yok

Lafı gevelemeden söyleyeyim: ölçüm yoksa optimize etme da yoktur. Ben bu tip işlerde önce cargo bloat, sonra size, ardından da startup süresi ölçümü yapıyorum. Çünkü bazen binary’nin içinde gerçekten büyük gözüken şey kod değildir; semboller, debug bilgisi ya da yanlış derleme ayarlarıdır. Tamamen farklı bir sorun.

Bak şimdi, Küçük bir not düşeyim: 2023’te İzmir’de yaptığım serbest çalışma projesinde sadece [profile.release] ayarlarını değiştirerek bile yaklaşık %18 kazanım almıştık. Yani optimize etmeye (belki yanılıyorum ama) başlamadan önce build profilinizi kontrol edin; bazen beklediğiniz kadar dramatik olmayan ama yine de işe yarayan kazanımlar oradan geliyor. Şaşırdım açıkçası o zaman.

Şimdi gelelim işin can alıcı noktasına. Bu konuyla ilgili Roblox’ta Yaş Doğrulama: 9 Yaş Sınırı Neyi Değiştiriyor? yazımıza da göz atmanızı tavsiye ederim.

Araç / Ayar Ne İşe Yarar? Bana Göre Artısı Zayıf Tarafı
cargo bloat Kodu hangi crate’in şişirdiğini gösterir Nokta atışı analiz verir Bazen tabloyu yorumlamak gerekiyor
-C opt-level=z Büyüklük odaklı derleme yapar Küçülme hissedilir olur Bazı durumlarda performans biraz oynar
-C strip=symbols Sembolleri temizler Anında boyut düşürür Daha zor debug edilir
LTO / ThinLTO Daha agresif link-time iyileştirmeu yapar Bazı projelerde ciddi fark yaratır Build süresi uzayabilir!

Kaba Kuvvet Değil, Cerrahi Müdahale Gerekir

Benim asıl kırılma noktam reqwest’i sorgulamak oldu. Evet, harika paket; evet, üretimde rahat ettiriyor… ama benim kullanım senaryom tek endpoint’e JSON POST atmaktı. Bunun için koca HTTP evrenini taşımaya gerek yoktu. Siz hiç denediniz mi? O yüzden çıplak TcpStream ile minik bir istek yazdım.

use std::io::{Read, Write};
use std::net::TcpStream;
fn http_post(host: &str, path: &str, body: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut stream = TcpStream::connect(format!("{}:443", host))?;
let request = format!(
"POST {} HTTP/1.1\r
Host: {}\r
Content-Length: {}\r
\r
{}",
path,
host,
body.len(),
body
);
stream.write_all(request.as_bytes())?;
let mut response = String::new();
stream.read_to_string(&mut response)?;
Ok(response)
}

E tabi burada önemli olan şu: bu yaklaşım herkese göre değil. Redirect takibi istiyorsanız, proxy kullanıyorsanız ya da sertifika doğrulama tarafında detaylara giriyorsanız çıplak soket çözümü çabuk ham kalıyor. Güzel tarafı basitlik. Kötü tarafı ise bazı güvenlik ve dayanıklılık konularını sizin halletmeniz gerekmesi — ikisi de gerçek.

Araya gireyim: Bana göre en doğru yaklaşım “her şeyi silmek” değil… ihtiyaç olmayan yükü kesmek. Bir başka deyişle marangozluk gibi düşünün; testereyi bütün dolabı yıkmak için değil, fazla tahtayı almak için kullanıyorsunuz.

Tatlı Nokta Nerede?

Küçük startup senaryosunda hızlı iterasyon önemlidir; orada biraz büyük binary kabul edilebilir çünkü mühendis zamanı pahalıdır. Ürün erken aşamadadır. Ama kenarda çalışan agent benzeri araçlarda ya da edge node üzerinde koşan hizmetlerde her kilobayt konuşur. neden konusundaki yazımız yazımızda bu konuya da değinmiştik.

Bence, Enterprise seviyede ise konu daha sertleşiyor çünkü image pull süreleri yüzlerce pod’da çarpan etkisi yaratıyor. Bir yerde 10 saniye kaybettiğinizde aslında bin sunucuda saatleri yakabiliyorsunuz… işin aslı bu kadar basit ve acımasız.

Binary küçültme işi “gösteriş” değil; dağıtımı hızlandıran, operasyon maliyetini azaltan ve soğuk başlangıçları toparlayan gerçek bir iyileştirme alanı.

Sadece Kod Değil, Derleme Ayarı da İşin İçinde

Kağıt üstünde süper görünen birçok proje aslında release profilinde gereksiz sembollerle dolaşıyor. Bu yüzden build ayarlarına eğilmeden önce sonuç beklentisini doğru kurmak lazım. Tek başına crate değiştirmek çoğu zaman yetmiyor; bazen kazanç asıl orada başlıyor. Roblox’ta Yaş Kilidi: Yeni Hesap Katmanları Ne Getiriyor? yazımızda bu konuya da değinmiştik.

Bilhassa şu ayarlar baya işe yarıyor:

  • Maksimum boyut odaklı derleme için -C opt-level=z
  • Sembol temizliği için -C strip=symbols
  • Daha akıllı linkleme için ThinLTO veya LTO denemesi
  • Kullanmadığınız feature flag’lerini kapatma
  • TLS gibi ağır bağımlılıkları gerçekten gerekli mi diye sorgulama
  • Mümkünse async runtime yerine sade senkron akış düşünme
  • Kendi ihtiyacına uygun minimal protokol katmanı yazma

Neyse uzatmayalım… burada kritik nokta şu ki her iyileştirme aynı projede aynı etkiyi vermiyor. Benim kendi testimde en büyük fark dependency sayısını azaltınca geldi; ikincisi ise release ayarlarını sıkıştırınca görüldü. 15 Noctua Fanla Gelen Garip Ama İşe Yarayan Soğutma: Superdome yazımızda da bu konuya değinmiştik. MacBook Neo’nun Sıradaki Sürprizi: En Büyük Sıçrama yazımızda da bu konuya değinmiştik.

Peki Gerçek Hayatta Ne Kazandırıyor?

Ne yalan söyleyeyim, Dürüst olayım, herkesin hedefi “400 KB’a inmek” olmayabilir ve zaten olmak zorunda da değil. Bazen amaç sadece makul seviyeye dönmektir; yani Docker image büyümesin, cold start makul olsun ve deploy sırasında gereksiz veri taşınmasın yeterli.

Dikkatimi çeken şeylerden biri de şu oldu: küçülen binary’nin yan etkisi yalnızca performans değildi. Bakım kolaylaştı. Çünkü bağımlılık azalınca saldırı yüzeyi de daraldı — bu özellikle güvenlik hassasiyeti olan sistemlerde ciddi artıdır (şaşırtıcı ama gerçek)

Aynı Soruna İki Farklı Bakış

Senaryo Ağır Stack Minimal Stack
Küçük CLI tool Kolay ama şişkin Sıkı ve hızlı
Kubernetes sidecar Büyük image çekmek acıtır Daha az overhead ekler
Ciddi güvenlik ihtiyacı Daha fazla bağımlılık daha fazla risk getirir Daha dar attack surface verir

Başka bir örnek vereyim: geçen ay Şubat 2026’da İstanbul Finans Merkezi’nde çalışan tanışığım Ahmet Bey’in takımında benzer mantıkla çalışan iki araç vardı; biri tam teşekküllü HTTP client kullanıyordu, diğeriyse hafif protokolle yazılmıştı. Hafif olanın sadece diskte küçülmesi yetmedi… rollout sonrası pod başlatma süreleri de gözle görülür biçimde düştü. Beklenenden fazla fark çıktı.

Benden Kalan Pratik Notlar

Bir proje ne kadar sade görünürse görünsün, kompozisyonu önemli. Yani dependency sayısı azalsın diye körlemesine sökmek de çözüm değil (buna dikkat edin). Şunları akılda tutuyorum:

  • Eğer hedefiniz tek endpoint ise genel amaçlı HTTP istemcisine hemen sarılmayın.
  • Kullandığınız crate’in default feature’larını genelde kontrol edin.
  • LTO ile strip kombinasyonunu farklı ortamlarda test edin.
  • Cargo tree ile transitive bağımlılık zincirine bakın.
  • panic = abort seçeneğini ancak hata stratejiniz uygunsa değerlendirin.
# Örnek release profili
[profile.release]
opt-level = "z"
lto = true
strip = "symbols"
panic = "abort"

Birçok kişi iyileştirme deyince sadece benchmark düşünüyor ama deployment deneyimi bambaşka (buna dikkat edin). CI pipeline süreleri, registry download trafiği, node üzerindeki memory baskısı… hepsi topluca etkileniyor. Geniş resme bakmak lazım.

Gel gelelim beklediğim kadar pürüzsüz olmayan taraflar da var. Çok minimal çözümler bakımda zorlayabiliyor. Takımdaki yeni biri beş dakika içinde anlayamıyorsa o çözüm biraz fazla zorlu demektir — bu da gerçek bir maliyet, görmezden gelmek olmaz.

Sıkça Sorulan Sorular
Rust binary neden bu kadar büyüyor?

Birden fazla ağır crate kullandığınızda transitive bağımlılıklar hızla büyür.

Binary boyutunu en hızlı nasıl düşürürüm?
Önce release profilini sıkıştırın sonra gereksiz feature flag’lerini kapatın.

Çok küçük binary üretmek performansı bozar mı?
Bazen evet,bazen hayır.Kullandığınız optimizasyon türüne göre değişir.

Her projede raw socket yazmalı mıyım?
Hayır.Sadece gerçekten çok basit ihtiyaçlarda mantıklı olur.

Bu yaklaşım production için güvenilir mi?
Doğru test edilirse evet ama TLS,güvenlik ve hata yönetimi tarafını hafife almamak gerekir.

Kaynaklar ve İleri Okuma
Rust Cargo Profiles Dokümantasyonu
cargo-bloat GitHub Sayfası
rustc Codegen Options – strip

Aşkın KILIÇ

20+ yıl deneyimli Azure Solutions Architect. Microsoft sertifikalı bulut mimari ve DevOps danışmanı. Azure, yapay zekâ ve bulut teknolojileri üzerine Türkçe teknik içerikler üretiyor.

AZ-305AZ-104AZ-500AZ-400DP-203AI-102

Bu içerik işinize yaradı mı?

Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.

Haftalık Bülten

Her pazar özenle seçilmiş teknoloji yazıları doğrudan e-postanıza gelsin.

← Onceki Yazi
Claude Code ve Cursor Neden Unutur? AMFS ile Çözüm
Sonraki Yazi →
Samsung’un Bellek Hamlesi: AI Dalgasında Sessiz Devlik

Yorum Yaz

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Haftalık Bülten

Azure, DevOps ve Yapay Zeka dünyasındaki en güncel içerikleri her hafta doğrudan e-postanıza alın.

Spam yok. İstediğiniz zaman iptal edebilirsiniz.
📱
Uygulamayı Yükle Ana ekrana ekle, çevrimdışı oku
Kategoriler
Ara
Paylaş
İçindekiler
← Claude Code ve Cursor Neden Un...
Samsung’un Bellek Hamlesi: AI ... →
📩

Gitmeden önce!

Her pazar özenle seçilmiş teknoloji yazıları ve AI haberleri doğrudan e-postanıza gelsin. Ücretsiz, spam yok.

🔒 Bilgileriniz güvende. İstediğiniz zaman ayrılabilirsiniz.

📬 Haftalık bülten: Teknoloji + AI haberleri