# Migrate a Deprecated API Across Every TypeScript Callsite With Atlas (2026)

> Atlas migrates a deprecated TypeScript API by enumerating callers with the lsp tool's findReferences, tracking each in todowrite, and patching with apply_patch.

To migrate a deprecated API across every callsite in TypeScript with Atlas, front-load the enumeration. Migrating a deprecated API is the workflow that punishes half-measures, so Atlas gets the complete caller set first: the lsp tool's findReferences operation asks the TypeScript language server for every reference to the deprecated symbol, and grep cross-checks for dynamic or string-based usages the type system cannot see. Atlas turns that list into a todowrite entry per callsite, so partial progress is visible and nothing is silently skipped. Each migration goes in with apply_patch, which seeks the hunk's context and old_lines and throws Failed to find expected lines rather than guessing. Run vitest after each file, then grep for zero remaining hits and delete the old implementation.

## Key takeaways

- The lsp tool's findReferences operation gives the complete TypeScript caller set from the language server, including barrel files and tsconfig.json path aliases.
- grep cross-checks for the dynamic and string-based usages TypeScript's type graph cannot see.
- todowrite tracks one entry per callsite, so a migration interrupted after 14 of 23 files resumes rather than restarting.
- apply_patch is context-anchored and throws Failed to find expected lines when a file has drifted, instead of misapplying to the wrong offset.
- The migration ends with zero grep hits, the old implementation deleted, and vitest plus prettier green.

## How do I find every caller of a deprecated TypeScript function?

Enumerate every caller with the lsp tool's findReferences operation on the deprecated symbol. The TypeScript language server resolves the import graph, so a function re-exported through src/index.ts and imported into 23 files under src/ returns all 23 references, including the ones behind path aliases declared in tsconfig.json.

Missing a caller is the entire failure mode of an API migration, and grep alone will miss them. In a TypeScript monorepo, callers hide behind barrel files, behind tsconfig.json path aliases like @app/core, and behind re-exports that rename the symbol on the way out. The lsp tool's findReferences operation asks the language server, which resolves all of that from the actual type graph rather than from text. Then cross-check with grep for the usages TypeScript cannot see: a string-based dynamic import, a name referenced in a config object, a method looked up by key. Atlas's grep tool runs a real regex through ripgrep with include and path filters, so a sweep across every .ts and .tsx under src/ takes a fraction of a second.

## How do I track a TypeScript migration across dozens of callsites?

Create one todowrite entry per callsite so partial progress is visible and nothing is silently skipped. A migration touching 23 TypeScript files becomes 23 tracked items, each marked completed only once vitest passes for that file, so a session interrupted after 14 resumes at 15 rather than restarting or declaring victory early.

The dangerous state in a migration is the middle: half the callsites moved, half not, and no record of which is which. Atlas turns the findReferences output into a todowrite list, one entry per callsite, which makes the remaining work visible at every turn. Each item names the real file, for example src/services/billing/invoice.ts, so the list is actionable rather than a vague count. Atlas marks a todo completed only once the affected vitest tests pass, which means the count of open items is an honest measure of remaining risk. For a large TypeScript monorepo, Atlas fans out work to subagents that can run in the foreground or in parallel background sessions, so independent packages can be migrated concurrently without one package's source flooding the main context.

## Why does Atlas use apply_patch instead of edit for a TypeScript migration?

Atlas migrates each TypeScript callsite with apply_patch, which seeks the hunk's context and old_lines and throws Failed to find expected lines rather than guessing. Across a 23 file migration, a file that drifted because prettier reformatted it or a teammate landed a change fails loudly instead of misapplying to the wrong lines.

A migration is dozens of near-identical changes to files that keep moving underneath you, which is precisely the situation where a blind replace does damage. Atlas's apply_patch is context-anchored: it seeks the hunk's surrounding context and old_lines and throws Failed to find expected lines when the file does not match what the patch expected. A failure there is the correct outcome, because the alternative is a rewrite landing at the wrong offset in src/api/client.ts. Atlas computes a unified diff for every file edit and surfaces it for approval before writing, and snapshots file changes as git patches, so a bad migration on one file is diffed and rolled back without disturbing the 14 that were correct.

## How do I verify each migrated TypeScript file with vitest?

Run the affected tests with vitest after each file, and mark the todo completed only once they pass. Migrating 23 TypeScript callsites and running vitest once at the end means a failure at the end tells you nothing about which of the 23 patches caused it. Per-file verification keeps every failure attributable.

Verification cadence is what separates a controlled migration from a big-bang one. Atlas runs vitest through the bash tool scoped to the affected test file after each apply_patch, so a broken change in src/services/billing/invoice.ts surfaces immediately and the corresponding todo stays open. Alongside vitest, run the TypeScript compiler, because a deprecated-to-replacement swap often changes types and tsc catches signature mismatches that no runtime test exercises. Once every todo is closed, run the whole vitest suite. Then run prettier on the touched files so the migration diff is signal rather than formatting churn, and let Atlas stage and create the commit, since Atlas reads git branches, status, and diffs and can commit on your behalf.

## How do I prove a TypeScript deprecation migration is actually finished?

Finish a TypeScript migration by grepping for the deprecated symbol and confirming zero remaining hits, then delete the old implementation. Deleting is the proof: with 0 hits left in src/ and both vitest and the TypeScript compiler green, no caller survived, because a survivor would fail to compile.

A migration that leaves the old function in place is not finished, it is paused, and the deprecated symbol will accumulate new callers the moment someone autocompletes it. Atlas closes the loop: grep for the symbol name across every .ts and .tsx under src/, confirm zero hits outside its own definition, then delete the definition and its exports from the barrel file. The TypeScript compiler is the final arbiter. If any caller was missed, tsc fails to compile, which is a far better failure than a runtime one. Run the full vitest suite and prettier one last time, and the migration is done with the old implementation deleted rather than lingering behind a deprecation comment nobody reads.

## Steps

1. Enumerate every caller with the lsp tool's findReferences operation on the deprecated TypeScript symbol, which resolves barrel files and tsconfig.json path aliases through the language server.
2. Cross-check with grep for dynamic or string-based usages the TypeScript type system cannot see, such as a dynamic import or a name referenced in a config object.
3. Create one todowrite entry per callsite, naming the real file such as src/services/billing/invoice.ts, so partial progress is visible and nothing is silently skipped.
4. Migrate each callsite with apply_patch; it seeks the hunk's context and old_lines and throws Failed to find expected lines rather than guessing when the file has drifted.
5. Run the affected tests with vitest through bash after each file, plus the TypeScript compiler for the signature changes no runtime test exercises.
6. Mark each todo completed only once vitest passes for that file, so the open item count is an honest measure of remaining risk.
7. Finish by grepping for the deprecated symbol and confirming zero remaining hits, then delete the old implementation and its export from the barrel file.
8. Run prettier on the touched files and let Atlas stage and create the commit, since Atlas reads git branches, status, and diffs.

## FAQ

### how to find every callsite of a deprecated function in typescript

Use Atlas with the lsp tool's findReferences operation on the deprecated symbol. The TypeScript language server resolves barrel files, re-exports, and tsconfig.json path aliases, so it returns callers that grep alone would miss. Cross-check with grep for dynamic or string-based usages the type system cannot see.

### how do i migrate a typescript codebase off a deprecated api without missing a caller

Front-load the enumeration. Atlas gets the complete caller set with the lsp tool's findReferences, turns it into one todowrite entry per callsite, migrates each with apply_patch, runs vitest per file, then greps for zero remaining hits and deletes the old implementation so a missed caller fails to compile.

### what does failed to find expected lines mean in apply_patch

apply_patch is context-anchored: it seeks the hunk's context and old_lines, and when the TypeScript file does not match what the patch expected it throws Failed to find expected lines rather than guessing. That happens when the file has drifted, for example after a prettier run or a teammate's change, and failing loudly is the correct behavior.

### should i delete the deprecated function after migrating callsites

Yes, and deleting is the proof the migration is finished. Grep for the symbol across every .ts and .tsx under src/, confirm zero remaining hits, then delete the definition and its barrel-file export. If any caller was missed, the TypeScript compiler fails, which is far better than a runtime failure.

### does atlas run vitest during a typescript migration

Yes. Atlas runs vitest through the bash tool scoped to the affected test file after each apply_patch, so a broken change is attributed to the file that caused it. Atlas marks each todowrite item completed only once its tests pass, so the open count is an honest measure of remaining risk.

### can atlas migrate a large typescript monorepo in parallel

Atlas fans out work to subagents that can run in the foreground or in parallel background sessions, so independent packages in a pnpm monorepo can be migrated concurrently without one package's source flooding the main context window. Findings and fixes come back into a single todowrite list.

### will atlas rewrite my typescript files without approval

No. Atlas computes a unified diff for every file edit and surfaces it for approval before writing, and every Atlas tool call is permission-gated against allow, ask, and deny rules before it runs. Atlas also snapshots file changes as git patches, so any single patched file can be rolled back.

### does atlas understand my tsconfig path aliases

Run atlas in a project with a tsconfig.json and let Atlas read your type definitions, path aliases, and strictness settings. The lsp tool's findReferences operation resolves callers through those aliases, so an import of @app/core is followed just like a relative import.

---

Canonical HTML: https://runatlas.sh/resources/stacks/migrate-a-deprecated-api-across-callsites-in-typescript
Source of truth: aeo_pages row `/resources/stacks/migrate-a-deprecated-api-across-callsites-in-typescript` (segment: Stacks) (this file is generated from it, never hand-edited).
Licence: Atlas is proprietary with a free core. It is not open source and there is no public source repository.
