Android teknik mülakatları yalnızca Kotlin sözdizimini hatırlayıp ViewModel açıklamakla ilgili değildir. İyi bir mülakat; gerçek bir Android uygulamasını tasarlayıp geliştirebildiğinizi, hata ayıklayabildiğinizi, test edebildiğinizi, yayınlayabildiğinizi ve uzun vadede sürdürebildiğinizi anlamaya çalışır.
Mülakat yapan kişi production ortamında bir şey bozulduğunda nasıl düşündüğünüzü, mimari kararları nasıl verdiğinizi, Android temellerini ne kadar iyi anladığınızı ve teknik ödünleşimleri açıkça anlatıp anlatamadığınızı görmek ister.
Birçok Android mülakatı üzerinde çalışınca şu sonuç netleşir: Teknolojiyi bilmek mücadelenin yalnızca yarısıdır. Bildiklerinizi yapılandırılmış, ikna edici ve gerçek deneyimlerle desteklenen bir şekilde anlatmanız da gerekir.
Bu rehber; Android mühendisliği teknik mülakatına nasıl hazırlanabileceğinizi, hangi sorularla karşılaşabileceğinizi ve cevaplarınızı nasıl daha güçlü hâle getirebileceğinizi anlatır.
Android Mülakatları Gerçekte Neyi Ölçer?
Teknik mülakat basit bir bilgi sınavı değildir.
Mülakat yapan kişi genellikle aynı anda birkaç sorunun cevabını arar: Sürdürülebilir Android kodu yazabilir misiniz? Production problemini sistematik biçimde araştırabilir misiniz? Ezberlenmiş tanımların ötesinde mimariyi anlayabiliyor musunuz? Performans ve eşzamanlılık hakkında akıl yürütebiliyor musunuz? Değişikliklerinizi nasıl test edeceğinizi biliyor musunuz? Her zaman tek bir mükemmel çözüm varmış gibi davranmadan ödünleşimleri açıklayabiliyor musunuz? Bir feature production ortamına çıktıktan sonra sorumluluk almaya devam eder misiniz?
Bu yüzden şu cevaplar genellikle yeterli değildir:
“MVVM kullandım çünkü temiz.”
“Jetpack Compose daha iyi çünkü daha az kod yazılıyor.”
Mülakat yapan kişi hangi problemin bulunduğunu, neden belirli bir yaklaşımı seçtiğinizi, hangi alternatifleri değerlendirdiğinizi ve çözümün gerçekten çalıştığını nasıl doğruladığınızı duymak ister.
Teknik Cevaplar İçin Daha İyi Bir Yapı
Davranışsal sorularda STAR — Situation, Task, Action, Result — modeli oldukça kullanışlıdır. Android teknik soruları için ise şu yapı daha açıklayıcıdır:
Problem → Araç veya Teknik → Teknik Karar → Ödünleşim → Doğrulama → Sonuç
Bu sıra, yalnızca teknoloji isimlerini sıralamak yerine mühendislik kararlarını açıklamanızı sağlar.
Örneğin “Firebase kullanarak crash’leri düzelttim” demek yerine şöyle cevap verebilirsiniz:
“Production’daki Android uygulamalarımızdan birinde crash oranı yaklaşık yüzde 4’tü. En sık görülen crash gruplarını bulmak için Firebase Crashlytics kullandım; stack trace’leri, etkilenen Android sürümlerini, cihazları ve kullanıcı akışlarını inceledim. En yüksek etkili problemi lokal ortamda yeniden ürettikten sonra lifecycle ve state yönetimiyle ilgili bir sorun buldum. Sadece savunmacı bir kontrol eklemek alttaki problemi bırakacağı için kısa vadeli düzeltmeyi yaptım ve state yönetimini ayrı bir değişiklikte refactor ettim. Regression testleri ekledik ve deploy sonrasında Crashlytics’i izledik. Crash oranı yaklaşık yüzde 4’ten yüzde 1’e düştü.”
Bu cevap; hata ayıklama, önceliklendirme, mimari, risk yönetimi, test ve production sorumluluğu hakkında kanıt sunar.
1. Kotlin Temelleri
Rol Android odaklı olsa bile Kotlin soruları bekleyin. Null safety, data class, sealed class, extension function, higher-order function, collection, generic, immutability, scope function, delegation ve hata yönetimi konularında rahat olmalısınız.
Sözdizimini bilmek kadar kavramların arasındaki farkı anlamak da önemlidir. val ve var sorusuna “val değişmez, var değişir” cevabı eksiktir. Daha iyi cevap şudur:
“
valreferansın yeniden atanmasını engeller ve immutable tasarımı teşvik eder; ancak referans verilen nesneyi otomatik olarak immutable yapmaz. Örneğinvalbir mutable listeyi gösterebilir ve listenin içeriği değişebilir. Özellikle Compose ve StateFlow ile çalışırken state geçişlerini daha kolay takip edebilmek için mümkün olduğunca immutable state kullanmayı tercih ederim.”
2. Coroutines ve Eşzamanlılık
Modern Android pozisyonlarında Kotlin Coroutines çok önemlidir. suspend, coroutine scope’ları, dispatcher’lar, structured concurrency, cancellation, exception handling, async, launch, withContext ve lifecycle-aware coroutine çalıştırmayı anlamalısınız.
launch doğrudan değer döndürmeden iş başlatır ve Job verir. async eşzamanlı işten sonuç beklediğinizde kullanılır; Deferred döndürür ve sonuç await() ile alınır. Her ikisini de cancellation ve hataların öngörülebilir ilerlemesi için structured scope içinde tutmaya çalışın.
İki coroutine aynı state’i aynı anda güncelliyorsa race condition oluşabilir. Çözüme göre immutable state, tek sahiplik, Mutex, atomic operation, database transaction veya mutation’ları tek bir akışta toplama yaklaşımlarından yararlanabilirsiniz.
3. Flow, StateFlow ve SharedFlow
Reactive state yönetimi modern Android’in temel parçalarındandır. Cold ve hot stream’leri, Flow, StateFlow, SharedFlow, lifecycle-aware collection, map, filter, combine, debounce ve hata yönetimini bilmelisiniz.
StateFlow mevcut durumu temsil eder ve her zaman bir değere sahiptir. SharedFlow ise güncel state semantiğinin gerekli olmadığı durumlarda birden fazla aboneye değer yayınlamak için kullanışlıdır.
“Loading, content veya error gibi bir ekran state’i için ViewModel’dan StateFlow expose ederim; çünkü UI en güncel durumu hemen görmelidir. Event benzeri bir davranış için SharedFlow kullanabilirim. Ancak event’in kaybolması UI’ı tutarsız bırakacaksa event’i kalıcı state’in parçası olarak modellemeyi de değerlendiririm.”
4. Jetpack Compose
Compose mülakatlarında recomposition, state, state hoisting, remember, rememberSaveable, LaunchedEffect, DisposableEffect, derivedStateOf, navigation, listeler, theming, preview ve geleneksel View’larla birlikte çalışma konuları sorulabilir.
Compose’un XML View’lara göre en önemli avantajı declarative ve state-driven modelidir. UI’ı elle bulup değişen state ile senkronize etmek yerine, belirli bir state için nasıl görünmesi gerektiğini tanımlar ve güncellemeleri Compose’a bırakırsınız. Bu yaklaşım boilerplate’i azaltır, ViewModel ve StateFlow ile doğal biçimde çalışır ve tekrar kullanılabilir bileşenlerle tutarlı bir design system kurmayı kolaylaştırır.
Ancak Compose otomatik olarak her uygulamayı daha hızlı yapmaz. Örneğin LazyColumn’ın her durumda RecyclerView’dan hızlı olduğunu söylemek doğru değildir. Compose’un avantajı çoğu zaman list state’ini ve güncellemeleri daha anlaşılır hâle getirmesidir. Stable key’ler, immutable UI state, gereksiz recomposition’lardan kaçınma ve pahalı işleri composable dışına çıkarma yine sizin sorumluluğunuzdadır.
5. Compose Performansını Ölçmek
Performansın iyileştiğini söylerseniz hemen şu soruyla karşılaşabilirsiniz: “Bunu nasıl ölçtünüz?” “Daha hızlı hissettirdi” bir mühendislik kanıtı değildir.
Duruma göre Android Studio Profiler, Compose Layout Inspector, recomposition incelemesi, frame rendering davranışı, memory kullanımı, startup ölçümleri, CPU profiling, Macrobenchmark, Baseline Profile ve gerçekçi büyüklükte dataset testlerinden bahsedebilirsiniz.
“Değişiklikten önce ve sonra scrolling davranışını Android Studio profiling araçlarıyla karşılaştırdım. Büyük dataset ile test yaptım, gereksiz recomposition’ları kontrol ettim ve pahalı işlemlerin composable içinde çalışmadığını doğruladım. ViewModel ve business logic’i koruyup presentation katmanını değiştirdim; liste yükleme, scrolling, seçim ve state değişiklikleri için UI testleri ekledim.”
6. Android Mimarisi
MVVM ve Clean Architecture’ı ders kitabı tanımı gibi değil, sorumluluklar üzerinden açıklayabilmelisiniz. ViewModel ekran state’ini sahiplenip dönüştürür; UI bu state’i gözlemler ve render etmeye odaklanır. Repository ve domain katmanları gerçekten değer kattığında kullanılır; UI bileşenleri networking, persistence ve business logic ile dolmaz.
“MVVM kullanıyorum çünkü UI state ile business logic arasında yararlı bir ayrım oluşturuyor. ViewModel ekran state’ini yönetiyor, UI ise bu state’i çiziyor. Bu ayrım test edilebilirliği artırıyor. Bununla birlikte küçük bir feature için yalnızca mimari saflık uğruna çok sayıda interface ve use case eklemem; gereksiz abstraction karmaşıklık yaratabilir.”
7. Lifecycle
Activity, Fragment, configuration change, process death, lifecycle-aware collection, ViewModel, saved state ve background davranışlarını anlamalısınız. Ezberlenmiş callback listeleri yerine sahipliği düşünün:
- State’in sahibi kim?
- State ne kadar süre yaşamalı?
- Ekran kaybolduğunda bu iş durmalı mı?
- Process yeniden oluşturulduğunda state korunmalı mı?
Bu sorular doğru lifecycle kararlarına doğal biçimde götürür. ViewModel ekranın yeniden oluşturulmasında state’i koruyabilir; lifecycle-aware collection ise artık görünür olmayan UI için gereksiz iş yapılmasını önleyebilir.
8. Background Work
Coroutine, Android Service, foreground service ve WorkManager arasındaki farkları bilin. WorkManager; uygulama process’i kaybolsa bile zaman içinde tamamlanması gereken, ertelenebilir synchronization, upload veya bakım işleri için uygundur. Sadece görünür bir ekrana bağlı işler için lifecycle-aware coroutine daha basit olabilir. Kullanıcının görünür biçimde devam eden uzun süreli bir iş beklediği durumda foreground service gerekebilir.
Seçimi teknoloji alışkanlığına göre değil; işin hemen mi başlaması gerektiğine, ne kadar süreceğine, kullanıcıya görünür olup olmadığına ve process termination sonrasında tamamlanıp tamamlanmaması gerektiğine göre yapın.
9. Networking
Retrofit, REST API, serialization, authentication, retry, cache, timeout, hata yönetimi ve connectivity failure başlıklarına hazırlanın. API bazen 500 dönüyor veya timeout oluyorsa client ve server hatalarını ayırın, güvenli request context log’layın, yalnızca tekrar edilmesi güvenli işlemlerde retry uygulayın ve exponential backoff kullanın.
“Üç kez retry ederim” demek yeterli değildir. Idempotency desteği yoksa POST işlemini körlemesine tekrar etmek duplicate kayıt oluşturabilir. UI’a da loading, offline, authentication failure ve server error gibi anlamlı durumlar aktarılmalıdır.
10. Room ve Yerel Persistence
Entity, DAO, migration, transaction, index, ilişki ve Room ile Flow kullanımını bilmelisiniz. Offline-capable bir uygulamada hangi kaynağın authoritative olduğunu açıklayın.
Yaygın bir akış Network → Repository → Room → Flow → ViewModel → UI şeklindedir; fakat bu tek doğru değildir. Önemli olan seçimin ürün gereksinimleriyle gerekçelendirilmesidir. Migration planı, iki işlemin aynı kayıtları değiştirmesi ve cache invalidation davranışı özellikle konuşulabilecek konulardır.
11. Dependency Injection
Dependency injection’ın amacı framework ezberinden önce anlaşılmalıdır. Nesne oluşturma sorumluluğunu nesneyi kullanma sorumluluğundan ayırır, bağımlılıkları görünür kılar, coupling’i azaltır ve test double kullanmayı kolaylaştırır.
“Bir ViewModel’ın repository’sini kendi içinde
DeviceRepositoryImpl()ile oluşturması testleri zorlaştırır. Repository’yi constructor üzerinden almak, gerçek veya fake implementasyonu dışarıdan vermeyi sağlar. Hilt ve Dagger gibi araçlar büyük projelerde dependency graph’ını yönetebilir; ancak araç kullanmak dependency tasarımının kendisi değildir.”
12. Testler
Unit, integration ve UI testlerinin görevlerini ayırın. Deterministic business logic ve ViewModel state geçişleri için hızlı unit testler; repository, database veya API sınırlarının birlikte çalışması için integration testler; önemli kullanıcı yolculukları için UI testleri kullanın.
“Her implementation detail’i UI automation ile test etmeye çalışmam; bu testler yavaş ve kırılgan olur. Production bug’ı için önce problemi yeniden üretir, hatanın hangi katmanda oluştuğunu bulur, düzeltir ve aynı problemi yakalayacak en dar güvenilir regression testini eklerim. Sonra etkilenen akışın tamamını doğrularım.”
13. Production Problemlerini Araştırmak
Son release’ten sonra crash oranı artarsa Crashlytics veya başka bir monitoring sistemiyle başlayın. Crash’leri sıklık ve etki bakımından gruplayın; stack trace, app version, OS version, cihaz modeli ve etkilenen feature’ı kontrol edin. Artışın deployment ile zaman ilişkisini inceleyin, mümkünse yeniden üretin ve kök nedeni belirleyin.
Seviyeye göre hotfix, rollback, feature flag veya daha geniş bir refactor arasında karar verin. Regression koruması ekleyin, mümkünse kademeli yayın yapın ve sonucu izleyin. Bu sıralama production olgunluğunu gösterir.
14. Performance ve Memory Leak
Startup time, memory, rendering, ANR, network, pil tüketimi, database query ve excessive recomposition başlıklarını kanıtla tartışın. İlke basittir: Önce ölçün, sonra optimize edin.
Memory leak örnekleri arasında uzun ömürlü nesnelerin Activity tutması, kaldırılmayan callback ve listener’lar, yanlış scope’a sahip dependency’ler, static reference’lar, sahibinden uzun yaşayan coroutine’ler ve lifecycle’dan habersiz observer’lar bulunur. Memory Profiler veya LeakCanary ile retained object chain’i izleyerek araştırma sürecini anlatın; yalnızca araç adını söylemeyin.
15. Media Playback
Media ağırlıklı pozisyonlar için Media3 ve ExoPlayer’a hazırlanın. Player lifecycle’ı, MediaSession, audio focus, notification, foreground service, buffering, playback state, configuration change ve kullanıcı oturumunu geri yükleme konuları sorulabilir.
“Playback motorunu Activity veya composable’a bağlamam; çünkü playback tek bir ekranın yaşamına bağlı olmamalı. Media3 ve uygun bir MediaSession kullanır, kalıcı playback için foreground-service mimarisini değerlendiririm. Playback state’in net bir sahibi olur ve farklı ekranların gözlemleyebilmesi için UI’a Flow ile açılır. Audio focus, interruption, notification, lifecycle ve state restoration senaryolarını ayrıca test ederim.”
16. Android İçin Sistem Tasarımı
Sistem tasarımı sorusunda hemen kütüphane isimleri sıralamayın. Önce gereksinimleri netleştirin: Kullanıcılar kim, ana akış nedir, offline davranış gerekli mi, veri ne kadar büyüyecek, latency ve consistency beklentisi nedir, güvenlik ve gözlemlenebilirlik gereksinimleri nelerdir?
Bir müzik uygulamasını tasarlarken UI, ViewModel, repository, local database, remote API, playback service, cache, authentication ve analytics sınırlarını açıklayın. Sonra process death, ağ kesintisi, duplicate event, token expiration ve büyük katalog gibi failure mode’ları ele alın.
İyi sistem tasarımı diyagramdan çok, kısıtlar ile kararlar arasındaki ilişkiyi gösterir.
17. Clean Architecture — Overengineering Yapmadan
Clean Architecture presentation, domain ve data sorumluluklarını ayırabilir. Değeri test edilebilirlik, değiştirilebilirlik ve sınırların netleşmesidir. Riski ise gereksiz katmanlarla küçük bir feature’ı ağırlaştırmaktır.
Bir feature için Screen, ViewModel, UseCase, Interactor, Repository, DataSource ve başka abstraction’lar eklemeden önce gerçek bir karmaşıklık problemi olduğundan emin olun. Mimari karmaşıklığı yönetmek içindir; yalnızca sofistike görünmek için yeni karmaşıklık üretmemelidir.
18. Trade-off Soruları
Senior seviyede mülakat yapan kişiler tek bir doğru cevap aramaz. Küçük ve düşük riskli bir hotfix ile kapsamlı refactor arasındaki farkı, hızlı teslimat ile sürdürülebilirlik arasındaki ilişkiyi ve teknik borcun ne zaman kabul edilebileceğini açıklamanızı bekler.
“Kritik production hatasında önce düşük riskli küçük bir düzeltmeyle sistemi stabilize edebilirim. Ardından kök mimari problemi ayrı bir iş olarak ele alırım. Kararı verirken public API değişimini, migration riskini, performansı, test kapsamını ve sonraki bakım maliyetini değerlendiririm.”
19. Kendi Projeleriniz Derinlemesine Sorulur
CV’nizdeki her teknoloji için ne yaptığınızı, neden yaptığınızı, hangi alternatifleri değerlendirdiğinizi, neyin bozulduğunu ve sonucu nasıl ölçtüğünüzü anlatmaya hazır olun. “Compose kullandım” dediğinizde recomposition’ı nasıl kontrol ettiğiniz sorulabilir. “Crash’i düzelttim” dediğinizde nasıl reproduce ettiğiniz sorulabilir.
Gerçekte katkınız sınırlıysa abartmayın. Kapsamınızı, kararınızı ve öğrendiğinizi açıkça anlatmak; projeyi tek başınıza tasarlamış gibi görünmeye çalışmaktan daha güçlüdür.
20. Bilmediğinizi Söylemeye Hazır Olun
Her Android API’sini bilmek mümkün değildir. Bilmediğiniz bir konuda tahmin yürütmek yerine bildiğiniz kısmı, varsayımınızı ve araştırma planınızı açıklayın.
“Bu API’nin tam davranışını production’da kullanmadım; bu nedenle kesin konuşmak istemem. Bildiğim kadarıyla lifecycle ile ilişkili, fakat dokümantasyonu ve küçük bir prototype’ı kontrol ederek karar verirdim. Benzer bir problemi daha önce şu yaklaşımla çözmüştüm.”
Bu cevap dürüstlüğü, öğrenme becerisini ve risk farkındalığını gösterir.
21. İletişim Skorunuzu Değiştirebilir
Teknik olarak doğru bir cevap anlaşılmıyorsa değerini kaybeder. Önce kısa cevabı verin, sonra gerekçeyi ve gerçek örneği ekleyin. Soruyu anlamadıysanız varsayımınızı söyleyip netleştirici soru sorun. Jargonla saklanmak yerine kararın kullanıcıya, ürüne ve operasyona etkisini anlatın.
Bir teknoloji adı söylediğinizde iki veya üç seviye takip sorusuna hazır olun. Mülakat yapan kişi ezberlenmiş tanımı değil, gerçek mühendislik deneyimini duymak ister.
22. Güçlü Deneyim Hikâyeleri Hazırlayın
Önceden şu başlıklarda kısa hikâyeler hazırlayın: zor bir production bug’ı, performans iyileştirmesi, mimari anlaşmazlık, başarısız olan bir yaklaşım, uçtan uca sahip olduğunuz bir feature, kısa deadline, mentorluk ve öğrendiğiniz bir hata.
Her hikâyeyi şu sırayla anlatın: Durum → Problem → Araştırma → Karar → Uygulama → Doğrulama → Sonuç. Mümkünse crash oranı, latency, adoption, test kapsamı veya teslimat süresi gibi ölçülebilir sonuçlar ekleyin.
23. Mülakat Yapan Kişiye Sorulabilecek Sorular
Mülakatın sonunda mutlaka soru sorun. Örneğin:
- Android ekibinin şu anda karşılaştığı en büyük teknik problemler neler?
- Uygulamanın ne kadarı Jetpack Compose kullanıyor ve kalan View tabanlı ekranlar için stratejiniz nedir?
- Bu role katılan bir mühendisten ilk üç ila altı ay içinde nasıl bir başarı bekliyorsunuz?
- Production gözlemlenebilirliği, release süreci ve incident yönetimi nasıl işliyor?
Bu sorular yalnızca teklif almakla değil, gerçek mühendislik ortamını anlamakla da ilgilendiğinizi gösterir.
Pratik Bir Hazırlık Stratejisi
- Kotlin, Coroutines, Flow, Compose, lifecycle ve architecture temellerini tekrar edin.
- Bir Android projesini baştan sona tasarlayıp nedenlerini açıklayabilecek hâle gelin.
- Crash, network failure, offline mode, performance ve memory leak senaryolarını çalışın.
- En az beş gerçek deneyim hikâyesi hazırlayın.
- Bir arkadaşınızla mock interview yapın ve takip sorularına özellikle çalışın.
- Cevaplarınızı ölçülebilir sonuçlar, trade-off’lar ve doğrulama adımlarıyla güçlendirin.
- Mülakat öncesinde şirketin ürününü, Android stack’ini ve rolün beklentilerini araştırın.
30 Android Teknik Mülakat Sorusu ve Gerçekçi Örnek Cevaplar
Teknik mülakatta yalnızca kavramın tanımını bilmek yeterli değildir. Güçlü bir aday, o kavramın gerçek bir projede neden kullanıldığını, hangi problemi çözdüğünü, hangi alternatiflerin değerlendirildiğini, çözümün nasıl doğrulandığını ve production sonrasında ne olduğunu anlatabilir.
Aşağıdaki cevaplar bilinçli olarak gerçek production deneyiminden anlatılıyormuş gibi hazırlanmıştır. Cevapların çoğu, “Situation, Task, Action, Result” başlıklarını açıkça kullanmadan doğal bir STAR akışı izler:
Bağlam → Problem → Karar → Uygulama → Doğrulama → Sonuç
Kavramsal sorularda yalnızca tanım vermeyin. Önce kavramı kısa ve doğru biçimde açıklayın, ardından gerçek bir projede neden kullandığınızı, hangi problemi çözdüğünü, hangi trade-off’ları gördüğünüzü ve sonucu nasıl doğruladığınızı anlatın. STAR yapısını başlıklara bölmeden doğal konuşmanın içine yerleştirin.
Bu cevapları ezberlenecek metinler olarak değil, kendi deneyiminizi yapılandırmak için örnekler olarak kullanın. Proje, teknoloji, ölçek ve metrikleri kendi gerçek deneyiminize göre değiştirin; yapmadığınız bir işi veya elde etmediğiniz bir sonucu sahiplenmeyin.
1. Yakın zamanda çalıştığınız bir Android projesini anlatır mısınız?
Bir saha operasyonu Android uygulamasında kullanıcılar özellikle aktif bir form sırasında uygulamayı arka plana alıp geri döndüklerinde zaman zaman crash yaşıyordu. Yaklaşan release’i geciktirmeden uygulamanın kararlılığını artırmam gerekiyordu. Crashlytics, stack trace’ler, lifecycle logları ve etkilenen Android sürümlerini inceleyerek en önemli crash gruplarını belirledim. Problemlerden birini aynı akışı tekrar ederek reproduce ettim ve Activity yeniden oluşturulduktan sonra eski UI state’ine erişildiğini gördüm. State sahipliğini ViewModel’e taşıdım, loading ve restoration durumlarını açık hale getirdim ve ilgili akış için regression testleri ekledim. Release sonrasında aynı crash grubunun production monitoring’de tekrar oluşmadığını doğruladım.
2. Production crash’ini nasıl araştırırsınız?
Bir e-commerce uygulamasında yeni checkout release’inden sonra Crashlytics üzerinde crash oranında ani bir artış gördük. Önce artışın uygulama sürümü, Android sürümü ve belirli cihazlarla ilişkili olup olmadığını kontrol ettim. Stack trace navigation state tarafını işaret ediyordu ancak yalnızca hata veren satırı değiştirmek yerine aynı checkout akışını lokal ortamda reproduce ettim. Gecikmiş bir network callback’inin artık aktif olmayan bir ekranı güncellemeye çalıştığını gördüm. Request yönetimini ViewModel’e taşıdım, cancellation’ı lifecycle-aware hale getirdim ve aynı sequence’i tekrar oluşturan regression testi ekledim. Fix’i kademeli yayınlayıp crash grubunun kaybolduğunu doğruladıktan sonra rollout’u tamamladık.
3. Kritik bir bug’ı düzeltirken hangi ödünleşimleri düşünürsünüz?
Bir release öncesinde ödeme akışında production’ı etkileyen kritik bir hata tespit ettik. Aynı component’in daha kapsamlı bir refactor’a ihtiyacı vardı fakat bu refactor’ı acil fix ile birlikte yayınlamak regression riskini artıracaktı. Bu nedenle problemi iki parçaya ayırmayı önerdim: önce ödeme akışını stabilize edecek küçük ve test edilebilir bir fix, ardından ayrı bir architectural refactor. Hatalı path için regression testleri ekledik ve deployment sonrasında payment failure metriklerini izledik. Böylece release’i geciktirmeden kritik problemi çözdük ve altta yatan teknik borcu da görünür bir follow-up task olarak bıraktık.
4. Jetpack Compose’un geleneksel View’lara avantajı nedir?
Bir uygulamada ürün listesi ekranı XML, RecyclerView, Adapter ve ViewHolder kullanılarak geliştirilmişti. Filtreler ve loading durumları arttıkça Adapter ile ekran state’ini manuel olarak senkronize etmek zorlaşmaya başladı. Yeni versiyonu Compose ile geliştirirken ekranı ViewModel’den StateFlow ile gelen immutable UI state olarak modelledim ve reusable composable’larla render ettim. Böylece loading, error, empty ve content durumları açık hale geldi ve manuel UI update kodlarının önemli bir kısmı ortadan kalktı. Benim için Compose’un temel avantajı yalnızca daha az kod değil, UI’ın state’in öngörülebilir bir fonksiyonu haline gelmesiydi.
5. Compose ile hangi zorlukları yaşadınız?
RecyclerView’dan LazyColumn’a geçiş yaptığımız bir ekranda ilk implementasyonda scrolling ve item selection sırasında beklediğimden fazla recomposition oluştuğunu fark ettim. Amacım Compose’un daha sade yapısını korurken performans regression’ı oluşturmamaktı. Compose tooling ve profiling araçlarıyla sık tekrar compose edilen item’ları tespit ettim, stable key kullandım, mutable screen modellerini immutable UI modellere çevirdim ve pahalı dönüşümleri composable’ların dışına taşıdım. Ayrıca her satıra yalnızca ihtiyacı olan state’i gönderdim. Aynı dataset ile tekrar ölçtüğümde scrolling stabil kaldı ve kod eski Adapter tabanlı yapıya göre daha kolay yönetilebilir hale geldi.
6. LazyColumn RecyclerView’dan hızlı mıdır?
Bir UI migration sırasında mevcut RecyclerView ekranıyla yeni LazyColumn implementasyonunu karşılaştırdık. RecyclerView zaten oldukça optimize olduğu için Compose’un otomatik olarak daha hızlı olacağını varsaymadım. Benzer dataset’lerle frame davranışı, scrolling, memory kullanımı ve update mekanizmasını ölçtük. Performans farkı tek başına migration’ı haklı çıkaracak kadar büyük değildi. Asıl kazanç Adapter, ViewHolder ve DiffUtil koordinasyonunun ortadan kalkması ve state yönetiminin sadeleşmesiydi. Bu deneyim bana RecyclerView veya LazyColumn kararının varsayıma değil mevcut mimariye, ekip deneyimine ve gerçek ölçümlere dayanması gerektiğini gösterdi.
7. Compose migration sonrasında performansı nasıl ölçtünüz?
Dinamik bir listeyi Compose’a taşıdıktan sonra yeni implementasyonun rendering veya memory problemi oluşturmadığını ölçümlerle göstermek istedim. Eski ve yeni ekranı gerçekçi ve normalden daha büyük dataset’lerle çalıştırarak Android Studio profiling ve Compose inspection araçlarıyla scrolling, memory kullanımı, frame davranışı ve recomposition sayısını karşılaştırdım. Karşılaştırmanın UI tarafına odaklanması için ViewModel ve data layer’ı değiştirmedim. Loading, scrolling, item selection ve state restoration için UI testleri ekledim ve mevcut unit testleri çalıştırdım. Ayrıca farklı Android sürümleri ve ekran boyutlarında manuel regression yaptım. Functional davranış ve performans kabul edilebilir seviyede kaldıktan sonra migration’ı production’a aldık.
8. Recomposition nedir?
Compose ile geliştirilmiş bir arama ekranında kullanıcı her karakter yazdığında query state değişiyor ve recomposition oluşuyordu. Bu beklenen bir davranıştı; recomposition gözlemlenen state değiştiğinde UI’ın güncellenme mekanizmasıdır. Problem, pahalı bir dönüşümün de her recomposition sırasında composable içinde tekrar çalışmasıydı. Bu hesaplamayı ViewModel’e taşıdım ve UI’a hazırlanmış state gönderdim. Ayrıca query state’ini yalnızca gerçekten ihtiyaç duyan component’lerin izlemesini sağladım. Ekranın responsiveness’ı iyileşti. Bu yüzden recomposition’ı tek başına problem olarak görmem; önemli olan recomposition sırasında hangi işlerin yapıldığı ve UI ağacının ne kadarının etkilendiğidir.
9. State hoisting nedir?
Tekrar kullanılabilir bir filtre component’i geliştirirken ilk versiyonda seçili filtre state’ini component’in içinde tutmuştum. Tek ekranda çalışıyordu fakat başka bir ekran aynı component’i dış state üzerinden kontrol etmek ve navigation sonrasında selection’ı restore etmek istedi. State sahipliğini parent seviyesine taşıdım ve component’e mevcut değer ile
onValueChangecallback’i verdim. Böylece component farklı akışlarda tekrar kullanılabilir hale geldi ve preview ile test etmek de kolaylaştı. Benim için state hoisting’in gerçek değeri state’i mümkün olan en yukarıya değil, onu gerçekten kontrol etmesi gereken seviyeye taşımaktır.
10. remember ve rememberSaveable arasındaki fark nedir?
Bir form ekranında yalnızca UI’a ait küçük bir expand/collapse state’i için
rememberkullandık; ekran yeniden oluşturulduğunda bu değerin kaybolması önemli değildi. Başka bir ekranda ise kullanıcı bir tab seçtikten sonra cihazı döndürdüğünde selection’ın sıfırlanması kötü bir deneyim yaratıyordu. Bu küçük ve restore edilebilir UI state’i içinrememberSaveablekullandık. Form verisi ve business state ise farklı lifecycle gerektirdiği için ViewModel’de kaldı. Böylece geçici UI state’i ile ekranın gerçek business state’ini birbirine karıştırmamış olduk.
11. Neden ViewModel kullanırsınız?
Bir checkout akışının ilk implementasyonunda loading state, seçilen teslimat bilgileri ve network request’leri Fragment tarafından yönetiliyordu. Rotation sonrasında bazı işlemler yeniden başlıyor ve error recovery karmaşıklaşıyordu. Screen-level state ve request koordinasyonunu ViewModel’e taşıdım ve tek bir immutable UI state’i StateFlow üzerinden expose ettim. Fragment veya composable yalnızca state’i render edip kullanıcı event’lerini ViewModel’e gönderdi. Böylece configuration change sırasında gereksiz reload’lar önlendi ve checkout state transition’ları unit test edilebilir hale geldi. ViewModel’in asıl değeri yalnızca rotation’dan survive etmesi değil, ekran state’ine UI’dan bağımsız net bir owner vermesiydi.
12. Neden MVVM kullanırsınız?
Bir medya uygulamasının eski ekranlarında playback çağrıları, networking, persistence ve UI update’leri doğrudan Activity içinde bulunuyordu. Feature sayısı arttıkça bir davranışı değiştirmek ilgisiz UI kodlarını da etkileyebiliyordu. Screen state ve koordinasyonu ViewModel’e, playback’i ayrı bir service katmanına, data access’i ise repository arkasına taşıdık. View tarafı ağırlıklı olarak state render edip user action iletmeye başladı. Böylece unit testing kolaylaştı ve UI değişiklikleri playback veya persistence kodunu etkilemeden yapılabildi. Ancak küçük bir ekran için aynı sayıda katmanı zorunlu tutmam; mimari gerçek karmaşıklıkla birlikte büyümeli.
13. Clean Architecture nedir?
Hem remote API hem de local database kullanan bir uygulamada business rule’lar zaman içinde Retrofit response modelleri ve database entity’lerine bağımlı hale gelmişti. Bu durum testleri zorlaştırıyor ve API değişikliklerinin etkisini büyütüyordu. Presentation, domain ve data katmanlarının sınırlarını belirgin hale getirip network ve persistence modellerini sınırda domain modellere çevirdik. Böylece business logic Android veya network bağımlılığı olmadan test edilebilir hale geldi ve API implementasyonu değiştiğinde daha küçük bir alan etkilendi. Bunun karşılığında mapping ve interface sayısı arttığı için yaklaşımı her feature’a mekanik olarak uygulamak yerine gerçek karmaşıklığın olduğu yerlerde kullandık.
14. StateFlow ve SharedFlow ne zaman kullanılır?
Bir müzik uygulamasında birden fazla ekran playback’in playing, paused veya buffering olup olmadığını ve hangi parçanın aktif olduğunu bilmek zorundaydı. Yeni bir collector başladığında mevcut player state’ini anında alması gerektiği için bu bilgiyi StateFlow ile expose ettim. Import işleminin tamamlandığını bildiren kısa süreli bir UI mesajında ise kalıcı screen state tutmaya gerek olmayan event tarzı bir akış kullandık. Durable state ile transient sinyalleri ayırmak, ekranların önemli state’i event’lerden yeniden oluşturmaya çalışmasını engelledi. Benim için temel soru yeni subscriber’ın mevcut değeri hemen bilmesi gerekip gerekmediğidir.
15. Flow ve LiveData arasındaki fark nedir?
Eski bir uygulamada UI, Room verisini LiveData üzerinden gözlemliyordu. Daha sonra local data’yı search input, user preferences ve remote synchronization state ile birleştirmemiz gerektiğinde bu pipeline Flow operator’larıyla daha kolay ifade edildi. Yeni feature’ı Flow ve StateFlow’a geçirirken stabil legacy ekranları sırf teknoloji değiştirmek için yeniden yazmadık. ViewModel database update’leri ile query değişikliklerini combine ederek UI state’e dönüştürdü ve Compose bu state’i lifecycle-aware şekilde collect etti. Böylece tüm uygulamayı gereksiz yere migrate etmeden daha esnek bir asynchronous pipeline elde ettik.
16. launch ve async arasındaki fark nedir?
Bir dashboard ekranında tam UI’ı gösterebilmek için birbirinden bağımsız iki API’den profile ve account-summary verisi almamız gerekiyordu. Request’leri sıralı çalıştırmak gereksiz bekleme oluşturduğu için ikisini aynı structured scope içinde
asyncile başlatıp sonuçlarınıawaitile aldım. Aynı ViewModel içinde sonucu ekran için gerekli olmayan analytics işlemlerini iselaunchile tetikledim. İkisini deviewModelScopeiçinde tuttuğumuz için ViewModel temizlendiğinde işler de iptal edilebildi. Gerçek projede benim için temel ayrım concurrent işten bir return value bekleyip beklemediğimdir.
17. Structured concurrency nedir?
Bir profile ekranında ekran yüklenirken kullanıcı bilgileri, preferences ve subscription verileri için birkaç child operation başlıyordu. İlk implementasyonda bunlardan bazıları application-level scope’ta çalıştığı için kullanıcı ekranı terk ettikten sonra da devam edip eski state’i güncelleyebiliyordu. İşleri ViewModel’in coroutine scope’u altına taşıdık ve child coroutine’leri aynı parent operation’a bağladık. Owner ortadan kalktığında cancellation öngörülebilir şekilde propagate oldu. Partial failure yönetimi de kolaylaştı çünkü her task’in yaşam süresi net hale geldi. Bu proje structured concurrency’nin benim için pratik anlamını gösterdi: asynchronous işin mutlaka anlamlı bir owner’ı olmalı.
18. Coroutine exception’larını nasıl yönetirsiniz?
Bir data-refresh akışında birbirinden bağımsız üç request paralel çalışıyordu. İlk implementasyonda optional bir request’in hata vermesi tüm refresh işlemini cancel ediyor, ana veri başarılı olsa bile ekran açılamıyordu. Failure semantics’i tekrar değerlendirip optional recommendation request’inin temel account data’yı iptal etmemesi gerektiğine karar verdik. Beklenen network failure’ı empty result’a dönüştürürken cancellation ve beklenmeyen programming error’ların propagate olmasına izin verdik. Böylece UI kritik veriyi gösterebildi ve yalnızca optional bölüm görünmedi. Buradan çıkardığım ana ders exception handling’in ürünün failure semantics’ini yansıtması gerektiğiydi; her şeyi global catch ile saklamak doğru çözüm değildi.
19. Race condition nedir?
Bir search feature’ında kullanıcı filtreleri hızlı değiştirdiğinde aynı anda birkaç API request’i oluşabiliyordu. Bazen eski request yenisinden sonra tamamlanıyor ve UI’ı yanlış filtre sonucu ile overwrite ediyordu. Sonucun timing’e bağlı olması bunun klasik bir race condition olduğunu gösteriyordu. Search pipeline’ını yeni filter seçildiğinde önceki request’i cancel edecek ve yalnızca son query’nin visible state üretebileceği şekilde değiştirdik. Controlled delayed response kullanan bir test ile request sıralamasını da reproduce ettik. Değişiklikten sonra hızlı filter geçişlerinde ekranda tamamlanma sırası değil kullanıcının son seçimi görünmeye başladı.
20. WorkManager’ı ne zaman kullanırsınız?
Bir inspection uygulamasında kullanıcılar offline durumda rapor tamamlayabiliyor ve daha sonra backend’e gönderilmesi gereken fotoğraflar ekleyebiliyordu. Screen lifecycle’ına bağlı coroutine yeterli değildi çünkü kullanıcı uygulamayı kapatabilir veya Android process’i upload tamamlanmadan sonlandırabilirdi. Pending operation’ı local olarak saklayıp network constraint ile WorkManager üzerinden upload planladık. Geçici network failure’larında backoff ile retry kullandık, permanent validation error’ları ise sonsuz retry yerine kullanıcı müdahalesi için kaydettik. Böylece upload güvenilirliği UI lifecycle’ından bağımsız hale geldi ve bağlantı geri geldiğinde iş tamamlanabildi.
21. Service ve WorkManager farkı nedir?
Bir medya uygulamasında iki farklı background ihtiyacımız vardı. Audio playback kullanıcı ekrandan ayrıldığında kesilmeden devam etmeli ve notification üzerinden kontrol edilebilmeliydi; bu nedenle MediaSession ile foreground service kullandık. Bunun yanında analytics synchronization ve metadata download işlemleri uygun koşullar oluşana kadar bekleyebildiği için WorkManager kullandık. İki problemi aynı mekanizmayla çözmek yanlış lifecycle davranışı oluşturacaktı. Kararı ihtiyaçtan çıkardık: playback immediate, uzun süren ve kullanıcıya görünür bir işti; synchronization ise ertelenebilir ama sonunda tamamlanması gereken bir işti.
22. Background audio playback’i nasıl tasarlarsınız?
Podcast benzeri bir uygulamada playback başlangıçta control’leri gösteren ekrana fazla bağlıydı. Navigation sırasında UI component’lerinin yeniden oluşturulması player state’inde tutarsızlık yaratabiliyordu. Media3 playback ve MediaSession sahipliğini foreground playback service’e taşıdık ve UI’ın observe edebileceği tek bir playback state oluşturduk. Activity ve composable’lar player’ın owner’ı olmak yerine controller haline geldi. Audio focus, headset events, notification controls, interruption ve aktif içeriğin restore edilmesini ayrı ele aldık. Navigation, background, screen lock ve Bluetooth senaryolarını test ettikten sonra playback ekranlardan bağımsız hale geldi ve birden fazla UI aynı session’ı tutarlı şekilde kontrol edebildi.
23. API failure’larını nasıl yönetirsiniz?
Bir order-creation akışında backend tarafında zaman zaman timeout oluşuyordu. Her başarısız request’i otomatik retry etmek tehlikeliydi çünkü server işlemi başarılı olmuş fakat response kaybolmuşsa duplicate order yaratılabilirdi. Hataları türlerine göre ayırdık ve order creation için backend ile birlikte idempotency key kullandık. Güvenli GET request’lerinde sınırlı exponential backoff, authentication failure’da session recovery, validation error’da ise doğrudan kullanıcıya anlamlı hata gösterimi uyguladık. Sensitive data içermeden gerekli request context’ini logladık. Böylece UI gerçek recovery seçenekleri sunarken “network hatası varsa retry et” gibi riskli bir yaklaşımı önledik.
24. Offline-first Android uygulamasını nasıl tasarlarsınız?
Bir depo uygulamasında çalışanların bağlantının zayıf olduğu alanlarda inspection kaydı yapabilmesi gerekiyordu. UI’ın observe ettiği kaynak olarak Room kullandık; kullanıcı inspection oluşturduğunda veya düzenlediğinde local database hemen güncellendi. Senkronize edilmemiş kayıtların ayrı sync state’i vardı ve connectivity geldiğinde WorkManager remote synchronization deniyordu. En önemli tasarım konusu Room değil conflict resolution’dı. Hangi field’ların server-authoritative olduğunu ve hem local hem remote değişen kayıtların nasıl birleştirileceğini önceden belirledik. Sonuç olarak kullanıcılar internetsiz çalışmaya devam edebildi ve synchronization davranışı sessiz overwrite yapmak yerine açık ve izlenebilir hale geldi.
25. Regression’ları nasıl önlersiniz?
Bir uygulamada kullanıcı form ekranındayken app background’a alınıp belirli süre sonra geri dönüldüğünde oluşan bir crash’i düzeltmiştik. Kodu değiştirmeden önce aynı lifecycle sequence’i reproduce edip invalid state’e izin veren transition’ı belirledim. State ownership problemini düzelttikten sonra hatayı tekrar oluşturan en dar güvenilir regression testini ekledim ve mevcut ViewModel testlerini korudum. Kritik UI flow’u manuel olarak da çalıştırdık ve release sonrasında ilgili Crashlytics grubunu izledik. Amacımız yalnızca yeni kodun testlerden geçmesi değil, production’da gördüğümüz aynı hatanın tekrar eklenmesini zorlaştırmaktı.
26. Android test stratejiniz nedir?
Bir checkout feature’ında farklı sorumlulukları farklı test seviyelerinde koruduk. Pricing ve validation rule’ları deterministic oldukları için hızlı unit testlerle test ettik. Database ile API mapping’in birlikte çalıştığı repository davranışlarını integration testlerle doğruladık. UI testlerini checkout açılması, delivery seçimi, payment tamamlanması ve error state gösterilmesi gibi kritik user journey’lerle sınırlı tuttuk. Böylece her implementation detail’ı yavaş UI automation ile test etmeye çalışmadan yeterli güven elde ettik. Testler CI’da çalıştı ve production incident’ları eksik regression coverage’ı tespit etmek için kullandık. Böylece test suite yavaş ve kırılgan bir yük yerine delivery sürecinin parçası oldu.
27. Uçtan uca sahip olduğunuz bir feature’ı anlatın.
Bir medya uygulamasında offline download feature’ının requirement aşamasından release’e kadar sorumluluğunu aldım. Kullanıcıların büyük medya dosyalarını indirebilmesi, pause/resume yapabilmesi ve ekrandan ayrıldıktan sonra progress’i kaybetmemesi gerekiyordu. Önce state modelini ve storage gereksinimlerini belirledim, ardından download execution’ı UI’dan ayırarak navigation’dan bağımsız hale getirdim. Download metadata’sını persist ettim, progress’i ViewModel üzerinden expose ettim ve recoverable network failure’ları için retry davranışı ekledim. Interrupted download, düşük storage, cancellation, app restart ve farklı network koşullarını test ettik. Release sonrasında feature stabil çalıştı ve aynı altyapı yeni medya türleri için de tekrar kullanılabildi.
28. Çözdüğünüz zor bir teknik problemi anlatın.
Büyük bir offline synchronization akışında kullanıcılar aynı kaydı iki farklı cihazda, iki cihaz da tekrar online olmadan önce düzenleyebiliyordu. İlk synchronization implementasyonu timestamp’e güveniyordu ve bazı durumlarda daha yeni kullanıcı verisini overwrite ediyordu. Controlled conflicting update’lerle problemi reproduce edip backend ekibiyle farklı stratejileri değerlendirdik. Hassas field’lar için basit “last write wins” yaklaşımını reddedip explicit versioning ve field-specific conflict rule’lar kullandık. Client güvenli field’ları otomatik merge etti, belirsiz değişiklikleri ise resolution için görünür hale getirdi. Farklı ordering ve network delay’lerini simüle eden integration testleri ekledik. Sistem biraz daha karmaşık hale geldi ancak sessiz veri kaybını önledi ve synchronization davranışını deterministic yaptı.
29. Code quality ile deadline arasında nasıl denge kurarsınız?
Önemli bir release öncesinde hem yeni bir feature deadline’ımız vardı hem de kullandığımız component’in refactor’a ihtiyacı olduğu açıktı. Component’i tamamen yeniden yazmak release’i geciktirip regression riskini artıracaktı; problemi tamamen görmezden gelmek ise sonraki işleri zorlaştıracaktı. Correctness, security, kritik path testleri ve anlaşılabilir kod gibi non-negotiable kalite noktalarını belirledim ve bunlardan vazgeçmek yerine feature scope’unu küçültmeyi önerdim. Daha küçük feature’ı güvenli şekilde yayınladık ve refactor’ı sınırları belirlenmiş follow-up çalışma olarak planladık. Böylece deadline’ı yakalarken gizli teknik borç veya sürekli overtime oluşturmadık.
30. Bize sormak istediğiniz bir şey var mı?
Evet. Ekibin gerçek mühendislik ortamını biraz daha anlamak isterim: “Android codebase’in şu anda karşılaştığı en büyük teknik problemler neler?” “Uygulamanın ne kadarı Jetpack Compose kullanıyor ve kalan View tabanlı ekranlara nasıl yaklaşıyorsunuz?” “Release sonrasında reliability ve performance’ı nasıl ölçüyorsunuz?” “Bu role katılan bir mühendenden ilk üç ila altı ay içinde nasıl bir başarı bekliyorsunuz?”
Takip Sorularına Nasıl Cevap Verilir?
En büyük hatalardan biri yalnızca ilk cevabı hazırlamaktır. “Performansı iyileştirdim” derseniz “Nasıl ölçtün?”; “StateFlow kullandım” derseniz “Neden SharedFlow değil?”; “Clean Architecture kullandım” derseniz “Bu mimarinin maliyeti neydi?” sorularını bekleyin.
Kullanışlı bir kural şudur: Mülakatta bir teknolojinin adını, onun hakkında iki veya üç seviye takip sorusuna hazır değilseniz kullanmayın.
Hatırlanacak Basit Formüller
Production deneyimi için:
Durum → Problem → Araştırma → Karar → Uygulama → Doğrulama → Sonuç
Teknik kavramlar için:
Tanım → Neden var → Ne zaman kullanırım → Ödünleşim → Gerçek örnek
Mimari için:
Gereksinimler → Kısıtlar → Seçenekler → Karar → Ödünleşimler → Doğrulama
Hata ayıklama için:
Gözle → Ölç → Yeniden üret → İzole et → Düzelt → Test et → İzle
Sonuç
Android teknik mülakatına hazırlanmak, API isimlerini ezberlemekten çok daha fazlasıdır. Gerçek bir uygulamanın state, lifecycle, network, persistence, performance, test ve release problemlerini nasıl ele aldığınızı anlatabilmelisiniz.
Güçlü cevaplar; problemi netleştirir, kararın nedenini açıklar, ödünleşimi kabul eder, sonucu ölçer ve production sorumluluğunu gösterir. Bu şekilde cevap verdiğinizde Android API’lerini ezberlemiş biri gibi değil, Android yazılımını gerçek hayatta işletmiş bir mühendis gibi görünürsünüz.