В какой‑то момент мне прилетел баг‑репорт, который идеально описывает боль social/mobile приложений:> «Я поставила в групповой привычке „выходной“, потом нажалаВ какой‑то момент мне прилетел баг‑репорт, который идеально описывает боль social/mobile приложений:> «Я поставила в групповой привычке „выходной“, потом нажала

Не Vibe-Coding, а инженерия с AI: как я за полгода сделал Android-приложение: социальный трекер привычек

2026/03/02 21:06
7м. чтение
Для обратной связи или замечаний по поводу данного контента, свяжитесь с нами по адресу crypto.news@mexc.com

В какой‑то момент мне прилетел баг‑репорт, который идеально описывает боль social/mobile приложений:
> «Я поставила в групповой привычке „выходной“, потом нажала „возобновить“ и выполнила. У меня всё ок. У подруги — у меня до сих пор „выходной“».

То есть локально — одно состояние, у других участников — другое. Не UI‑баг, не «кнопка не нажимается». Чистая проблема консистентности данных.

Ниже — разбор, как я за ~6 месяцев сделал Android‑приложение Habit Tracker со всеми основными функциями, геймификацией, виджетом домашнего экрана, а так же social‑функциями. Расскажу почему основная сложность была в синхронизации Room↔Firestore, и где AI реально помогает, а где без инженерной дисциплины всё развалится.

Приложение в RuStore: [Habit Tracker](https://www.rustore.ru/catalog/app/com.habittracker) <!‑more‑→

‑- ## TL;DR

— Идея: собрать «лучшее из разных трекеров» + добавить социальные функции, которых обычно не хватает. ‑ Архитектура: Clean + MVVM, Room-first, Outbox, Realtime merge. ‑ Основная сложность: не экраны, а корректные multi‑user/offline состояния. ‑ AI использовал как ускоритель, а не как «автопилот разработки».

‑- ## Откуда взялась идея

Идею разработать такое приложение мне подкинул друг. До этого я даже и не слышал вообще о существовании трекеров привычек, не знал что такое вообще есть и как, для каких целей и для чего такие приложения используют. А мой друг как раз искал для себя такое и обратился ко мне со своей идеей. Он прошёлся по нескольким трекерам привычек и в каждом нашёл «что‑то полезное», но полного набора того, чего ему бы хотелось видеть для себя, в таких трекерах не было нигде. И тогда, объяснив свои потребности, он предложил мне создать самом такое приложение. Концепт состоял в том, что бы в приложении было всё, что ему нужно: личные привычки, с возможностью отмечать их выполнение или указывать сколько раз было сделано то или иное действие привычки (отжался 20 раз, или выпил 8 стаканов воды в день и тому подобное), групповые привычки — привычки, которые можно выполнять совместно с друзьями, следить за прогрессом друг друга, напоминать друг другу («пнуть друга») о выполнении привычки, устраивать челленджи между друзьями по тем или иным привычкам, добавлять фото‑подтверждения, комментарии, и для интереса — геймификация (очки/уровни/достижения).

Ок, вызов принят!

Я взял это как продуктовую гипотезу:

1. Собрать базу: личные привычки, статистика, напоминания, прогресс.
2. Добавить social‑слой:
— друзья и групповые привычки;
— челленджи;
— «пнуть друга»;
— фото‑подтверждения + комментарии;
— геймификацию (очки/уровни/достижения).
3. Сделать так, чтобы это работало при плохой сети и в офлайне.

‑-

## Что в итоге есть в продукте

— личные и групповые привычки;
— инвайты в привычки и челленджи;
— прогресс по участникам в группах;
— фото‑пруфы выполнения;
— статистика (день/неделя/год), рейтинг друзей;
— виджет домашнего экрана списка привычек «На сегодня»;
— офлайн‑режим с последующей синхронизацией.

Splash-экран приложения Habit Tracker
Splash‑экран приложения Habit Tracker
Главный экран «Мои привычки» (тёмная тема)
Главный экран «Мои привычки» (тёмная тема)
Экран деталей групповой привычки с прогрессом участников
Экран деталей групповой привычки с прогрессом участников
Экран статистики: уровни, активность и рейтинг друзей (тёмная тема)
Экран статистики: уровни, активность и рейтинг друзей (тёмная тема)

‑-
## Стек и архитектура (без магии)
Стек:

Kotlin, Coroutines, Flow
Jetpack Compose, Navigation Compose
Hilt
Room (локально)
Firebase Auth, Firestore, FCM
WorkManager
Crashlytics, Firebase Performance, Yandex AppMetrica
Слои:

```text
presentation (Compose, ViewModel)
domain (use-cases, модели, интерфейсы)
data (Room/Firestore, sync, repository)
```
Почему так:

- предсказуемые границы ответственности;
- проще тестировать переходы состояний;
- проще локализовывать race conditions в sync-слое.---
## Главный технический узел: синхронизация Room ↔ Firestore

Я прошёл несколько итераций и остановился на схеме Room-first + Outbox + Realtime merge.

### 1) Сначала Room, потом outbox

Любое действие пользователя сразу отражается локально, а затем уходит в очередь синхронизации.

```kotlin
suspend fun enqueueUpsert(
entityType: String,
entityId: String,
payloadJson: String,
updatedAt: Long,
operationId: String = UUID.randomUUID().toString()
) = withContext(Dispatchers.IO) {
outboxDao.insert(
SyncOutboxEntity(
operationId = operationId,
entityType = entityType,
entityId = entityId,
opType = "UPSERT",
payloadJson = payloadJson,
updatedAt = updatedAt
)
)
}
```
### 2) WorkManager + ручной KickSync

Периодический воркер разгружает outbox, но после пользовательских действий я дополнительно «пинаю» синк, чтобы изменения не висели только локально.

```kotlin
override suspend fun doWork(): Result {
return try {
syncManager.drainPending()
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
```
### 3) Pending-guard на входящем realtime

Если безусловно мержить Firestore в Room, можно затирать локальные более свежие изменения.

```kotlin
val hasPending = syncManager.hasPendingOperations("habit_entry", entityId)
if (hasPending) {
continue // не перетираем локальные pending-изменения
}
```
Плюс LWW-логика по времени обновления.
Связка pending guard + LWW убрала заметную часть рассинхронов.---
## Разбор реального бага: «выходной → возобновить → выполнить»

Почему этот кейс часто ломается:

- флаг isRestDay живёт отдельно;
- «возобновить» и «выполнить» приходят как два события;
- клиенты могут зафиксировать промежуточное состояние.

Что пришлось сделать:

1. При выполнении привычки всегда принудительно снимать isRestDay.
2. Нормализовать переходы состояний в domain/data.
3. Не позволять старому remote-снапшоту затирать локальное pending-состояние.Ключевой фрагмент:

```kotlin
val updatedEntry = existingEntry.copy(
completed = true,
completedAt = now,
note = note,
isRestDay = false,
updatedAt = today
)
updateEntry(updatedEntry)
```
После фикса кейс перестал расходиться между участниками (проверял руками на двух клиентах + прогонял тесты).

---
## Soft-delete: почему это не лишняя сложность

Для части сущностей я использую tombstones (isActive=false, deletedAtMillis) вместо немедленного hard delete.

Это закрывает неприятные сценарии:

- одно устройство уже удалило;
- второе долго было офлайн;
- после reconnection данные не должны «воскресать» или теряться.Для social-приложения это стоит дополнительной сложности.

---
## Виджет «Сегодня»: маленькая фича, много нюансов

На виджете важны две вещи:

- очевидный ручной refresh;
- предсказуемый автоrefresh.

Я поменял расписание автообновлений:

- 00:10
- 15:00
- отдельный force refresh в 05:00
```kotlin
schedule(context, ACTION_REFRESH_MIDNIGHT, requestCode = 2001, hour = 0, minute = 10)
schedule(context, ACTION_REFRESH_AFTERNOON, requestCode = 2002, hour = 15, minute = 0)
schedule(context, ACTION_FORCE_REFRESH_MORNING, requestCode = 2003, hour = 5, minute = 0)
```
Плюс добавил явную иконку refresh рядом с заголовком «Сегодня»: тап по заголовку пользователи не считывали как действие.

---
## Наблюдаемость: иначе вы разрабатываете вслепую

Использую:

- Crashlytics для падений и stacktrace;
- AppMetrica для продуктовых событий и ошибок;
- Firebase Performance для перфоманса.

Ошибки отправляются в оба канала:

```kotlin
crashlytics.recordException(throwable)
AppMetrica.reportEvent(AnalyticsEvents.ERROR_OCCURRED, mapOf(...))
AppMetrica.reportError(message ?: "Error", throwable)
```
Текущий срез (RuStore ~1.5 месяца):

— около 100 пользователей;
— 11–14 активных в среднем.

‑-
## Где в этом AI, и почему это не «нагенерил и выкатил»

AI я использовал активно, но как ускоритель:

— быстрые прототипы и дизайн UI/UX;
— для рефакторинга и обновления зависимостей «без конфликтов»;
— генерация тест‑кейсов и вариантов решения;
— ускорение рутинных правок.

Где AI не заменяет разработчика:

— проектирование state‑machine (частично);
— merge/conflict‑правила;
— проверка race conditions;
— финальные архитектурные решения.

Коротко: AI отлично ускоряет реализацию/рефакторинг, после грамотного проектирования архитектуры, а так же помогает быстро поправить ошибки при сборке и выполнить рутинные задачи и тесты, но не принимает за вас ответственные инженерные решения.

Про то какие AI инструменты и модели я использовал, и для каких кейсов, в рамках данной статьи я рассматривать, пожалуй, не буду, так как этот материал заслуживает отдельной статьи. Мне есть что рассказать и чем поделиться на основе полученного опыта во время разработки данного проекта, но это будет уже другая история.
Если вам будет интересно об этом послушать, напишите об этом в комментариях и я постараюсь подготовить для вас следующую статью уже на тему AI инструментов, LLM моделей, и в каких кейсах какие из них лучше всего себя показывали на основе примеров разработки приложения Habit Tracker.

‑-
## Что оказалось самым дорогим по времени

Не экраны и не анимации.
Больше всего времени забрали:

1. консистентность в multi‑user сценариях;
2. офлайн + последующая синхронизация без потери пользовательских действий;
3. UX для опасных операций (сбросы/массовые действия/подтверждения).

Именно здесь проект перестаёт быть «просто pet‑проектом».

‑-
## Что планирую дальше

— держать стабильность релизов;
— отслеживать метрики крашей и ошибок, оперативно исправляя их;
— развивать social‑механики и челленджи;
— расширять аналитику поведения;
— дорабатывать виджетные сценарии (по необходимости);
— аккуратно внедрять монетизацию: рекламу/платные фичи.

‑-
## Вывод

Сделать трекер привычек — несложно. Сложно сделать его социальным, офлайн‑устойчивым и консистентным.

И второй вывод:

**AI — это не «вместо инженера», а «вместе с инженером».** Если критическое мышление не выключено, можно сильно ускориться без потери качества.

Если вам будет интересно, в следующей статье я могу сделать более узкий deep dive по sync‑слою: схема потоков, конфликтные кейсы и тесты на них. Пишите в комментариях — о чём бы вы хотели, что бы я по подробнее описал по технической части из функционала и фичей этого проекта.

‑-

## Материалы‑ Приложение в RuStore: [HabitTracker](https://www.rustore.ru/catalog/app/com.habittracker)

Источник

Возможности рынка
Логотип Helium Mobile
Helium Mobile Курс (MOBILE)
$0.0001734
$0.0001734$0.0001734
-1.64%
USD
График цены Helium Mobile (MOBILE) в реальном времени
Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу crypto.news@mexc.com для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.

Вам также может быть интересно