android-sdd 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/dist/index.js +143 -0
  2. package/package.json +27 -0
  3. package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
  4. package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
  5. package/skills/Android Platform/Configuration/SKILL.md +201 -0
  6. package/skills/Android Platform/Filesystem/SKILL.md +216 -0
  7. package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
  8. package/skills/Android Platform/Manifest/SKILL.md +226 -0
  9. package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
  10. package/skills/Android Platform/Resources/SKILL.md +234 -0
  11. package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
  12. package/skills/Android Platform/State Restoration/SKILL.md +210 -0
  13. package/skills/Architecture/Bounded Context/SKILL.md +207 -0
  14. package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
  15. package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
  16. package/skills/Architecture/Entity Design/SKILL.md +243 -0
  17. package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
  18. package/skills/Architecture/MVI/SKILL.md +224 -0
  19. package/skills/Architecture/MVVM/SKILL.md +198 -0
  20. package/skills/Architecture/Modularization/SKILL.md +194 -0
  21. package/skills/Architecture/Offline First/SKILL.md +249 -0
  22. package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
  23. package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
  24. package/skills/Architecture/State Management/SKILL.md +229 -0
  25. package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
  26. package/skills/Architecture/Use Case Design/SKILL.md +244 -0
  27. package/skills/Architecture/Value Object/SKILL.md +226 -0
  28. package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
  29. package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
  30. package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
  31. package/skills/Build System/Build Cache/SKILL.md +233 -0
  32. package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
  33. package/skills/Build System/Build Variant/SKILL.md +215 -0
  34. package/skills/Build System/Convention Plugin/SKILL.md +288 -0
  35. package/skills/Build System/Dependency Management/SKILL.md +261 -0
  36. package/skills/Build System/Gradle/SKILL.md +284 -0
  37. package/skills/Build System/Incremental Build/SKILL.md +199 -0
  38. package/skills/Build System/KAPT/SKILL.md +198 -0
  39. package/skills/Build System/KSP/SKILL.md +263 -0
  40. package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
  41. package/skills/Build System/Specialized/C++/SKILL.md +308 -0
  42. package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
  43. package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
  44. package/skills/Build System/Version Catalog/SKILL.md +304 -0
  45. package/skills/Concurrency/Background Processing/SKILL.md +185 -0
  46. package/skills/Concurrency/Channel/SKILL.md +207 -0
  47. package/skills/Concurrency/Coroutine/SKILL.md +200 -0
  48. package/skills/Concurrency/Flow/SKILL.md +179 -0
  49. package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
  50. package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
  51. package/skills/Concurrency/StateFlow/SKILL.md +175 -0
  52. package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
  53. package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
  54. package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
  55. package/skills/Core Language/DSL/SKILL.md +186 -0
  56. package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
  57. package/skills/Core Language/Immutability/SKILL.md +156 -0
  58. package/skills/Core Language/KMP/SKILL.md +182 -0
  59. package/skills/Core Language/Kotlin/SKILL.md +187 -0
  60. package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
  61. package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
  62. package/skills/Core Language/Serialization/SKILL.md +191 -0
  63. package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
  64. package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
  65. package/skills/Data Layer/DAO/SKILL.md +225 -0
  66. package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
  67. package/skills/Data Layer/DataStore/SKILL.md +264 -0
  68. package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
  69. package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
  70. package/skills/Data Layer/File Storage/SKILL.md +247 -0
  71. package/skills/Data Layer/Indexing/SKILL.md +184 -0
  72. package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
  73. package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
  74. package/skills/Data Layer/Migration/SKILL.md +243 -0
  75. package/skills/Data Layer/Paging/SKILL.md +264 -0
  76. package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
  77. package/skills/Data Layer/Room/SKILL.md +244 -0
  78. package/skills/Data Layer/SQLite/SKILL.md +255 -0
  79. package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
  80. package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
  81. package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
  82. package/skills/Dependency Injection/Koin/SKILL.md +282 -0
  83. package/skills/Developer Experience/Detekt/SKILL.md +272 -0
  84. package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
  85. package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
  86. package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
  87. package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
  88. package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
  89. package/skills/Media/Audio/SKILL.md +257 -0
  90. package/skills/Media/Camera/SKILL.md +229 -0
  91. package/skills/Media/CameraX/SKILL.md +295 -0
  92. package/skills/Media/ExoPlayer/SKILL.md +258 -0
  93. package/skills/Media/Video/SKILL.md +228 -0
  94. package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
  95. package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
  96. package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
  97. package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
  98. package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
  99. package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
  100. package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
  101. package/skills/Navigation/Navigation/SKILL.md +215 -0
  102. package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
  103. package/skills/Networking/API Contract/SKILL.md +220 -0
  104. package/skills/Networking/Authentication/SKILL.md +210 -0
  105. package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
  106. package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
  107. package/skills/Networking/Ktor/SKILL.md +219 -0
  108. package/skills/Networking/Multipart Upload/SKILL.md +213 -0
  109. package/skills/Networking/OkHttp/SKILL.md +193 -0
  110. package/skills/Networking/REST/SKILL.md +178 -0
  111. package/skills/Networking/Rate Limiting/SKILL.md +170 -0
  112. package/skills/Networking/Retrofit/SKILL.md +241 -0
  113. package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
  114. package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
  115. package/skills/Networking/WebSocket/SKILL.md +224 -0
  116. package/skills/Observability/Crash Reporting/SKILL.md +219 -0
  117. package/skills/Observability/Logging/SKILL.md +168 -0
  118. package/skills/Observability/Metrics/SKILL.md +227 -0
  119. package/skills/Observability/Structured Logging/SKILL.md +234 -0
  120. package/skills/Performance/ANR Prevention/SKILL.md +192 -0
  121. package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
  122. package/skills/Performance/App Startup/SKILL.md +183 -0
  123. package/skills/Performance/Baseline Profile/SKILL.md +205 -0
  124. package/skills/Performance/Battery Optimization/SKILL.md +192 -0
  125. package/skills/Performance/Benchmark/SKILL.md +182 -0
  126. package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
  127. package/skills/Performance/Compose Optimization/SKILL.md +187 -0
  128. package/skills/Performance/Heap Management/SKILL.md +184 -0
  129. package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
  130. package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
  131. package/skills/Performance/Rendering Performance/SKILL.md +205 -0
  132. package/skills/Performance/Startup Optimization/SKILL.md +219 -0
  133. package/skills/Security/Biometric/SKILL.md +224 -0
  134. package/skills/Security/Certificate Transparency/SKILL.md +158 -0
  135. package/skills/Security/Cryptography/SKILL.md +244 -0
  136. package/skills/Security/Encrypted Storage/SKILL.md +273 -0
  137. package/skills/Security/Frida Detection/SKILL.md +230 -0
  138. package/skills/Security/Hook Detection/SKILL.md +197 -0
  139. package/skills/Security/Keystore/SKILL.md +272 -0
  140. package/skills/Security/Network Security Config/SKILL.md +186 -0
  141. package/skills/Security/Obfuscation/SKILL.md +226 -0
  142. package/skills/Security/Proguard/SKILL.md +202 -0
  143. package/skills/Security/R8/SKILL.md +234 -0
  144. package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
  145. package/skills/Security/Root Detection/SKILL.md +220 -0
  146. package/skills/Security/Secure Networking/SKILL.md +220 -0
  147. package/skills/System Integration/AlarmManager/SKILL.md +182 -0
  148. package/skills/System Integration/App Widget/SKILL.md +182 -0
  149. package/skills/System Integration/Deep Link/SKILL.md +187 -0
  150. package/skills/System Integration/Foreground Service/SKILL.md +212 -0
  151. package/skills/System Integration/Notification/SKILL.md +237 -0
  152. package/skills/System Integration/WorkManager/SKILL.md +256 -0
  153. package/skills/System Integration/clipboard/SKILL.md +155 -0
  154. package/skills/System Integration/share-intent/SKILL.md +182 -0
  155. package/skills/Testing/Compose Testing/SKILL.md +296 -0
  156. package/skills/Testing/Espresso/SKILL.md +292 -0
  157. package/skills/Testing/Fake Data/SKILL.md +245 -0
  158. package/skills/Testing/Integration Testing/SKILL.md +288 -0
  159. package/skills/Testing/Mocking/SKILL.md +229 -0
  160. package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
  161. package/skills/Testing/UI Testing/SKILL.md +293 -0
  162. package/skills/Testing/Unit Testing/SKILL.md +309 -0
  163. package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
  164. package/skills/UI System/Compose/SKILL.md +296 -0
  165. package/skills/UI System/Compose Animation/SKILL.md +281 -0
  166. package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
  167. package/skills/UI System/Compose Navigation/SKILL.md +255 -0
  168. package/skills/UI System/Compose Performance/SKILL.md +274 -0
  169. package/skills/UI System/Design System/SKILL.md +217 -0
  170. package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
  171. package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
  172. package/skills/UI System/Loading Strategy/SKILL.md +254 -0
  173. package/skills/UI System/Material 3/SKILL.md +279 -0
  174. package/skills/UI System/RTL/SKILL.md +179 -0
  175. package/src/index.ts +182 -0
  176. package/tsconfig.json +19 -0
@@ -0,0 +1,240 @@
1
+ ---
2
+ name: merge-strategy
3
+ description: >
4
+ Merge strategies for combining local and remote data changes in Android.
5
+ Load this skill when syncing collections, merging list changes,
6
+ handling additions/deletions on both sides, or implementing CRDT-like patterns.
7
+ ---
8
+
9
+ # Merge Strategy
10
+
11
+ ## Overview
12
+
13
+ A merge strategy defines how to combine two diverged versions of a dataset into a single consistent result. This is more complex than single-record conflict resolution — it handles collections where both sides may have added, modified, and deleted different items.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Merging requires knowledge of the **base state** — the last known common version
20
+ - Track **deletions explicitly** — a missing item could mean "deleted" or "not yet synced"
21
+ - Use **stable IDs** as the merge key — never use position or timestamp alone
22
+ - Make merge operations **idempotent** — running merge twice yields the same result
23
+ - Prefer **additive merges** where possible — deletions are the hardest case
24
+
25
+ ---
26
+
27
+ ## Collection Merge Patterns
28
+
29
+ ### Pattern 1: Remote-Authoritative List Merge
30
+
31
+ ```kotlin
32
+ // ✅ Server is source of truth for the list — local additions queued for push
33
+ fun mergeUserList(
34
+ localItems: List<UserEntity>,
35
+ remoteItems: List<UserDto>,
36
+ pendingLocalCreates: List<UserEntity>
37
+ ): List<UserEntity> {
38
+ val remoteById = remoteItems.associateBy { it.id }
39
+ val localById = localItems.associateBy { it.id }
40
+
41
+ val merged = mutableListOf<UserEntity>()
42
+
43
+ // Add all remote items (authoritative)
44
+ remoteItems.forEach { remote ->
45
+ val local = localById[remote.id]
46
+ merged.add(
47
+ if (local != null && local.updatedAt > remote.updatedAt) {
48
+ local // local is newer — keep, will push
49
+ } else {
50
+ remote.toEntity() // remote wins
51
+ }
52
+ )
53
+ }
54
+
55
+ // Add local-only creates (not yet on server)
56
+ pendingLocalCreates
57
+ .filter { it.id !in remoteById }
58
+ .forEach { merged.add(it) }
59
+
60
+ return merged
61
+ }
62
+ ```
63
+
64
+ ---
65
+
66
+ ### Pattern 2: Three-Way Collection Merge
67
+
68
+ ```kotlin
69
+ // ✅ Three-way merge — base + local + remote
70
+ data class CollectionDiff<T>(
71
+ val added: List<T>,
72
+ val modified: List<T>,
73
+ val deleted: List<String> // IDs
74
+ )
75
+
76
+ fun <T : HasId> threeWayMerge(
77
+ base: List<T>,
78
+ local: List<T>,
79
+ remote: List<T>
80
+ ): List<T> {
81
+ val baseIds = base.map { it.id }.toSet()
82
+ val localIds = local.map { it.id }.toSet()
83
+ val remoteIds = remote.map { it.id }.toSet()
84
+
85
+ val baseById = base.associateBy { it.id }
86
+ val localById = local.associateBy { it.id }
87
+ val remoteById = remote.associateBy { it.id }
88
+
89
+ val result = mutableListOf<T>()
90
+
91
+ // Items present in remote
92
+ remoteIds.forEach { id ->
93
+ val remoteItem = remoteById[id]!!
94
+ val localItem = localById[id]
95
+ val baseItem = baseById[id]
96
+
97
+ when {
98
+ // New on remote — add
99
+ id !in baseIds -> result.add(remoteItem)
100
+
101
+ // Deleted locally — don't add (local delete wins)
102
+ id !in localIds -> { /* skip — locally deleted */ }
103
+
104
+ // Modified on both sides — resolve conflict
105
+ localItem != baseItem && remoteItem != baseItem ->
106
+ result.add(resolveFieldConflict(localItem!!, remoteItem))
107
+
108
+ // Only local changed
109
+ localItem != baseItem -> result.add(localItem!!)
110
+
111
+ // Only remote changed, or neither changed
112
+ else -> result.add(remoteItem)
113
+ }
114
+ }
115
+
116
+ // Items added locally (not on remote or base)
117
+ localIds
118
+ .filter { it !in baseIds && it !in remoteIds }
119
+ .forEach { result.add(localById[it]!!) }
120
+
121
+ return result
122
+ }
123
+
124
+ interface HasId {
125
+ val id: String
126
+ }
127
+ ```
128
+
129
+ ---
130
+
131
+ ### Pattern 3: Additive-Only Merge (No Deletions)
132
+
133
+ ```kotlin
134
+ // ✅ Simplest merge — items are never deleted, only added
135
+ // Use for: event logs, audit trails, append-only feeds
136
+
137
+ fun <T : HasId> additiveMerge(
138
+ local: List<T>,
139
+ remote: List<T>
140
+ ): List<T> {
141
+ val localById = local.associateBy { it.id }
142
+ val remoteById = remote.associateBy { it.id }
143
+
144
+ // Union of both sets — remote wins on conflict
145
+ return (localById + remoteById).values.toList()
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ### Pattern 4: Timestamp-Based List Merge
152
+
153
+ ```kotlin
154
+ // ✅ Each item has a timestamp — most recent version of each ID wins
155
+ data class TimestampedItem<T>(val id: String, val data: T, val updatedAt: Long)
156
+
157
+ fun <T> timestampMerge(
158
+ local: List<TimestampedItem<T>>,
159
+ remote: List<TimestampedItem<T>>,
160
+ deletedRemoteIds: Set<String> = emptySet()
161
+ ): List<TimestampedItem<T>> {
162
+ val merged = (local + remote)
163
+ .groupBy { it.id }
164
+ .mapValues { (_, versions) -> versions.maxByOrNull { it.updatedAt }!! }
165
+ .values
166
+ .filter { it.id !in deletedRemoteIds }
167
+ .toList()
168
+
169
+ return merged
170
+ }
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Tracking Deletions
176
+
177
+ ```kotlin
178
+ // ✅ Server must provide a tombstone/deleted list
179
+ // Without this, you can't distinguish "deleted" from "not synced yet"
180
+
181
+ @Serializable
182
+ data class SyncResponseDto(
183
+ val updated: List<UserDto>,
184
+ val deletedIds: List<String>, // explicit deletions
185
+ val syncToken: String // cursor for next delta sync
186
+ )
187
+
188
+ // ✅ Apply deletions explicitly
189
+ suspend fun applySync(response: SyncResponseDto) {
190
+ db.withTransaction {
191
+ // Apply updates/additions
192
+ userDao.upsertAll(response.updated.map { it.toEntity() })
193
+ // Apply deletions
194
+ userDao.deleteByIds(response.deletedIds)
195
+ // Save sync cursor
196
+ prefs.saveSyncToken(response.syncToken)
197
+ }
198
+ }
199
+ ```
200
+
201
+ ---
202
+
203
+ ## CRDT-Inspired Patterns
204
+
205
+ ```kotlin
206
+ // ✅ Grow-only Set (G-Set) — items can only be added, never removed
207
+ // Use for: tags, labels, read receipts
208
+
209
+ class GrowOnlySet<T>(private val items: MutableSet<T> = mutableSetOf()) {
210
+ fun add(item: T) { items.add(item) }
211
+ fun merge(other: GrowOnlySet<T>): GrowOnlySet<T> =
212
+ GrowOnlySet((items + other.items).toMutableSet())
213
+ fun contains(item: T) = items.contains(item)
214
+ }
215
+
216
+ // ✅ Last-Write-Wins Register (LWW-Register) — per-field versioning
217
+ data class LWWRegister<T>(val value: T, val timestamp: Long) {
218
+ fun merge(other: LWWRegister<T>): LWWRegister<T> =
219
+ if (other.timestamp > timestamp) other else this
220
+ }
221
+ ```
222
+
223
+ ---
224
+
225
+ ## Anti-Patterns
226
+
227
+ - Replacing the entire local list with remote on every sync — loses local changes
228
+ - No tombstones for deletions — deleted items reappear after sync
229
+ - Using list position as merge key — positions shift, wrong items merged
230
+ - Merging without a base state — can't distinguish add from conflict
231
+ - Merging on the main thread — use Dispatchers.IO for large collections
232
+
233
+ ---
234
+
235
+ ## Related Skills
236
+
237
+ - `conflict-resolution` — single-record conflict handling
238
+ - `sync-engine` — orchestrating full sync with merge
239
+ - `room` — persisting merged results
240
+ - `dto-mapping` — mapping remote items for merge
@@ -0,0 +1,243 @@
1
+ ---
2
+ name: migration
3
+ description: >
4
+ Room database migration — writing, testing, and managing schema changes.
5
+ Load this skill when changing the database schema (add/remove column or table,
6
+ change type), writing Migration objects, or testing migrations.
7
+ ---
8
+
9
+ # Migration
10
+
11
+ ## Overview
12
+ A migration defines how to transform the database schema from one version to the next without losing user data. Room requires a migration path for every version increment. Missing migrations cause a crash at startup — Room detects the schema mismatch and throws an `IllegalStateException`.
13
+
14
+ ---
15
+
16
+ ## Core Principles
17
+
18
+ - **Never use `fallbackToDestructiveMigration()`** in production — it deletes all user data
19
+ - Always export schema (`exportSchema = true`) — required for writing correct migrations
20
+ - Write a migration for **every version increment** — even if only one table changed
21
+ - **Test every migration** — automated migration tests catch silent data corruption
22
+ - Keep migration SQL simple — complex logic belongs in a one-time data migration job
23
+
24
+ ---
25
+
26
+ ## Version Increment Workflow
27
+
28
+ ```
29
+ 1. Change @Entity — add/remove/rename column or table
30
+ 2. Increment version in @Database
31
+ 3. Write Migration(oldVersion, newVersion)
32
+ 4. Add migration to RoomDatabase builder
33
+ 5. Export schema and commit schemas/ directory
34
+ 6. Write migration test
35
+ ```
36
+
37
+ ---
38
+
39
+ ## Writing Migrations
40
+
41
+ ```kotlin
42
+ // ✅ Add a column
43
+ val MIGRATION_1_2 = object : Migration(1, 2) {
44
+ override fun migrate(database: SupportSQLiteDatabase) {
45
+ database.execSQL(
46
+ "ALTER TABLE users ADD COLUMN phone_number TEXT"
47
+ )
48
+ }
49
+ }
50
+
51
+ // ✅ Add a column with default value
52
+ val MIGRATION_2_3 = object : Migration(2, 3) {
53
+ override fun migrate(database: SupportSQLiteDatabase) {
54
+ database.execSQL(
55
+ "ALTER TABLE users ADD COLUMN status TEXT NOT NULL DEFAULT 'active'"
56
+ )
57
+ }
58
+ }
59
+
60
+ // ✅ Add a new table
61
+ val MIGRATION_3_4 = object : Migration(3, 4) {
62
+ override fun migrate(database: SupportSQLiteDatabase) {
63
+ database.execSQL("""
64
+ CREATE TABLE IF NOT EXISTS orders (
65
+ id TEXT NOT NULL PRIMARY KEY,
66
+ user_id TEXT NOT NULL,
67
+ total INTEGER NOT NULL,
68
+ created_at INTEGER NOT NULL,
69
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
70
+ )
71
+ """)
72
+ database.execSQL(
73
+ "CREATE INDEX IF NOT EXISTS index_orders_user_id ON orders(user_id)"
74
+ )
75
+ }
76
+ }
77
+
78
+ // ✅ Rename a column (SQLite doesn't support RENAME COLUMN before API 29)
79
+ val MIGRATION_4_5 = object : Migration(4, 5) {
80
+ override fun migrate(database: SupportSQLiteDatabase) {
81
+ // Create new table with correct schema
82
+ database.execSQL("""
83
+ CREATE TABLE users_new (
84
+ id TEXT NOT NULL PRIMARY KEY,
85
+ full_name TEXT NOT NULL,
86
+ email TEXT NOT NULL
87
+ )
88
+ """)
89
+ // Copy data
90
+ database.execSQL("""
91
+ INSERT INTO users_new (id, full_name, email)
92
+ SELECT id, name, email FROM users
93
+ """)
94
+ // Drop old table
95
+ database.execSQL("DROP TABLE users")
96
+ // Rename new table
97
+ database.execSQL("ALTER TABLE users_new RENAME TO users")
98
+ }
99
+ }
100
+
101
+ // ✅ Drop a column (same approach — recreate table)
102
+ val MIGRATION_5_6 = object : Migration(5, 6) {
103
+ override fun migrate(database: SupportSQLiteDatabase) {
104
+ database.execSQL("""
105
+ CREATE TABLE users_new (
106
+ id TEXT NOT NULL PRIMARY KEY,
107
+ full_name TEXT NOT NULL
108
+ -- removed: phone_number
109
+ )
110
+ """)
111
+ database.execSQL("INSERT INTO users_new SELECT id, full_name FROM users")
112
+ database.execSQL("DROP TABLE users")
113
+ database.execSQL("ALTER TABLE users_new RENAME TO users")
114
+ }
115
+ }
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Registering Migrations
121
+
122
+ ```kotlin
123
+ // ✅ Register all migrations in database builder
124
+ @Module
125
+ @InstallIn(SingletonComponent::class)
126
+ object DatabaseModule {
127
+
128
+ @Provides
129
+ @Singleton
130
+ fun provideDatabase(@ApplicationContext context: Context): AppDatabase =
131
+ Room.databaseBuilder(context, AppDatabase::class.java, "app_database")
132
+ .addMigrations(
133
+ MIGRATION_1_2,
134
+ MIGRATION_2_3,
135
+ MIGRATION_3_4,
136
+ MIGRATION_4_5,
137
+ MIGRATION_5_6
138
+ )
139
+ .build()
140
+ }
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Auto Migration (Room 2.4+)
146
+
147
+ ```kotlin
148
+ // ✅ For simple schema changes — Room generates migration automatically
149
+ @Database(
150
+ entities = [UserEntity::class],
151
+ version = 3,
152
+ exportSchema = true,
153
+ autoMigrations = [
154
+ AutoMigration(from = 1, to = 2), // simple add column — auto-generated
155
+ AutoMigration(from = 2, to = 3, spec = Migration2To3::class) // with rename
156
+ ]
157
+ )
158
+ abstract class AppDatabase : RoomDatabase()
159
+
160
+ // ✅ AutoMigrationSpec — for rename/delete (needs disambiguation)
161
+ @RenameColumn(tableName = "users", fromColumnName = "name", toColumnName = "full_name")
162
+ class Migration2To3 : AutoMigrationSpec
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Testing Migrations
168
+
169
+ ```kotlin
170
+ // ✅ MigrationTestHelper — test each migration
171
+ @RunWith(AndroidJUnit4::class)
172
+ class MigrationTest {
173
+
174
+ @get:Rule
175
+ val helper = MigrationTestHelper(
176
+ InstrumentationRegistry.getInstrumentation(),
177
+ AppDatabase::class.java
178
+ )
179
+
180
+ @Test
181
+ fun migrate1To2() {
182
+ // Create version 1 database
183
+ helper.createDatabase(TEST_DB, 1).apply {
184
+ execSQL("INSERT INTO users VALUES ('1', 'Ali')")
185
+ close()
186
+ }
187
+
188
+ // Run migration
189
+ val db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2)
190
+
191
+ // Verify data integrity
192
+ val cursor = db.query("SELECT * FROM users WHERE id = '1'")
193
+ assertTrue(cursor.moveToFirst())
194
+ assertEquals("Ali", cursor.getString(cursor.getColumnIndex("full_name")))
195
+ // new column exists with default value
196
+ assertNotNull(cursor.getString(cursor.getColumnIndex("phone_number")))
197
+ cursor.close()
198
+ }
199
+
200
+ companion object {
201
+ private const val TEST_DB = "migration_test"
202
+ }
203
+ }
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Multi-Step Migration Path
209
+
210
+ ```kotlin
211
+ // ✅ Room applies migrations sequentially — 1→2→3→4 not 1→4
212
+ // All intermediate migrations must be present
213
+
214
+ // If user has version 1, Room applies:
215
+ // MIGRATION_1_2, then MIGRATION_2_3, then MIGRATION_3_4
216
+
217
+ // ✅ Multi-version single migration (skip versions)
218
+ val MIGRATION_1_4 = object : Migration(1, 4) {
219
+ override fun migrate(database: SupportSQLiteDatabase) {
220
+ // Apply all changes from 1→4 in one step
221
+ // Only needed if you want to optimize multi-version upgrades
222
+ }
223
+ }
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Anti-Patterns
229
+
230
+ - `fallbackToDestructiveMigration()` in production — deletes all user data
231
+ - Not exporting schema (`exportSchema = false`) — can't write correct migrations
232
+ - Running data transformations in migration SQL — use a one-time WorkManager job instead
233
+ - Not committing `schemas/` directory to version control — migration tests require it
234
+ - Incrementing version without writing migration — crashes on existing installs
235
+ - Writing migration without a test — silent data corruption risk
236
+
237
+ ---
238
+
239
+ ## Related Skills
240
+ - `room` — Room database setup
241
+ - `dao` — DAO queries after schema changes
242
+ - `database-versioning-strategy` — when and how to version the schema
243
+ - `workmanager` — one-time data migration jobs after schema change