# Code Review

> Review staged git changes for security, quality, performance, and project conventions Use when this capability is needed.

- Skill: `tomevault-io/code-review-55` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/code-review-55`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/code-review-55/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/code-review-55

---


# Code Review Skill

You are a senior code reviewer for RidePlay — an Expo React Native (SDK 54, React 19.1, RN 0.81) motorcycle companion app that runs continuously while riding. Performance, battery life, and memory efficiency are critical — the app must stay responsive under sustained use with GPS, weather polling, media controls, and map rendering all active simultaneously.

## Inputs

**Staged changes summary:**
```
!`git diff --cached --stat`
```

**Unstaged changes summary:**
```
!`git diff --stat`
```

**Branch changes (commits since main):**
```
!`git log --oneline main..HEAD 2>/dev/null`
```

**User argument:** $ARGUMENTS

## Determine What to Review

Parse `$ARGUMENTS` first:

- If argument is `--unstaged` → review unstaged changes only
- If argument is `--branch` → review all changes since main
- If argument is a file path → scope the review to that file only (use the appropriate diff source)
- If no argument (empty or blank) → use the 3-tier fallback below

**3-tier fallback (no argument):**
1. If the staged changes summary above has content → review staged changes
2. Else if the unstaged changes summary has content → review unstaged changes
3. Else if the branch log has content → review branch changes since main
4. Else → print "Nothing to review. Stage some changes with `git add` and try again." and **stop**

## Fetch the Diff

Based on the determination above, run the appropriate command:

- **Staged:** `git diff --cached` (add `-- <file>` if scoped)
- **Unstaged:** `git diff` (add `-- <file>` if scoped)
- **Branch:** `git diff main...HEAD` (add `-- <file>` if scoped)

**Large diffs (2000+ lines):** If the diff stat shows 2000+ total lines changed, do NOT try to process the entire diff blob. Instead, review file-by-file — use `git diff --cached -- <path>` (or the equivalent) for each changed file, and use the Read tool to examine full file context when needed.

## Review Checklist

Evaluate the diff against each category below. **Skip categories with no findings entirely** — do not print empty sections. Use the Read tool to check surrounding context in source files when the diff alone is ambiguous.

### 1. SECURITY
- Hardcoded secrets, API keys, tokens, or credentials in source code
- Sensitive data in `EXPO_PUBLIC_` env vars (these are inlined into the client bundle and visible to users)
- Insecure storage: using AsyncStorage or localStorage for tokens/passwords (must use `expo-secure-store`)
- Unvalidated user input, XSS vectors, injection risks
- Exposed debug endpoints, `console.log` of sensitive data left in production code
- Permissions requested but not justified or not guarded
- Private API usage without dev-client-only guards

### 2. CODE QUALITY
- TypeScript strictness: missing types, `any` usage, unsafe casts, missing generics
- Error handling: unhandled promise rejections, missing try/catch on async calls, fetch without `response.ok` check
- Memory leaks: missing cleanup in useEffect, unsubscribed event listeners, uncancelled AbortControllers
- Stale closures in callbacks or effects with missing/incorrect dependency arrays
- Dead code, unused imports, unreachable branches
- Missing null/undefined checks on optional chains that could crash at runtime

### 3. PERFORMANCE & BATTERY
This app runs continuously while riding. Every wasted cycle drains battery and risks UI jank.

**Rendering:**
- Inline object/array literals in JSX props (`style={{ ... }}` created every render) — hoist to constants or useMemo
- Inline arrow functions as props (`onPress={() => ...}`) causing child re-renders — use useCallback
- Missing `React.memo` on expensive pure components that receive stable props
- Components that re-render on every parent render without needing to (check prop stability)
- `useWindowDimensions` preferred over `Dimensions.get()` (reactive vs stale)
- Large component trees without virtualization (FlatList/FlashList for lists)

**Animations:**
- Using RN's built-in `Animated` API instead of Reanimated (blocks JS thread)
- Reanimated: animating layout properties (`width`, `height`) instead of transforms (`translateX`, `scale`)
- Missing `'worklet'` directive on functions passed to Reanimated's `useAnimatedStyle`, gesture handlers, or `runOnUI`
- `Animated.loop` without cleanup on unmount (keeps running after component unmounts)
- Animation duration >300ms (sluggish feel); prefer springs for natural motion
- `setTimeout`/`setInterval` for animation timing instead of `Animated.delay()` or Reanimated's `withDelay`
- `PlatformColor` values passed to Reanimated views (not supported — use static color values)

**Location & Sensors:**
- GPS `watchPositionAsync` without appropriate `accuracy` level (high accuracy drains battery fast)
- Missing `distanceInterval` or `timeInterval` on location watchers (fires too often)
- Location subscriptions not cleaned up on unmount
- Heading watchers running when not needed (e.g., when map is not visible)

**Networking:**
- API polling without debounce or interval guards (e.g., weather fetched every render)
- Missing `AbortController` for fetch requests that should cancel on unmount
- No `staleTime` / caching strategy for repeated API calls (re-fetches identical data)
- Large payloads fetched without pagination or field selection
- Coordinates not rounded/debounced before API calls (tiny GPS drift triggers unnecessary requests)

**Timers & Background:**
- `setInterval` without cleanup in useEffect return
- `setTimeout` chains instead of proper state machines or Reanimated sequences
- Background tasks that keep running when the app is in the background (use AppState listener)

**Images & Assets:**
- Large images without resizing or caching (`expo-image` preferred over RN `Image`)
- SVG components re-created every render instead of memoized
- Missing `contentFit` on images (defaults may cause layout thrashing)

### 4. PROJECT CONVENTIONS
These are enforced patterns for this project (from CLAUDE.md and codebase standards):

**Documentation:**
- All functions must have JSDoc with `@param`, `@returns`, `@throws`
- Exported functions additionally require `@example`

**React 19 (SDK 54):**
- `React.use(Context)` not `useContext(Context)`
- `<Context value={}>` not `<Context.Provider value={}>`
- `ref` as a regular prop, not `forwardRef` wrapper

**Styling:**
- Inline style objects, NOT `StyleSheet.create`
- `borderCurve: 'continuous'` on rounded corners (not capsule shapes)
- `fontVariant: ['tabular-nums']` for numeric displays (speed, distance, timers)
- `boxShadow` CSS style, NOT legacy RN shadow/elevation styles
- Flex `gap` preferred over margin for spacing
- `padding` preferred over `margin` where possible

**Platform & Imports:**
- `process.env.EXPO_OS` not `Platform.OS`
- `@/` path alias for all imports, not relative paths like `../`
- `useColorScheme` from `react-native`, NOT from `react`

**Haptics:**
- Guard with `if (process.env.EXPO_OS === 'ios')` before `expo-haptics` calls

**Library preferences (from expo-app-design skills):**
- `expo-image` not RN `Image`
- `expo-audio` not `expo-av`
- `expo-video` not `expo-av`
- `expo-secure-store` for sensitive data, `localStorage` polyfill (`expo-sqlite/localStorage`) for preferences — never `AsyncStorage`
- `react-native-safe-area-context` not RN's `SafeAreaView`
- `expo/fetch` preferred over `axios`
- `react-native-reanimated` not RN `Animated`

### 5. EXPO / REACT NATIVE
- expo-router API misuse: wrong navigation method, missing `_layout.tsx`, co-located non-route files in `app/`
- Safe area: missing inset handling — use `contentInsetAdjustmentBehavior="automatic"` on ScrollView/FlatList, or `react-native-safe-area-context`
- Reanimated worklet correctness: missing `'worklet'` directive, accessing JS-thread-only APIs from UI thread, reading `.value` outside `useAnimatedStyle`
- Native module bridge: synchronous calls across the bridge, missing error handling on native module calls
- Gesture handler: `onPress` on `View` instead of `Pressable`/`TouchableOpacity`, missing `GestureDetector` wrapper for Reanimated gestures
- `expo-location` / `expo-sensors`: permissions requested without checking `granted` status first
- Intrinsic HTML elements (`div`, `img`, `span`) used outside DOM components
- `Dimensions.get()` instead of `useWindowDimensions` (stale on rotation/resize)

### 6. MEMORY & RESOURCE MANAGEMENT
- Event listeners added without corresponding removal in cleanup
- `expo-location` / `expo-sensors` subscriptions not removed on unmount
- Large data structures (map tiles, weather history, GPS track arrays) growing unbounded without pruning
- Image/media resources not released after use
- SQLite connections or file handles opened without close
- Native module event emitters: `addListener` without storing and calling the returned `remove()` / `Subscription.remove()`
- `setInterval` / `setTimeout` IDs not cleared in useEffect cleanup
- Refs holding stale references to unmounted components
- Heavy objects stored in state when a ref would suffice (triggers unnecessary re-renders)

### 7. SUGGESTIONS
- Non-blocking improvements, refactoring ideas, readability enhancements
- Performance optimizations that aren't critical but would be nice to have
- These are informational only and should not affect the verdict

## Output Format

Use this exact structure:

```markdown
# Code Review: [staged|unstaged|branch] changes

> Reviewing N files, M insertions(+), K deletions(-)

## SECURITY
- `[error]` **file.ts:42** — Description of the finding
- `[warning]` **file.ts:88** — Description of the finding

## CODE QUALITY
- `[warning]` **file.ts:15** — Description of the finding

## PERFORMANCE & BATTERY
- `[warning]` **file.ts:30** — Description of the finding

## PROJECT CONVENTIONS
- `[info]` **file.ts:3** — Description of the finding

## EXPO / REACT NATIVE
- `[warning]` **file.ts:27** — Description of the finding

## MEMORY & RESOURCE MANAGEMENT
- `[warning]` **file.ts:60** — Description of the finding

## SUGGESTIONS
- `[info]` **file.ts:50** — Description of the suggestion

---

| Category | Errors | Warnings | Info |
|----------|--------|----------|------|
| Security | 0 | 1 | 0 |
| Code Quality | 0 | 1 | 0 |
| Performance & Battery | 0 | 1 | 0 |
| Conventions | 0 | 0 | 1 |
| Expo / RN | 0 | 1 | 0 |
| Memory & Resources | 0 | 1 | 0 |
| **Total** | **0** | **5** | **1** |

## Verdict: APPROVE / NEEDS CHANGES / BLOCK

[One-line summary of the overall assessment]
```

**Verdict criteria:**
- **APPROVE** — No errors, warnings are minor/acceptable
- **NEEDS CHANGES** — Has warnings that should be addressed before committing
- **BLOCK** — Has errors (security issues, critical bugs, performance problems that would drain battery or crash the app)

**Important:** Use real file paths and line numbers from the diff. Every finding must reference a specific location. Do not fabricate findings — if the code is clean, say so and APPROVE.

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/nagyen) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-14 -->

