You can use Codex effectively in a Kotlin and Android project by starting codex in the repository root, giving it precise tasks, and letting it run Gradle tests and lint after each change. See Codex for the Codex CLI and How to use Codex CLI: a step-by-step guide for an end-to-end walkthrough. Treat it like a fast junior engineer who knows Kotlin, Compose, and Android conventions but needs an explicit project map and strict verification.
Use Codex where Android development is already structured
Codex suits Kotlin and Android because the work is heavily structured by Gradle, the Android Gradle plugin, manifest rules, and Compose’s declarative UI. The agent can read build.gradle.kts files, version catalogs, and source sets, then make changes that line up with existing conventions. It is also useful for the repetitive parts of Android development: adding a feature screen, converting a ViewModel to StateFlow, writing unit tests with kotlinx-coroutines-test, and fixing lint issues. The catch is that Android builds are slow and the verification loop must be explicit; Codex will not reliably know when to run an emulator test versus a JVM test unless AGENTS.md says so.
Make AGENTS.md the operating manual for the repo
Put an AGENTS.md at the repo root. Keep it short and specific: build commands, test commands, lint commands, folder conventions, and rules that prevent common Android and Kotlin mistakes. Codex reads this file as instructions before acting. Example:
# AGENTS.md
## Commands
- Build debug APK: ./gradlew assembleDebug
- Run JVM unit tests: ./gradlew testDebugUnitTest
- Run instrumented tests: ./gradlew connectedDebugAndroidTest
- Run lint: ./gradlew lintDebug
## Conventions
- Each feature lives in `feature/<name>/` with `ui/`, `data/`, and `domain/` packages.
- Compose screens go in `feature/<name>/ui/`; state hoisting and event callbacks are preferred.
- ViewModels expose `StateFlow<UiState>`, not `LiveData`.
- Use `collectAsStateWithLifecycle()` in composables that observe ViewModel state.
- Use Material 3 components and theme tokens; do not add new color literals.
## Rules
- Never use `GlobalScope`; use `viewModelScope` and structured concurrency.
- Do not access Android framework classes from ViewModels; use use cases or repositories.
- Keep composables free of network or disk I/O.
- Before finishing, run unit tests and lint and report failures with file:line.
- Do not commit changes outside the requested task unless they are required to compile.
Adjust module names and flavors to your project. If you have multiple Gradle modules, add the command for each affected module or specify the module path in AGENTS.md.
Plan small, verify each step, review the diff
Use a plan, small steps, run tests, review diff. Start with an explicit prompt that names files, expected behavior, and verification command. Ask for one change at a time and require tests only when they add signal. After Codex stops, review the diff before running anything; Android diffs can silently change Gradle versions, add unused dependencies, or modify Compose previews. Then run ./gradlew testDebugUnitTest lintDebug yourself or ask Codex to run it and paste the failure.
Example prompts:
- Add a
ProfileScreeninfeature/profile/uithat observesProfileViewModel.profileState: StateFlow<ProfileUiState>withcollectAsStateWithLifecycle, shows loading, error, and content states, and uses existing Material 3 components. Do not change the ViewModel API. - Refactor
HomeViewModelfromLiveDatatoStateFlowwhile keeping the behavior ofHomeViewModelTestintact. Update tests only if the coroutine test setup requires it, and explain the diff. - Write a JUnit test for
CartViewModelwithkotlinx-coroutines-testthat verifiescartTotalupdates when an item quantity changes and when a coupon is applied. UserunTestand aStandardTestDispatcherwhere appropriate. - Fix the failing Espresso test in
feature/checkout/src/androidTest/kotlin/.... Run./gradlew connectedDebugAndroidTestonly if a device or emulator is available; otherwise run the corresponding JVM tests andlintDebugand describe what you changed. - Review
HomeScreenfor unnecessary recomposition in aLazyColumnitem. Suggest concrete fixes such asderivedStateOf, stable keys, or moving state up, and apply the smallest safe change.
Let Codex do boilerplate, but review Android-specific edits
Codex handles well-defined, local tasks such as adding a Compose screen with a ViewModel and state holder, converting a small XML layout or View system screen to Compose, updating version catalog entries, generating unit tests with Turbine or kotlinx-coroutines-test, and writing simple repository or use case code. It also reduces boilerplate around Parcelable, Compose previews, and test tags.
Watch the diff closely in these areas:
- Compose state and side effects: the agent may put side effects inside composition, forget
rememberorderivedStateOf, or pass unstable lambdas intoLazyColumnitems. - Coroutine scopes: it can introduce
GlobalScopeorrunBlockingin production code. RequireviewModelScopeand structured concurrency. - Gradle and dependencies: it may add a dependency directly to
build.gradle.ktsinstead of the version catalog, bump a library version without explanation, or change compileSdk and minSdk unexpectedly. - Android resources and manifest: watch for hardcoded strings, missing resource qualifiers, manifest permissions added without justification, and buildConfig changes.
- Instrumented tests: the agent might write Espresso code that assumes a specific screen state or forgets
IdlingResourcefor coroutines or network calls. - Compose UI tests: it can add unstable test tags or assert on text that varies by locale.
Run unit tests and lint, not just compile
Testing equals verification. Codex can run JVM tests quickly; instrumented tests need a device. For Compose, use createAndroidComposeRule and test tags. Ask Codex to add test tags if they are missing. After changes, run unit tests, lint, and an assemble task. If you want another model to catch mistakes after an agent stops, see AI code review: check what your agents write.
| Command | What it catches | When to run |
|---|---|---|
./gradlew testDebugUnitTest | JVM unit tests, ViewModel logic, coroutines | After every logic change |
./gradlew lintDebug | Android lint issues, resource errors, API misuse | After layout, manifest, or resource changes |
./gradlew assembleDebug | Compilation, manifest merger, dependency resolution | Before marking a task done |
./gradlew connectedDebugAndroidTest | Espresso or Compose UI tests on an emulator or device | Only when UI behavior changed and a device is available |
For unit tests, prefer runTest and a StandardTestDispatcher so suspending ViewModel logic is deterministic. For Compose UI tests, use stable test tags such as Modifier.testTag("checkout_button") instead of matching by text. Codex should report the exact failing command and include the test report path under build/reports/tests/ when a test fails.
Parallelize without overwriting files using AgentVera
AgentVera suits a Kotlin and Android repo because several agents can work in parallel, each in its own git worktree, so a Compose feature and a data layer refactor do not overwrite files. After an agent’s turn, a second model reviews its code and links findings to file:line, which catches Android-specific issues such as GlobalScope leaks or API changes. Checkpoints let you undo a turn and replay it, useful when a Gradle or manifest edit breaks the build.
Keep the loop tight
Codex becomes a reliable pairing tool for Kotlin and Android when the project is explicit: AGENTS.md defines the build and test commands, prompts are small and file-oriented, and every change is verified with Gradle tests and lint. Review diffs for Compose and coroutine details, keep UI tests on a real device or emulator when they matter, and let the agent work in parallel only when the repo structure and worktrees keep modules isolated. With that discipline, Codex handles much of the repetitive Android work without turning the codebase into generated noise.