Skip to content

Use Case 2: Legacy Code Modernization

Duration: ~60 minutes
Goal: Migrate a legacy application using Plan Mode, custom subagents, skills, and checkpointing. Learn to decompose massive codebases safely.
Exercise PRDs: .NET Modernization · Java Upgrade

Last updated: 2026-05-05 · Source verified against gemini-cli repository


2.1 — Plan Mode: Safe Research (15 min)

Enter Plan Mode

Plan Mode is read-only research. The agent analyzes your codebase, proposes changes, but doesn't modify anything until you approve.

/plan

The CLI indicates you're in Plan Mode. The agent loses access to write tools — it can only read files, search the web, and think.

Analyze the Codebase

Analyze this codebase for a migration to a modern architecture. 
Identify:
1. Key dependencies and their versions
2. Architectural patterns currently in use
3. Areas of technical debt
4. Migration risks and complexity hotspots

What's happening: The agent reads the project — package.json, source files, configuration — and builds a mental model. It explores your codebase on-demand using tools like read_file, glob, and grep_search to trace every dependency, pattern, and anti-pattern.

Review the Plan

The agent produces a structured migration plan. Review it carefully:

Propose a step-by-step plan to modernize the authentication system 
from session-based to JWT with refresh tokens. Include:
- Files that need to change
- Order of operations
- Risk assessment for each step
- Rollback strategy

Collaborative Plan Editing

Open the external editor to refine the plan:

Ctrl+G

This opens your $EDITOR (or a built-in editor) where you can modify the plan directly. The agent sees your edits and adjusts its approach.

Exit Plan Mode

/plan

Toggle back to normal mode. Now the agent can execute the approved plan.


2.2 — Model Routing and Steering (10 min)

Automatic Model Routing

Gemini CLI can automatically select between models based on task complexity:

Task Type Typical Model Why
Planning, architecture analysis Gemini Pro Complex reasoning, long-form analysis
Code generation, file edits Gemini Flash Fast execution, lower cost
Simple queries, status checks Gemini Flash Speed-optimized

This routing is heuristic, not deterministic — the CLI evaluates prompt complexity and selects accordingly. You can override with /model to select a specific model. See Model Routing for details.

Model Steering 🔬

During execution, you can steer the agent mid-stream:

# While the agent is working on a migration step:
Actually, skip the database migration for now. Focus on the API 
layer first — we need the endpoints working before we touch the schema.

Model steering lets you course-correct without starting over. The agent adjusts its plan based on your input and continues from the new direction.

Check Which Model Is Active

/stats

Shows the current model, token usage, and caching status.


2.3 — Context Engineering for Migrations (10 min)

Migration projects are where context engineering pays the biggest dividend. A legacy codebase is full of implicit knowledge — architectural patterns, deprecated API usage, hidden dependency chains — that isn't written down anywhere. The agent needs to internalize this before it can safely change anything.

There are two approaches: manual (you write the GEMINI.md) and agent-driven (the agent writes it for you). Both produce the same artifact, but the agent-driven approach often surfaces things you'd miss.

Agent-Driven: Self-Onboarding with @codebase_investigator

The most powerful pattern for migrations is having the agent investigate the codebase and write its own GEMINI.md. This is the "agent self-onboarding" pattern — it mirrors what a senior engineer does when joining a new project, but at machine speed.

Step 1 — Investigate:

@codebase_investigator Analyze this entire codebase. Map:
1. Framework versions, build system, and dependency tree
2. Architectural patterns (MVC, data access layers, security config)
3. All javax.* imports that will need jakarta.* migration
4. Configuration files and property sources
5. Test frameworks and coverage patterns
Report any migration risks or complexity hotspots.

What's happening: The @codebase_investigator subagent reads every file, traces imports, maps class hierarchies, and builds a complete picture — all in read-only mode. It never modifies anything.

Step 2 — Generate context:

Based on your codebase analysis, write a GEMINI.md that:
1. Documents what you found (current state: Boot 2.6, Java 8, javax.*)
2. Defines the target state (Boot 3.3, Java 21, jakarta.*)
3. Lists migration rules (one module at a time, preserve API contracts)
4. Encodes testing standards (every phase must pass mvn clean verify)
5. Flags the specific risks you identified

Write this file to the project root as GEMINI.md.

Step 3 — Review and refine:

The agent produces a GEMINI.md grounded in what it actually found in the code — not guesses. Review it, add any team-specific conventions, and approve. From this point forward, every migration command the agent executes is guided by this context.

Why this works: The agent is writing instructions for itself. The GEMINI.md it produces becomes the guardrails for its own subsequent work. This is a self-reinforcing loop: better context → better code changes → the agent learns more patterns → context improves further (via Auto Memory).

See it in practice: The Java Upgrade PRD uses this pattern as Phase 0 — the agent must self-onboard before touching any migration code.

Manual: Writing Migration Standards Directly

For teams with established standards, write the GEMINI.md yourself:

# Migration Standards

## Target Architecture
- Framework: .NET 8 (or modern equivalent)
- Hosting: Cloud Run (containerized)
- Database: Cloud SQL with Entity Framework Core
- Auth: JWT with Google Identity Platform
- Config: appsettings.json (not web.config)

## Migration Rules
- Migrate one module at a time — never refactor everything at once
- Every migrated endpoint must have unit tests before moving on
- Preserve existing API contracts — no breaking changes to consumers
- Document every decision in a MIGRATION.md changelog

The @file Import Syntax

For large projects, split GEMINI.md into modular files:

# GEMINI.md
@./docs/architecture.md
@./docs/coding-standards.md
@./docs/migration-checklist.md

Why imports matter: A single GEMINI.md can get unwieldy for enterprise projects. Imports let you organize context into focused documents that are easier to maintain and review. See GEMINI.md reference for full syntax.

Memory for Migration Patterns

As the agent discovers patterns during migration, it saves them:

/memory show

You can also explicitly teach it:

/memory add "When migrating Entity Framework 6 to EF Core, always 
check for .edmx files and replace them with code-first models. 
The database-first approach is deprecated in EF Core."

Context engineering lifecycle: The best migration workflows combine all three: an agent-generated GEMINI.md (initial context), @file imports (modular standards), and Auto Memory (patterns learned during execution). Each reinforces the others.


2.4 — Subagents: Delegating Specialized Work (15 min)

Built-in Subagents

Gemini CLI includes built-in subagents for common tasks:

@codebase_investigator Map the relationships between all controllers 
in the backend/ directory. Show which models each controller depends 
on and which routes call each controller.

@codebase_investigator is a read-only agent that maps code relationships, traces call chains, and identifies architectural patterns. It never modifies files.

Custom Subagents

Create a security scanner for your migration:

cat .gemini/agents/security-scanner.md

The security scanner subagent (from samples/agents/security-scanner.md): - Has a focused system prompt for security analysis - Can be restricted to specific tools - Uses a specific model (you can assign Flash for speed or Pro for depth)

Using Custom Subagents

@security-scanner Review the authentication middleware for OWASP 
Top 10 vulnerabilities. Check for:
1. Injection attacks (SQL, NoSQL)
2. Broken authentication
3. Sensitive data exposure
4. Missing rate limiting

Subagent Tool Isolation

Each subagent can have its own tool allowlist:

# .gemini/agents/security-scanner.md
---
model: gemini-3.1-flash-lite-preview
tools:
  - read_file
  - list_directory
  - google_web_search
# No write_file, no run_shell_command — this agent is read-only
---

You are a security analyst. Your job is to find vulnerabilities...

Enterprise value: The security scanner can read code and search for CVEs, but it can never modify files or run commands. Tool isolation is defense-in-depth.


2.5 — Skills: Reusable Expertise (5 min)

View Available Skills

/skills list

Skills are reusable instruction sets that the agent activates when relevant:

How Skills Work

  1. Auto-activation: The agent reads skill descriptions and activates relevant ones based on your prompt
  2. Manual activation: You can force a skill with its name
  3. Persistence: Skills survive across sessions — learn once, use everywhere

Auto Memory 🔬

Auto Memory extracts patterns from your sessions and saves them to GEMINI.md:

/memory show

Experimental: Auto Memory requires experimental.autoMemory to be enabled in settings.json. See Auto Memory docs. When enabled, the agent might auto-save patterns like: "When migrating Express.js middleware, check for req.query vs req.params mismatches."


2.6 — Checkpointing and Git Worktrees (5 min)

Checkpointing

Checkpointing automatically saves the state of modified files before changes, allowing you to revert if something goes wrong. To enable it, add to your settings.json:

{
  "general": {
    "checkpointing": {
      "enabled": true
    }
  }
}

When enabled, use /restore to revert to a previous checkpoint:

/restore

Checkpoints are lightweight — they track file changes, not full git history. See Checkpointing docs for details.

Git Worktrees 🔬

For parallel migration work, use Git worktrees:

# Create a worktree for the auth migration
git worktree add ../proshop-auth-migration feature/auth-migration
cd ../proshop-auth-migration
gemini

Why worktrees? You can have the original code in one terminal and the migrated code in another. Run tests on both simultaneously. Compare approaches without branch switching.


Hands-On Exercise

Open the .NET Modernization PRD or Java Upgrade PRD and work through a migration. Choose your approach:

Approach A: Conductor-First (Plan → Context → Execute)

Start with structured planning and let the plan drive context creation:

  1. Enter Plan Mode (/plan) → analyze the target codebase read-only
  2. Use Conductor to create a phased migration plan matching the PRD phases
  3. Write a GEMINI.md encoding migration standards and the approved plan
  4. Use @codebase_investigator to map dependencies and validate the plan
  5. Create a checkpoint before starting
  6. Exit Plan Mode → begin the migration, one phase at a time
  7. Use model steering to course-correct as needed
  8. After each phase, run mvn clean verify and a security scan (see below)
  9. Review what Auto Memory learned from the session

Approach B: Self-Onboarding (Investigate → Context → Plan → Execute)

Let the agent build its own understanding first, then plan from what it found:

  1. Use @codebase_investigator to analyze the target codebase and map dependencies
  2. Have the agent write a GEMINI.md based on its analysis (agent self-onboarding)
  3. Review and refine the generated context — add team-specific standards
  4. Enter Plan Mode → let Conductor create a phased migration plan informed by the GEMINI.md
  5. Create a checkpoint before starting
  6. Begin the migration — use model steering to course-correct as needed
  7. After each phase, run mvn clean verify and a security scan (see below)
  8. Review what Auto Memory learned from the session

Which approach? Approach A works well when you already know the codebase and want to lead with structure. Approach B works better with unfamiliar legacy code — the agent often surfaces migration risks that a human-written plan would miss. Try both and compare the quality of the resulting plans.

Post-migration security scan: After modernizing legacy code, run the official Security Extension to catch vulnerabilities introduced during migration. Install it with gemini extensions install https://github.com/gemini-cli-extensions/security, then run /security:analyze to scan your changes. See Extensions Ecosystem — Exercise 4 for full details.


Summary: What You Learned

Feature What It Does
Plan Mode Read-only research — analyze before modifying
Model routing Automatic Pro (planning) → Flash (coding) selection
Model steering Course-correct the agent mid-stream
Agent self-onboarding Agent investigates codebase and writes its own GEMINI.md
@ import syntax Modular GEMINI.md for large projects
@codebase_investigator Read-only codebase analysis subagent
Custom subagents Specialized agents with tool isolation
Skills Reusable instruction sets that auto-activate
Auto Memory Agent learns patterns from sessions
Checkpointing Automatic save/restore state before risky changes (enable in settings.json)
Git Worktrees Parallel branches for simultaneous work
Security Extension Post-migration vulnerability scan with /security:analyze

Next Step

→ Continue to Use Case 3: Agentic DevOps Orchestration

→ For power users: Advanced Patterns — prompting craft, verification loops, and parallel development