
İster veri mühendisi, ister veri bilimci, makine öğrenmesi mühendisi veya veri analisti olun, yazdığınız kodun ve geliştirdiğiniz uygulamaların sağlam, ölçeklenebilir ve sürdürülebilir olması kritik öneme sahiptir. 2011 yılında Heroku’daki geliştiriciler tarafından ortaya atılan 12-Factor App (12 Faktörlü Uygulama) metodolojisi, tam da bu hedeflere ulaşmak için bir dizi ilke sunar.1 Bu metodoloji, web uygulamaları için tasarlanmış olsa da, temel prensipleri günümüzün veri odaklı projeleri için de son derece geçerli ve yol göstericidir.
Bu yazıda, 12-Factor App metodolojisinin her bir ilkesini, veri profesyonellerinin (veri mühendisleri, veri bilimcileri, makine öğrenmesi mühendisleri, yapay zeka mühendisleri ve veri analistleri) karşılaştığı zorluklar ve ihtiyaçlar doğrultusunda mercek altına alacağız. Bu ilkelerin, veri hatlarınızı (data pipeline), makine öğrenmesi modellerinizi ve analiz uygulamalarınızı nasıl daha sağlam, esnek ve yönetilebilir hale getirebileceğini pratik örneklerle inceleyeceğiz.
1. Kod Tabanı (Codebase): Tek Bir Kod, Birçok Dağıtım
Her uygulamanın, versiyon kontrol sistemi (örneğin Git) ile takip edilen tek bir kod tabanı olmalıdır.2 Bu kod tabanından geliştirme (development), test (staging) ve canlı (production) gibi farklı ortamlar için çok sayıda dağıtım (deployment) yapılabilir.
Veri Profesyonelleri İçin Anlamı:
- Veri Hatları (Data Pipelines): Bir ETL/ELT süreci için yazdığınız tüm Python, SQL veya Scala kodları tek bir Git reposunda tutulmalıdır. Bu reponun farklı dalları (branch) ise geliştirme ve canlı ortamları için kullanılabilir. Böylece, geliştirdiğiniz yeni bir özellik (örneğin, yeni bir veri kaynağının eklenmesi) önce geliştirme ortamında test edilir, ardından ana dala (main/master) birleştirilerek canlı ortama dağıtılır.
- Makine Öğrenmesi Modelleri: Bir makine öğrenmesi modelinin eğitimi, değerlendirilmesi ve sunulması için gerekli tüm kodlar (veri ön işleme, özellik mühendisliği, model eğitimi script’leri, API kodları vb.) tek bir kod tabanında yer almalıdır. Bu, modelin farklı versiyonlarını (örneğin, farklı hiperparametrelerle eğitilmiş modeller) yönetmeyi ve dağıtmayı kolaylaştırır. Farklı projeler için farklı repolar oluşturulmalıdır; tek bir repoda birden fazla alakasız model veya veri hattı bulunması bu ilkenin ihlalidir.
2. Bağımlılıklar (Dependencies): Bağımlılıkları Açıkça Belirt ve İzolasyonunu Sağla
Bir 12 faktörlü uygulama, sistem genelinde kurulu olan paketlere asla örtülü olarak güvenmez. Projenin ihtiyaç duyduğu tüm bağımlılıklar (kütüphaneler, framework’ler vb.) bir manifesto dosyası ile (örneğin, Python için requirements.txt
veya pyproject.toml
) açıkça belirtilmeli ve bir izolasyon aracı (örneğin, venv
, conda
veya Docker) ile projenin geri kalanından izole edilmelidir.
Veri Profesyonelleri İçin Anlamı:
Bu ilke, veri projelerinde “benim makinemde çalışıyordu” sendromunu ortadan kaldırmak için hayati önem taşır.
- Örnek: Bir veri analizi projesi geliştirdiğinizi düşünün. Projenizde
pandas
,scikit-learn
vematplotlib
kütüphanelerinin belirli versiyonlarını kullanıyorsunuz. Bu bağımlılıkları ve versiyon numaralarınırequirements.txt
dosyasına eklemelisiniz. (Python versiyonunu da belirmeyi unutmayının. Zira paket sürümleri python sürümleriyle bağımlı. Bazı paket sürümleri bazı python sürümlerinde desteklenmiyor, bu dapip install -r requirements.txt
komutunun hata almasına sebep oluyor). Ekibe yeni katılan bir veri bilimci, projeyi kendi bilgisayarına kurarken sadecepip install -r requirements.txt
komutunu çalıştırarak gerekli tüm paketleri doğru versiyonlarıyla kurabilir. Bu, hem kurulum sürecini basitleştirir hem de farklı geliştirici ortamlarından kaynaklanabilecek hataları önler. Özellikle veri bilimi projelerinde kütüphane versiyonları arasındaki küçük farklar bile model sonuçlarını önemli ölçüde etkileyebilir.
3. Konfigürasyon (Config): Konfigürasyonu Ortamda (Environment) Sakla
Uygulamanın konfigürasyonu (veritabanı bağlantı bilgileri, API anahtarları, dış servislerin adresleri gibi) kodun içinde sabit olarak bulunmamalıdır. Konfigürasyon, dağıtıldığı ortama özeldir ve ortam değişkenleri (environment variables) olarak saklanmalıdır.
Veri Profesyonelleri İçin Anlamı:
- Güvenlik: Bir veri mühendisi olarak, Airflow veya dbt gibi bir araçla veri ambarına (data warehouse) bağlanmanız gerekir. Veri ambarı kullanıcı adı ve şifresini doğrudan Python kodunuza yazmak büyük bir güvenlik açığıdır. Bu bilgileri ortam değişkenleri olarak ayarlayıp kodunuzdan bu değişkenlere referans vermelisiniz. Böylece, kodunuzu açık kaynaklı hale getirseniz bile hassas bilgileriniz güvende kalır.
- Esneklik: Geliştirme ortamında lokal bir PostgreSQL veritabanı kullanırken, canlı ortamda Amazon RDS veya Google Cloud SQL gibi yönetilen bir veritabanı hizmeti kullanabilirsiniz. Bu iki ortam arasındaki tek fark, ortam değişkenleriyle tanımlanan veritabanı bağlantı dizesi (connection string) olmalıdır. Kodda herhangi bir değişiklik yapmanıza gerek kalmaz.
4. Destekleyici Servisler (Backing Services): Destekleyici Servisleri İliştirilmiş Kaynaklar Olarak Gör
Veritabanları, mesaj kuyrukları (RabbitMQ, Kafka), cache sistemleri (Redis) veya e-posta servisleri gibi ağ üzerinden erişilen tüm servisler, iliştirilmiş kaynaklar olarak ele alınmalıdır. Uygulama, bu servislerin yerel mi yoksa üçüncü parti mi olduğunu ayırt etmemelidir. Dağıtım, kodda hiçbir değişiklik yapmadan bir veritabanını diğeriyle değiştirebilmelidir.
Veri Profesyonelleri İçin Anlamı:
- Makine Öğrenmesi Mühendisliği: Bir ML modelini canlıya aldığınızda, modelin özellik verilerini (feature data) almak için bir Redis veritabanı kullanabilirsiniz. Eğer Redis’te bir sorun yaşanırsa veya daha performanslı bir çözüme (örneğin, Amazon ElastiCache) geçmek isterseniz, bu değişikliği sadece konfigürasyon (ortam değişkenleri) üzerinden yapabilmelisiniz. Model sunumunu yapan API kodunuzda hiçbir değişiklik gerekmemelidir.
- Veri Mühendisliği: Veri hattınız, ham verileri bir S3 bucket’ından okuyor olabilir. Test ortamında, bu bucket yerel bir MinIO servisi olabilirken, canlıda gerçek bir AWS S3 bucket’ı olabilir.3 Kodunuz bu iki kaynağı da aynı şekilde, birer “destekleyici servis” olarak görmelidir.
5. İnşa Et, Yayınla, Çalıştır (Build, Release, Run): İnşa ve Çalıştırma Aşamalarını Kesinlikle Ayır
Bir uygulamanın dağıtımı üç ayrı aşamadan oluşur:
- İnşa (Build): Kod tabanını alır ve bağımlılıklarıyla birlikte çalıştırılabilir bir pakete (build) dönüştürür.
- Yayınla (Release): İnşa edilen paketi alır ve o dağıtıma özel konfigürasyonla birleştirir.
- Çalıştır (Run): Uygulamayı çalışma ortamında koşturur.
Bu aşamalar arasında katı bir ayrım olmalıdır. Örneğin, çalışma zamanında (runtime) kodda değişiklik yapmak imkansız olmalıdır.
Veri Profesyonelleri İçin Anlamı:
- MLOps: Bir makine öğrenmesi modelini dağıtırken bu ayrım çok önemlidir.4
- İnşa (Build) Aşaması: Modelinizi eğiten ve bir
model.pkl
dosyası oluşturan script’i çalıştırıp, bu artifaktı model sunumu yapacak Flask/FastAPI uygulamasıyla birlikte bir Docker imajı olarak paketlersiniz. - Yayınla (Release) Aşaması: Bu Docker imajını, canlı ortamının konfigürasyonlarıyla (örneğin, veritabanı bağlantısı, log seviyesi) birleştirerek bir “yayın” oluşturursunuz.
- Çalıştır (Run) Aşaması: Bu yayını Kubernetes gibi bir orkestrasyon platformunda çalıştırırsınız. Bu sayede, aynı “inşa” (Docker imajı) farklı konfigürasyonlarla test ve canlı ortamlarında çalıştırılabilir.
- İnşa (Build) Aşaması: Modelinizi eğiten ve bir
6. Süreçler (Processes): Uygulamayı Bir veya Daha Fazla Durumsuz (Stateless) Süreç Olarak Yürüt
12 faktörlü uygulama, durumsuz (stateless) ve paylaşımsız (shared-nothing) süreçler olarak çalışır. Kalıcı olması gereken her türlü veri, veritabanı gibi durumlu (stateful) bir destekleyici serviste saklanmalıdır.
Veri Profesyonelleri İçin Anlamı:
- Ölçeklenebilirlik: Bir veri işleme (data processing) uygulamanız olduğunu düşünün. Eğer bu uygulama işlediği verileri veya ara sonuçları kendi belleğinde veya yerel diskinde tutarsa, bu uygulamayı yatay olarak ölçeklendirmek (yani birden fazla kopyasını çalıştırmak) imkansız hale gelir. Çünkü her kopya kendi durumuna sahip olur ve veriler tutarsızlaşır. Bunun yerine, tüm ara ve nihai sonuçlar bir veritabanına, data lake’e veya bir mesaj kuyruğuna yazılmalıdır. Böylece uygulamanızın onlarca kopyasını aynı anda çalıştırarak iş yükünü dağıtabilirsiniz.
- ML Model Sunumu: Bir API üzerinden tahmin yapan bir ML modeli de durumsuz olmalıdır. Gelen her tahmin isteği, önceki isteklerden bağımsız olarak ele alınmalıdır. Kullanıcı oturumları veya geçmiş isteklerle ilgili bilgiler, modelin kendisinde değil, Redis gibi harici bir cache sisteminde tutulmalıdır.
7. Port Bağlama (Port Binding): Servisleri Port Bağlama Yoluyla Dışa Aktar
Web uygulamaları bazen bir web sunucusunun (örneğin, Apache) içine enjekte edilerek çalıştırılır.5 12 faktörlü bir uygulama ise tamamen kendi kendine yeten bir yapıdadır ve bir web sunucusunun çalışma zamanı enjeksiyonuna dayanmaz. Uygulama, bir porta bağlanarak istekleri dinler ve hizmetlerini bu yolla dışa aktarır.
Veri Profesyonelleri İçin Anlamı:
- API’ler: Geliştirdiğiniz bir makine öğrenmesi modelini veya bir veri analiz sonucunu bir REST API aracılığıyla sunuyorsanız, bu API’yi (örneğin FastAPI) kendi içinde bir web sunucusu çalıştıracak şekilde paketlemelisiniz (genellikle Gunicorn veya uvicorn gibi araçlarla). Bu API, ortam değişkeniyle belirlenen bir porta (örneğin, 8000) bağlanmalıdır. Bu, uygulamanızın taşınabilirliğini artırır ve Docker gibi konteyner teknolojileriyle mükemmel uyum sağlar.
8. Eşzamanlılık (Concurrency): Süreç Modeliyle Yatay Ölçeklen
Her süreç birinci sınıf bir vatandaştır. Uygulamanızı, farklı iş yüklerini farklı süreç türleri ile ele alacak şekilde tasarlayın (örneğin, web isteklerini işleyen web
süreci, uzun süren veri işleme görevlerini yapan worker
süreci). Bu süreçler yatay olarak, yani daha fazla süreç ekleyerek ölçeklenmelidir.
Veri Profesyonelleri İçin Anlamı:
- Veri Hatları: Karmaşık bir veri hattında, farklı görevler için farklı
worker
‘lar tanımlayabilirsiniz. Örneğin, birworker
grubu veri çekme (ingestion) işini yaparken, başka birworker
grubu veriyi dönüştürme (transformation) işini yapabilir. Veri hacmi arttığında, kodunuzu değiştirmeden sadeceworker
süreçlerinin sayısını artırarak sistemin kapasitesini artırabilirsiniz. Airflow veya dbt gibi araçlar bu prensip üzerine kuruludur. - Asenkron Görevler: Bir veri bilimci, kullanıcıdan gelen bir istekle tetiklenen ve 30 dakika süren bir model eğitimi süreci tasarlayabilir. Bu görevi doğrudan bir web isteği içinde çalıştırmak yerine, bir mesaj kuyruğuna (örneğin, Celery ile RabbitMQ) atıp, bu kuyruğu dinleyen ayrı bir
worker
sürecine yaptırmak doğru yaklaşımdır. Bu sayede web süreci meşgul edilmez ve kullanıcı anında yanıt alır.
9. Atılabilirlik (Disposability): Hızlı Başlatma ve Düzgün Kapatma ile Dayanıklılığı En Üst Düzeye Çıkar
Uygulamanın süreçleri atılabilir (disposable) olmalıdır, yani anında başlatılabilir ve durdurulabilirler. Bu, elastik ölçeklendirme, hızlı dağıtım ve dayanıklılık (robustness) için esastır. Süreçler hızlı başlamalı ve kapatıldıklarında (örneğin, bir sinyal aldıklarında) mevcut işlerini bitirip düzgün bir şekilde sonlanmalıdır (graceful shutdown).
Veri Profesyonelleri İçin Anlamı:
- Hızlı Başlatma: Bir
worker
süreci, bir veri işleme görevini devraldığında, veritabanı veya diğer servislere olan bağlantılarını hızla kurabilmelidir. - Düzgün Kapatma: Bir veri işleme
worker
‘ı kapatılma sinyali aldığında, o an işlediği veri yığınını (batch) bitirmeli, durumu bir veritabanına kaydetmeli ve sonra kapanmalıdır. Eğer aniden ölürse, yarım kalan işin başka birworker
tarafından devralınabilmesi gerekir. Bu, veri kaybını ve tutarsızlığı önler.
10. Geliştirme/Canlı Benzerliği (Dev/Prod Parity): Geliştirme, Test ve Canlı Ortamlarını Mümkün Olduğunca Benzer Tut
Geliştirme (development), test (staging) ve canlı (production) ortamları arasındaki farkları en aza indirin. Bu, “benim makinemde çalışıyordu, neden canlıda patladı?” sorununu önlemenin anahtarıdır.
Veri Profesyonelleri İçin Anlamı:
- Zaman Farkı: Geliştirdiğiniz bir kodun canlıya çıkması haftalar sürerse, geliştirme ve canlı ortamları arasındaki fark açılır. 12 faktörlü uygulama, sürekli entegrasyon ve sürekli dağıtım (CI/CD) pratiklerini teşvik ederek bu süreyi saatlere indirmeyi hedefler.
- Araç Farkı: Geliştirme ortamında SQLite kullanıp, canlıda PostgreSQL kullanmak bu ilkenin ihlalidir. Veri türleri, fonksiyonlar ve performans karakteristikleri arasındaki farklar beklenmedik hatalara yol açabilir. Docker gibi araçlar sayesinde, canlıda kullandığınız veritabanının (örneğin, PostgreSQL 14) aynısını geliştirme ortamınızda kolayca çalıştırabilirsiniz.6
- Veri Farkı: Bir veri bilimci, modelini geliştirirken genellikle küçük veya örneklenmiş bir veri seti kullanır. Canlı ortamda ise model, gerçek ve çok daha büyük bir veriyle karşılaşır. Geliştirme ve canlı ortamlarındaki veri dağılımlarının ve özelliklerinin mümkün olduğunca benzer olması (tabii ki gizlilik kurallarına uyarak) kritik öneme sahiptir.
11. Günlükler (Logs): Günlükleri Olay Akışları Olarak Ele Al
Bir 12 faktörlü uygulama, kendi günlük dosyalarını (log files) yazmak veya yönetmekle uğraşmaz. Bunun yerine, her çalışan süreç, olay akışını (event stream) arabelleğe almadan stdout
‘a (standard output – standart çıktı) yazar.
Veri Profesyonelleri İçin Anlamı:
Uygulamanızın (veri hattı, ML API vb.) logları doğrudan bir dosyaya yazmaması gerekir. Logları standart çıktıya yazdığında, çalıştırıldığı ortam (örneğin Docker, Kubernetes) bu akışı yakalayıp merkezi bir log toplama sistemine (örneğin, Fluentd, Logstash) yönlendirebilir. Bu loglar daha sonra Elasticsearch, Splunk veya Datadog gibi platformlarda indekslenerek aranabilir, analiz edilebilir ve üzerine alarmlar kurulabilir. Bu, özellikle dağıtık bir sistemde yüzlerce worker
‘ın çalıştığı bir veri işleme platformunun takibi ve hata ayıklaması için paha biçilmezdir.
12. Yönetici Süreçleri (Admin Processes): Yönetim Görevlerini Tek Seferlik Süreçler Olarak Çalıştır
Veritabanı göçleri (database migrations), REPL (Read-Eval-Print Loop) açarak kod çalıştırma veya tek seferlik script’ler gibi yönetimsel görevler, uygulamanın normal uzun ömürlü süreçleriyle aynı ortamda çalıştırılmalıdır. Bu yönetimsel kodlar, senkronizasyon sorunlarını önlemek için uygulama koduyla birlikte dağıtılmalıdır.
Veri Profesyonelleri İçin Anlamı:
- Veritabanı Göçü:
dbt
(data build tool) kullanarak veri ambarınızda bir tablo şemasını değiştirmek istediğinizde,dbt run
veyadbt migrate
gibi bir komut çalıştırırsınız. Bu komut, uygulamanın geri kalanıyla aynı konfigürasyonlara (veritabanı bağlantısı vb.) sahip bir ortamda tek seferlik bir süreç olarak çalışmalıdır. - Model Yönetimi: Belirli bir makine öğrenmesi modelini yeniden eğitmek (retrain) için bir script’iniz olabilir. Bu script, uygulamanın ana kod tabanında yer almalı ve gerektiğinde (örneğin, bir cron job ile veya manuel olarak) uygulamanın canlı ortamda tek seferlik bir görev olarak çalıştırılmalıdır.
Sonuç
12-Factor App metodolojisi, 2011’de ortaya atılmış olmasına rağmen, ilkeleri bugün bulut-native ve veri-yoğun uygulamalar çağında her zamankinden daha geçerlidir.7 Konteynerleştirme (Docker) ve orkestrasyon (Kubernetes) gibi teknolojiler, bu ilkeleri hayata geçirmeyi daha da kolaylaştırmıştır.
Bir veri profesyoneli olarak bu prensipleri benimsemek, sadece daha iyi kod yazmanızı sağlamakla kalmaz, aynı zamanda geliştirdiğiniz veri ürünlerinin ve sistemlerinin daha güvenilir, ölçeklenebilir, bakımı kolay ve geleceğe dönük olmasını garanti eder. Bu ilkeler, sizi bireysel bir geliştirici olmaktan çıkarıp, büyük ölçekli ve dayanıklı sistemler inşa edebilen bir mühendis seviyesine taşıyacak bir yol haritası sunar.
Bir veri profesyoneli olarak bu konudaki ve Kubernestes yetenklerinizi geliştirmek isterseniz VBO Kubernetes for Data Professionals eğitimini tavsiye ederim.
Ek Kaynaklar
- Resmi 12-Factor App Websitesi (Türkçe): https://12factor.net/tr/
- Devops Türkiye – 12 Factor App Makalesi: https://medium.com/devopsturkiye/12-factor-app-uygulama-geliştirme-prensipleri-1ac51891cfc7
- dbt (data build tool): Veri dönüşümleri için 12-Factor prensiplerini benimseyen modern bir araç. https://www.getdbt.com/
- MLOps Prensİpleri: 12-Factor App’in birçok ilkesi, MLOps (Machine Learning Operations) dünyasının temelini oluşturur.