Yapay zeka destekli antrenman planlaması, giyilebilir cihaz entegrasyonu ve gerçek zamanlı yapay zeka koçluk sohbeti sunan, GCP Cloud Run üzerinde serverless çalışan ve Terraform ile yönetilen altyapıya sahip bir fitness ve sağlık takibi mobil uygulaması için FastAPI backend'i.
Markdown olarak oku ↗
Voxx'un backend'ini FastAPI ve asenkron SQLAlchemy 2.0 üzerine kurdum; buradaki temel problem antrenman planlarının statik olamamasıydı: kullanıcının uyku, HRV, strain ve ağrı sinyalleri günlük olarak değiştiği için planın buna göre — güvenilir ve hızlı bir şekilde — revize edilmesi gerekiyordu. Revizyonu iki katmana ayırdım: günlük plan güncellemeleri yapay zeka öncelikli olarak kural tabanlı bir fallback ile çalışır (başarısız bir Gemini çağrısı sessizce deterministik kurallara düşer), yeniden planlama kaynaklı 14 günlük plan onarımı ise tamamen deterministiktir ve veritabanı ya da yapay zeka çağrısı olmadan, öncelik sıralı bir kademe izleyerek çalışır — önce yer değiştirme, sonra mikro kaydırma, sonra tampon ekleme, sonra ölçek küçültme, sonra kaldırma, sonra tam yeniden oluşturma önerisi. Yapay zeka yalnızca gerçekten ihtiyaç duyulan yerde — içerik üretiminde — kullanılıyor; karar mantığı ise hem maliyet hem de test edilebilirlik açısından deterministik kalıyor. Giyilebilir cihaz entegrasyonu tarafında gerçek bir üretim olayıyla karşılaştım: Apple Health'ten gelen büyük birikmiş webhook'lar (~9,7MB, çoğunlukla saniye başına kalp atış hızı örnekleri) global 5MB body limitine takılıyor ve 413 hatası döndürüyordu; Insights ekranı da kullanıcılara sessizce "No Data" gösteriyordu. Bunu yola özgü bir body-limit override'ı, raw_data'nın kayıpsız küçültülmesi (kullanılmayan detaylı HR dizisinin kaldırılması, kayıt başına ~4,3MB'tan 27KB'a inilmesi), bozuk content-length/chunked body'lere karşı koruma önlemleri ve Redis kesintileri sırasında Terra'nın tekrar denemesi için 503 + Retry-After yanıtıyla çözdüm — sorunu kök nedenine kadar izleyerek kalıcı veri kaybını önledim. Ayrıca spesifikasyondaki belirsizlikleri bir mühendislik tahmini yerine bir ürün kararı olarak ele aldım: "48 saat" gibi bir gereksinim birden fazla şekilde yorumlanabildiğinde, seçtiğim yorumu (örneğin takvim günü bazlı) gerekçesiyle birlikte yazıya döktüm ve onay için müşterinin önüne koydum; küçük bir değişiklikle geri alınabilir olduğunu da belirttim — belirsizliği bir tahminle örtbas etmek yerine görünür kıldım. Kod tabanı genelinde servis/collaborator sınırları için 9 kuraldan oluşan bir kompozisyon deseni tanımladım (class'a karşı düz fonksiyon'a karşı dataclass, package'a karşı düz dosya) ve bunu modüller genelinde tutarlı şekilde uyguladım — örneğin injury-risk/ACWR hesaplayıcıları bağımsız, stateless, saf fonksiyonlardır; "0.0 strain eksik veri anlamına gelir" gibi ince davranışsal farklar kodda açıkça belgelenmiştir, çünkü bu davranışın dört farklı üretici genelinde tutarlı şekilde değişmesi gerekir.