Migrations
Tern helps you understand large-scale code changes before you commit to them. Here’s how the product is organized.
The Big Idea
Tern separates planning from execution. Before you start generating code or firing off PRs, you need to know: what needs to change, how much of it is there, and what’s the shape of the work? Tern builds that picture for you, a structured, shareable view of what exists in your codebase and what you want to do about it.
Tern will not change your code until you explicitly tell it to.
Migrations
A migration is a container for a unit of large-scale work. “Migrate from Enzyme to RTL,” “Convert Django templates to React,” “Replace styled-components with Tailwind.” Each of these is a migration.
Every migration has exactly one spreadsheet, a set of tasks, and an agent you can chat with to build the plan.
The Spreadsheet
The spreadsheet is the center of Tern. It’s where you spend most of your time.
Each row represents a place in your codebase where work needs to happen: a file, a component, a call site, a test. Columns describe what’s there: who owns it, what type of pattern it is, how complex the change looks, whether the AI thinks it’s mechanical or needs human judgment.
The spreadsheet is how you answer the question: what is the shape of this migration?
You can group, filter, and sort to see patterns. Maybe 3,800 of your styled-components are styling raw HTML elements (probably mechanical, probably easy). Another 2,000 are custom components with a long-tail distribution: 170 override Button, 101 override Link, and then progressively fewer from there. That tells you where to start and what to expect.
Populating the spreadsheet
Two ways to get rows into the spreadsheet:
Use the agent. Open the agent chat and say something like “find all Django templates” or “find every Enzyme test file.” The agent searches your codebase and populates the spreadsheet. It can also create columns to organize what it finds, classifying patterns, flagging complexity, identifying owners.
Use search directly. In the spreadsheet, type a search query (regex-based) to find matching code across your repos. This is useful when you know exactly what you’re looking for.
Either way, the spreadsheet is just a list of places you might want to do work. Adding rows doesn’t change anything in your code.
Tasks
A task is a prompt. It describes the transformation you want applied to each row.
At its simplest, a task is a plain-language instruction:
Convert this styled-component to use Tailwind classes.But the syntax goes much further. Tasks support multi-step workflows where each step can store context for later steps, run validation commands, and automatically retry on failure. A real task might look like this:
# Analyze
Read {file} and identify:
- Which Enzyme APIs are used (shallow, mount, render)
- Whether it tests behavior or implementation details
- What component dependencies exist
## Store
Output: { "apis": [...], "testStyle": "behavior|implementation" }
# Migrate
Rewrite this Enzyme test for React Testing Library.
- If it's a shallow render test that only asserts a child exists, delete it.
- If it tests visible behavior, rewrite to RTL.
- If it calls .state() or .instance(), delete it (no RTL equivalent).
Use the analysis from step 1 to guide which approach to take.
## Validation
max_retries: 3
run: npx tsc --noEmit {file}
run: npx vitest run {file}
Confirm all assertions test user-visible behavior, not implementation details.Each # header is a step. Steps run in order, one AI session per file per step. The ## Store block captures structured data that later steps can reference as template variables. The ## Validation block combines shell commands (run: lines) with LLM judgment; if validation fails, the agent sees the error output and retries automatically.
Tasks can also reference files with @ syntax (@migration-guide.md or @myrepo:standards.yaml) to pull in shared context without duplicating it.
You create tasks separately, then assign them to rows in the spreadsheet. Different rows can have different tasks. Select a group of rows, bulk-assign a task, and now every row in that group knows what work it needs.
This is the “how” to the spreadsheet’s “where.” See Task Instructions for the full syntax reference.
The Agent
Each migration has an agent. It has full read access to your codebase (the same access a coding agent would have) but instead of editing your code, it edits your spreadsheet. You talk to it, it searches your repos, and it organizes what it finds into rows, columns, and tasks.
Typical conversation: “Find every file that imports from the old auth SDK.” The agent runs a search, populates the spreadsheet with matching files, then creates columns to classify what it found (direct imports vs. re-exports, which auth methods are used, estimated complexity). You look at the spreadsheet, ask follow-up questions, and the agent refines.
It can also create tasks and assign them to rows, define PRs, and generally do the planning legwork. The agent modifies the spreadsheet, not your code. You decide when and how to execute.
Repos
To get started, sign into GitHub and install the Tern GitHub app on the repos you want to work with. Each Tern account connects to one GitHub organization. Once connected, you can associate repos with a migration and Tern searches across all of them when populating the spreadsheet.
For enterprise deployments, Tern also supports a local CLI mode where code never leaves your infrastructure. See Architecture for details.
PRs
Once your spreadsheet is organized and tasks are assigned, you can group rows into PRs before running anything.
Filter the spreadsheet to a set of rows (say, all the div-based styled-components owned by one team) and assign them to a named PR. Do the same for another group. Now you have a set of planned PRs, each scoped to a coherent chunk of work, before any code has been generated.
When you run, each PR becomes a branch with the changes for its rows.
Execution
There are two ways to run the work:
In Tern. Select rows or a PR in the spreadsheet and hit run. Tern executes the task for each row remotely, and results flow back into the spreadsheet. See Background Agents for how this works.
In Claude Code. Run tern init in your repo. This drops a skill into .claude/skills that gives Claude Code access to your migration plan. Open Claude Code, say “let’s do the next chunk of the migration,” and it pulls your spreadsheet context (what’s planned, what tasks are assigned, which PRs are ready) and executes from there. You watch it work on your machine. See Skills for details.
Next Steps
- Task Instructions - the full syntax for writing tasks
- Background Agents - what happens when you hit run
- Skills - using Tern with Claude Code
- Search Syntax - how to write precise queries for finding code
- Architecture - how the web app and CLI work together
