Review code across five axes: Correctness, Readability, Architecture, Security, and Performance. The approval standard: "Approve when it definitely improves overall code health of the system." Every finding is categorized and actionable.
When to Use
Reviewing a pull request (own or teammate's)
Self-review before creating a PR
Requested code quality check on a specific file or module
After completing a feature (final quality gate)
Skip when: Not reviewing code (this is a review skill, not a writing skill).
Core Process
Step 1: Understand Context
Read the PR description — what problem does it solve?
Read the spec or ticket — does the PR match the stated goal?
Check the diff size — target ~100 lines, flag >1000 lines for splitting
Step 2: Review Tests First
Start with test files — they reveal the intended behavior
Check for:
Are critical paths tested?
Are edge cases covered (null, empty, error, boundary values)?
Do test names describe behavior?
Are tests independent (no shared mutable state)?
Step 3: Five-Axis Review
Axis 1: Correctness
Verify behavior matches intent:
Does the code handle all states? (loading, success, error, empty)
Are nulls handled safely? (no !!, proper ?. chains)
Are coroutines structured correctly? (proper scope, cancellation)
Are lifecycle-aware collections used? (collectAsStateWithLifecycle)
Do Room queries match the schema?
Are migrations correct and tested?
Axis 2: Readability
Kotlin-specific readability:
// GOOD: idiomatic Kotlin
val activeTask = tasks.firstOrNull { !it.completed }
?: return TaskListUiState.Empty
// BAD: Java-style
var activeTask: Task? = null
for (task in tasks) {
if (!task.completed) {
activeTask = task
break
}
}
if (activeTask == null) return TaskListUiState.Empty
Appropriate use of when expressions, let/also/apply, extension functions
Functions under ~40 lines
Sealed classes/interfaces for exhaustive state handling
No nested callbacks (use coroutines)
Axis 3: Architecture
Verify layer boundaries:
UI layer only calls ViewModel (never Repository/DAO directly)
Domain layer has no Android dependencies
Data layer implements domain interfaces
Feature modules don't depend on each other
No business logic in Composables
Check for:
Proper use of @Inject constructor (not field injection)
StateFlow exposed from ViewModel (not MutableStateFlow)
Repository pattern for data access
Single source of truth (local DB for offline-first)
Axis 4: Security
Check for:
No hardcoded secrets, API keys, or passwords
Input validation for user-provided data
Proper intent validation (exported components)
No logging of sensitive data (Log.d with tokens, passwords)
Secure storage (EncryptedSharedPreferences for sensitive data)
See security-and-hardening for comprehensive checklist
Axis 5: Performance
Check for:
N+1 query patterns in Room
Unbounded data loading (should use Paging3 for large datasets)
Unnecessary recompositions in Compose (unstable parameters, lambda allocations)
Heavy work on main thread (use withContext(Dispatchers.IO))
Memory leaks (Activity/Context references in singletons)
See performance-optimization for comprehensive checklist
Step 4: Categorize Findings
Use severity categories:
Category
Description
Action Required
Critical
Security vulnerability, data loss, crash
Must fix before merge
Important
Missing tests, architecture violation, bug risk
Should fix before merge
Suggestion
Better Kotlin idiom, readability improvement
Optional, author's discretion
Nit
Formatting, naming preference
Optional
FYI
Context or explanation, no action needed
Informational
Format findings:
**[Critical]** `TaskRepository.kt:45` — API key hardcoded in source.
Move to `local.properties` and access via `BuildConfig`.
**[Important]** `TaskListViewModel.kt:23` — Uses `GlobalScope.launch`.
Use `viewModelScope.launch` for proper lifecycle management.
**[Suggestion]** `TaskMapper.kt:12` — Could use `copy()` instead
of manual field mapping for partial updates.
Step 5: Verify Build and Tests
Before approving:
./gradlew test passes
./gradlew assembleDebug builds
./gradlew lint has no new warnings
./gradlew detekt passes (if configured)
Common Rationalizations
Shortcut
Why It Fails
"LGTM" without reading the code
Rubber-stamp reviews miss bugs. They also train teammates to skip reviews.
"I'll clean it up later"
Later never comes. Fix it now or create a tracked issue.
"It works, so it's fine"
Working code with poor architecture becomes non-working code during the next change.
"The author knows best"
Fresh eyes catch blind spots. That's the point of review.
"It's just a small change"
Small changes in the wrong layer create architectural debt.
Red Flags
PR over 1000 lines without justification
No tests in the PR
Tests that only cover happy path
!! (non-null assertion) without justification
GlobalScope usage
Mutable state exposed from ViewModel
Business logic in Composables
Feature module depending on another feature module
Secrets or API keys in source code
@Suppress annotations without explanatory comments
Verification
All five axes reviewed (Correctness, Readability, Architecture, Security, Performance)
Tests reviewed first (coverage, edge cases, naming)
1---2name: code-review-and-quality3description: Use when reviewing Android code (own or others'). Five-axis review framework: Correctness, Readability, Architecture, Security, Performance. Categorized findings with Kotlin/Compose-specific checks.4---56# Code Review and Quality78## Overview910Review code across five axes: Correctness, Readability, Architecture, Security, and Performance. The approval standard: "Approve when it definitely improves overall code health of the system." Every finding is categorized and actionable.1112## When to Use1314- Reviewing a pull request (own or teammate's)15- Self-review before creating a PR16- Requested code quality check on a specific file or module17- After completing a feature (final quality gate)1819**Skip when:** Not reviewing code (this is a review skill, not a writing skill).2021## Core Process2223### Step 1: Understand Context24251. **Read the PR description** — what problem does it solve?262. **Read the spec or ticket** — does the PR match the stated goal?273. **Check the diff size** — target ~100 lines, flag >1000 lines for splitting2829### Step 2: Review Tests First30314. **Start with test files** — they reveal the intended behavior325. **Check for:**33 - Are critical paths tested?34 - Are edge cases covered (null, empty, error, boundary values)?35 - Do test names describe behavior?36 - Are tests independent (no shared mutable state)?3738### Step 3: Five-Axis Review3940#### Axis 1: Correctness41426. **Verify behavior matches intent:**43 - Does the code handle all states? (loading, success, error, empty)44 - Are nulls handled safely? (no `!!`, proper `?.` chains)45 - Are coroutines structured correctly? (proper scope, cancellation)46 - Are lifecycle-aware collections used? (`collectAsStateWithLifecycle`)47 - Do Room queries match the schema?48 - Are migrations correct and tested?4950#### Axis 2: Readability51527. **Kotlin-specific readability:**5354```kotlin55// GOOD: idiomatic Kotlin56val activeTask = tasks.firstOrNull { !it.completed }57 ?: return TaskListUiState.Empty5859// BAD: Java-style60var activeTask: Task? = null61for (task in tasks) {62 if (!task.completed) {63 activeTask = task64 break65 }66}67if (activeTask == null) return TaskListUiState.Empty68```69708. **Check for:**71 - Clear naming (functions describe actions, variables describe content)72 - Appropriate use of `when` expressions, `let`/`also`/`apply`, extension functions73 - Functions under ~40 lines74 - Sealed classes/interfaces for exhaustive state handling75 - No nested callbacks (use coroutines)7677#### Axis 3: Architecture78799. **Verify layer boundaries:**80 - UI layer only calls ViewModel (never Repository/DAO directly)81 - Domain layer has no Android dependencies82 - Data layer implements domain interfaces83 - Feature modules don't depend on each other84 - No business logic in Composables858610. **Check for:**87 - Proper use of `@Inject constructor` (not field injection)88 - `StateFlow` exposed from ViewModel (not `MutableStateFlow`)89 - Repository pattern for data access90 - Single source of truth (local DB for offline-first)9192#### Axis 4: Security939411. **Check for:**95 - No hardcoded secrets, API keys, or passwords96 - Input validation for user-provided data97 - Proper intent validation (exported components)98 - No logging of sensitive data (`Log.d` with tokens, passwords)99 - Secure storage (EncryptedSharedPreferences for sensitive data)100 - See `security-and-hardening` for comprehensive checklist101102#### Axis 5: Performance10310412. **Check for:**105 - N+1 query patterns in Room106 - Unbounded data loading (should use Paging3 for large datasets)107 - Unnecessary recompositions in Compose (unstable parameters, lambda allocations)108 - Heavy work on main thread (use `withContext(Dispatchers.IO)`)109 - Memory leaks (Activity/Context references in singletons)110 - See `performance-optimization` for comprehensive checklist111112### Step 4: Categorize Findings11311413. **Use severity categories:**115116| Category | Description | Action Required |117|----------|-------------|----------------|118| **Critical** | Security vulnerability, data loss, crash | Must fix before merge |119| **Important** | Missing tests, architecture violation, bug risk | Should fix before merge |120| **Suggestion** | Better Kotlin idiom, readability improvement | Optional, author's discretion |121| **Nit** | Formatting, naming preference | Optional |122| **FYI** | Context or explanation, no action needed | Informational |12312414. **Format findings:**125```126**[Critical]** `TaskRepository.kt:45` — API key hardcoded in source.127Move to `local.properties` and access via `BuildConfig`.128129**[Important]** `TaskListViewModel.kt:23` — Uses `GlobalScope.launch`.130Use `viewModelScope.launch` for proper lifecycle management.131132**[Suggestion]** `TaskMapper.kt:12` — Could use `copy()` instead133of manual field mapping for partial updates.134```135136### Step 5: Verify Build and Tests13713815. **Before approving:**139 - `./gradlew test` passes140 - `./gradlew assembleDebug` builds141 - `./gradlew lint` has no new warnings142 - `./gradlew detekt` passes (if configured)143144## Common Rationalizations145146| Shortcut | Why It Fails |147|----------|-------------|148| "LGTM" without reading the code | Rubber-stamp reviews miss bugs. They also train teammates to skip reviews. |149| "I'll clean it up later" | Later never comes. Fix it now or create a tracked issue. |150| "It works, so it's fine" | Working code with poor architecture becomes non-working code during the next change. |151| "The author knows best" | Fresh eyes catch blind spots. That's the point of review. |152| "It's just a small change" | Small changes in the wrong layer create architectural debt. |153154## Red Flags155156- PR over 1000 lines without justification157- No tests in the PR158- Tests that only cover happy path159- `!!` (non-null assertion) without justification160- `GlobalScope` usage161- Mutable state exposed from ViewModel162- Business logic in Composables163- Feature module depending on another feature module164- Secrets or API keys in source code165- `@Suppress` annotations without explanatory comments166167## Verification168169- [ ] All five axes reviewed (Correctness, Readability, Architecture, Security, Performance)170- [ ] Tests reviewed first (coverage, edge cases, naming)171- [ ] Findings categorized (Critical, Important, Suggestion, Nit, FYI)172- [ ] Critical findings resolved before approval173- [ ] `./gradlew test` passes174- [ ] `./gradlew assembleDebug` builds175- [ ] `./gradlew lint` clean176- [ ] PR size reasonable (~100 lines, flagged if >1000)
Run npx skillmds@latest add guillemroca/code-review-and-quality in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Use when reviewing Android code (own or others'). Five-axis review framework: Correctness, Readability, Architecture, Security, Performance. Categorized findings with Kotlin/Compose-specific checks. It is listed under Security on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
GuillemRoca (@guillemroca) published this skill. Their other Agent Skills are listed on their SkillMD profile.