Best practices to get production-quality code from AI assistants — especially Gemini. Built for Android/Kotlin developers.
#Gemini#Android#Kotlin#PromptEngineering
// 01Role & Context
01
Assign a role before anything else
Start every prompt by telling the AI who it is. A well-defined role activates the right "mode" — a senior engineer produces different output than a generic assistant. Be specific: include seniority, stack, and project context.
✅ Example
You are a senior Android engineer with expertise in Kotlin, Jetpack Compose, Room, and Hilt DI. You are maintaining a production app called SafePlan. You write clean, minimal, well-structured code and never introduce regressions.
02
Provide full project context upfront
AI has no memory of previous sessions. At the start of any new conversation, paste your architecture summary, tech stack, file tree, and key constraints. This prevents the AI from suggesting incompatible patterns or libraries.
✅ Example
Project: SafePlan (Android, Kotlin, minSdk 28)
Architecture: MVVM + Repository + Hilt + Room + Coroutines/Flow
UI: Jetpack Compose + Material 3 + Navigation-Compose
Key constraints: offline-first, no cloud sync, all strings localized (EN + FR)
// 02Output Control
03
Always demand full file output
Never accept partial files or "here's the relevant section" snippets. Partial output forces you to manually merge code, which causes bugs. Explicitly require complete files every time.
✅ Template to include in every prompt
Always output the COMPLETE file content, from first line to last.
Never truncate. Never use "// ... rest of file unchanged".
Begin every file with:
FILE: <relative/path/from/project/root>
✅ Do
Output full GoBagViewModel.kt from line 1 to end, no omissions.
❌ Don't
// ... existing code ... // Only showing changed function
04
Use a consistent file header format
Require a standardized prefix on every file so you can copy-paste directly into your project without hunting for the path.
✅ Template
FILE: app/src/main/java/com/safeplan/ui/gobag/GoBagViewModel.kt
// ── full file content below ──
// 03Change Management
05
Require a "Files Changed" log after every reply
Ask the AI to always end its reply with a structured list of every file it modified and a one-line reason. This makes code review fast and helps you spot unintended changes immediately.
✅ Template to include in every prompt
At the end of every reply, print:
## Files Changed
| File | Reason |
|------|--------|
| path/to/File.kt | One-line justification |
06
Number every request for easy follow-up
Long conversations cause the AI to lose track of which request it is addressing. Ask it to print a numbered request header at the start of every reply. This lets you reference previous requests precisely ("go back to Request #3") without ambiguity.
✅ Template
At the beginning of every reply, print:
## Request #[N] — [one-line summary of what was asked]
Increment N for every new request in this conversation.
07
Ask for a Scope summary before any code
Require the AI to print what it understood and what it will NOT touch before writing a single line of code. This catches misunderstandings early and prevents the AI from "helping" by refactoring things you didn't ask for.
✅ Template
Before writing any code, print:
## Scope
- What I will change: [list]
- What I will NOT touch: [list]
Wait for confirmation if the scope is ambiguous.
// 04Safety & Quality
08
Enforce the minimal diff principle
AI models love to "improve" code beyond what was asked, introducing style changes, renames, and refactors that break things. Explicitly forbid this. Make it a standing rule in every prompt.
✅ Template
RULE — Minimal diff: Only modify code strictly necessary to implement
the requested change. Never refactor, rename, reformat, or touch code
outside the scope of this request. If you feel a refactor would help,
mention it as a comment — do NOT apply it.
✅ Do
Fix only the broken DatePicker state hoisting in AddItemScreen.kt
❌ Don't
I also improved the ViewModel and moved the repository layer to be cleaner
09
Demand regression checks and simulated build
Ask the AI to mentally "compile" its output before presenting it: verify imports, check function signatures match their call sites, confirm navigation routes exist, and ensure no existing feature is broken.
✅ Template
Before outputting any file, perform a simulated build check:
1. Verify all Kotlin imports are correct and present
2. Confirm all string resources exist in both EN and FR
3. Confirm all navigation routes match the NavGraph
4. Confirm Room version is consistent (no missing migrations)
5. Confirm no existing feature is broken by this change
If you find a regression risk, warn explicitly BEFORE the code.
// 05Structure & Decomposition
10
Break large requests into numbered items
Never dump a wall of text. Structure your requests as a numbered list of independent tasks. This forces both you and the AI to think about each change atomically, and lets you skip or reorder items easily.
✅ Example
Implement the following changes:
1. HomeScreen: Add minimap to meeting point cards
2. Go-Bag: Fix broken category dropdown state
3. Settings: Add edit button to contact rows
Process items 1 and 2 first. Wait for confirmation before item 3.
11
Process one group at a time
Large context windows are still limited. Asking for too much at once causes the AI to rush, truncate, or hallucinate. Ask it to process one logical group then pause — giving you control checkpoints.
✅ Template
Process one logical group at a time. After completing each group,
stop and wait for my "continue" or "fix X" before proceeding.
Do not attempt all items in one reply unless I explicitly say "do all".
12
Handle output limits gracefully
If the AI hits its output limit mid-file, it will truncate silently — producing corrupt code. Tell it exactly how to behave when it runs out of space.
✅ Template
If you reach your output limit before finishing:
- Stop at the end of the last COMPLETE file (never mid-file)
- Print exactly: "PAUSED — next file: [path/to/NextFile.kt]"
- Wait for me to say "continue" before resuming
// 06Gemini-Specific Tips
13
Use Gemini 2.0 Pro or 2.5 Pro for code generation
Gemini Flash is fast but cuts corners on code quality. For full Android project generation, always use Gemini 2.0 Pro or 2.5 Pro. The difference in code coherence across a multi-file project is significant.
✅ Gemini 2.5 Pro✅ Gemini 2.0 Pro⚠️ Flash (avoid for full projects)
14
Paste the file tree at the start of follow-up sessions
Gemini has no memory between sessions. At the start of a follow-up conversation, always paste the current file tree and a 5-line architecture summary. Without this, it will invent a different architecture and produce incompatible code.
✅ Template for follow-up sessions
Continuing work on SafePlan. Current file tree:
app/src/main/java/com/safeplan/
├── data/
│ ├── db/AppDatabase.kt
│ ├── repository/GoBagRepository.kt
├── ui/
│ ├── home/HomeScreen.kt
│ └── gobag/GoBagScreen.kt
Do not change the architecture or package structure.
15
Specify exact library versions
Gemini will pick library versions it was trained on, which may be outdated or incompatible with your project. Always specify exact versions for critical dependencies in your prompt.
✅ Example
Use these exact versions (do not change them):
- Room: 2.6.1
- Hilt: 2.51.1
- Compose BOM: 2024.09.00
- WorkManager: 2.9.1
- Coil: 2.7.0
- Maps Compose: 4.3.3
16
Ask for acceptance criteria, not just implementation
Instead of just saying "implement X," define what success looks like. This forces the AI to think about edge cases and produce more complete, testable code.
✅ Example
Implement vault password change.
Acceptance criteria:
- Dialog shows current password, new password, confirm fields
- Wrong current password → show error, do not save
- New password hashed with PBKDF2 + random salt → EncryptedSharedPreferences
- On success: dialog closes, snackbar shows "Password updated" (localized)
- On cancel: no changes to stored password
17
Never say "make it better" or "improve this"
Vague improvement requests are a regression machine. The AI will rewrite things you didn't want changed. Always describe exactly what the current problem is and exactly what the desired behavior should be.
✅ Do
The category DropdownMenu in AddItemScreen.kt does not respond to taps. Fix the state hoisting: move selected category into ViewModel StateFlow, not local remember.
❌ Don't
The Go-Bag screen needs improvement. Make it better and fix the bugs.
18
Include localization rules as a standing constraint
If your app is multilingual, add localization as a non-negotiable rule once and reference it in every follow-up. Without this, the AI will hardcode strings the moment you stop watching.
✅ Template
STANDING RULE — Localization:
Every new user-facing string must be added to:
- res/values/strings.xml (English)
- res/values-fr/strings.xml (French)
Zero hardcoded strings in Kotlin or Compose.
Use plurals (<plurals>) where counts are involved.
// CHEATSHEET
Quick Reference Cheatsheet
Copy this block into every new Gemini session for instant best-practice enforcement.
🎭Start with a role: "You are a senior Android engineer…"
🗂️Paste file tree + stack at start of each session
📄Require full file output with FILE: prefix
📋Require Files Changed table at end of reply
🔢Number each request: Request #N at reply start
🔍Require Scope section before any code
✂️Enforce minimal diff: no unrequested changes
🛡️Require simulated build check before output
⛔Handle output limits: stop cleanly, state next file
🏷️Specify exact library versions
✅Define acceptance criteria per feature
🌍Set localization as a standing rule
⚡ Master prompt header — copy into every session
You are a senior Android engineer maintaining [PROJECT NAME].
Stack: [your stack here]
Architecture: [your architecture here]
STANDING RULES (apply to every reply):
1. Minimal diff: only change code strictly needed. No unrequested refactors.
2. Full files: always output complete files with FILE: <path> header.
3. Files Changed: end every reply with a table of changed files + reason.
4. Request tracking: start every reply with "## Request #N — [summary]"
5. Scope first: before any code, print what you WILL and WON'T touch.
6. Simulated build: verify imports, routes, string resources, Room version.
7. No regressions: warn explicitly if a change risks breaking existing features.
8. Localization: all new strings in values/strings.xml AND values-fr/strings.xml.
9. Output limits: if you hit the limit, stop at end of complete file,
print "PAUSED — next file: [path]" and wait for "continue".