Builder Brigade
Senior Android Engineer · Tapforce · 2024–present
- Kotlin
- Jetpack Compose
- Hilt
- Apollo GraphQL
- Hasura
- Room
- WorkManager
View on Google Play: open play://com.tapforce.builder_brigade
Context
Builder Brigade keeps a home-construction or renovation project organized from concept to completion. The work of a build — guided checklists broken into phases, decisions and the resources behind them, progress posts, moodboards, a community feed — usually scatters across email, spreadsheets, and group chats; Builder Brigade puts it in one place, and exports the finished record to PDF or Excel. It is built for the homeowners, contractors, and DIY builders running those projects.
Approach
Builder Brigade is a single-module app organized by feature: mybuild, community, discover, moodboard, profile, onboarding, and subscription each own their data, domain, and presentation over a shared core. The product is wide — guided checklists, a community feed, moodboards, progress posts, and export are very different problems sharing one app — and the feature seams are what keep that breadth from turning into sprawl. Konsist boundary tests enforce them: core may never import a feature, and feature-to-feature edges live in a curated allowlist.
The backend is Hasura, spoken through Apollo — eighty-eight GraphQL operations, including live queries over WebSocket subscriptions, with a SQLite-backed normalized cache underneath.
Technical decisions
The defining recent work is offline-first. A construction site is the canonical bad-signal environment, so the app now treats the network as optional: Room snapshots capture server state, edits land in a local outbox, and Hilt-injected WorkManager workers drain that outbox — checklist edits, task status, posts, poll votes, comments — replaying them against Hasura when connectivity returns, while Apollo live queries reconcile whatever changed in the meantime.
On screen, a project is a flat set of tasks that has to read the way a build actually unfolds — grouped into phases, in build order. The grouping is derived state, recomputed only when the task list changes, and the sort encodes the real rule: unnamed phases sink to the bottom, the rest order by their order field, ties break alphabetically.
val groupTasks = projectDataState.projectData?.groupTasks.orEmpty()
val groupedTasks by remember(groupTasks) {
derivedStateOf {
groupTasks
.groupBy { it.projectPhase }
.toList()
.sortedWith(
compareBy<Pair<ProjectPhase?, List<GroupTask>>> { it.first == null || it.first?.name?.isBlank() == true }
.thenBy { it.first?.order ?: Int.MAX_VALUE }
.thenBy { it.first?.name?.lowercase(java.util.Locale.ROOT).orEmpty() }
)
.associate { (phase, tasks) -> phase?.name.orEmpty() to tasks }
}
}Outcome
Builder Brigade is a mature, shipped product — version 1.30, a full Play Store listing, and a GitHub Actions + Fastlane pipeline that deploys each release. It grew there over six hundred and seventy-four commits, all but one of them mine, and the feature-based foundation took on a genuinely broad set — checklists, feed, moodboards, export, and now offline-first — without the app losing its shape. For something this wide, holding that shape was the outcome that mattered most.