MyFamilyTracker
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
After shipping 20+ Android apps as a solo developer, I settled on MVVM with Clean Architecture, Hilt, and StateFlow. Here's why, and what I'd tell myself if I were starting over.
On this page
After shipping more than 20 Android apps solo — everything from habit trackers to sleep monitors to focus tools — I've made the same architectural mistakes enough times to know exactly what works and what quietly destroys you at 2am before a Play Store deadline.
Here's what I landed on, and why.
When you're building your first Android app, you put everything in the Activity. It works. Then you add a second screen, a third, a network call, a database — and suddenly you have a 1000-line Activity that nobody (including future-you) can read.
The second mistake is overreacting and building a giant framework before you have anything to show. I've done both.
The right level of structure is: just enough to not drown.
Every app I ship now follows the same three-layer pattern:
Data layer — repositories, Room DAOs, DataStore, network calls. Nothing here knows about the UI.
Domain layer — one UseCase class per action.
GetHabitsUseCaseLogWaterUseCaseCompleteOnboardingUseCasePresentation layer — ViewModels that hold
StateFlow<UiState>The rule I enforce: if a Composable contains an
ifLiveData requires an Activity or Fragment lifecycle owner. StateFlow doesn't. When I moved to pure Compose, I stopped needing lifecycle owners for UI observation — StateFlow just works.
The other reason: StateFlow is explicit about initial state. You declare
MutableStateFlow(initialValue)I tried manual dependency injection once. You create a factory, pass it everywhere, update 12 files when a constructor changes. It's miserable.
Hilt adds two annotations to your module, one to your ViewModel, and everything gets wired automatically. The compile-time validation means you find missing dependencies at build time, not runtime.
The rule:
@HiltViewModel@Provides @SingletonKAPT (Kotlin Annotation Processing Tool) is deprecated. It compiles Kotlin to Java stubs, runs Java annotation processors, then compiles back. It's slow and occasionally broken.
KSP (Kotlin Symbol Processing) processes Kotlin directly. Build times dropped noticeably on my larger projects. Room, Hilt, and every major library support it. There's no reason to use KAPT in a new project today.
I shipped two apps with bare
CoroutineScope(Dispatchers.IO).launch {}@SingletonThe fix is one extra class:
@Qualifier @Retention(AnnotationRetention.BINARY)
annotation class ApplicationScope
@Provides @Singleton @ApplicationScope
fun provideApplicationScope(): CoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.IO)Inject
@ApplicationScope CoroutineScopePick an architecture and stay consistent. The specific choices matter less than applying them uniformly. Mixed patterns across screens are harder to debug than a slightly imperfect pattern applied everywhere.
UseCase classes feel over-engineered until they save you. When you need to reuse logic across two ViewModels, or test business logic without mocking Android, you'll be glad it's in its own class.
StateFlow initial state is not optional. Every screen should render something on first observation. Define your sealed class or data class upfront.
KSP from day one. Never KAPT.
Hilt is not boilerplate — it's leverage. The annotations pay for themselves the first time you need to swap an implementation for tests.
The architecture isn't the goal. Shipping apps that don't embarrass you in six months is the goal. These choices just happen to make that easier.
All 20+ apps are live on Google Play under SudarshanTechLabs.
Sudarshan Chaudhari
AI Systems Builder / Product Engineer
Bangkok, Thailand
Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.
Related Posts
Related Apps
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Building something? Available for Android dev and QA consulting.
Work with meComments — powered by Giscus
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
ReadPrivate dream journal — structured entry capture, pattern tagging, and optional Claude-powered insight generation. All data stays on-device by default.
ReadWorkout tracker — exercise logging with set/rep/weight history, goal progression, and local Room DB persistence. No account, no cloud sync required.
Read