In 2026, F# developers can refactor legacy modules without changing their behavior or breaking callers by leveraging Atlas, which integrates directly with the F# toolchain. Atlas uses `dotnet test (Expecto)` to pin existing behavior, applies structural changes with `apply_patch`, and ensures code quality with `fantomas`, all while tracking callsite migrations.
How Atlas maps F# module structure for safe refactoring
Atlas maps the public surface of your F# modules by parsing your `.fsproj` file to understand file order and AST declarations, ensuring it respects discriminated unions and computation expressions. This initial indexing process, often completed in mere seconds, is crucial for identifying all 100% of potential callsites before any changes are made.
Before Atlas touches any F# code, it first builds a comprehensive understanding of your module's structure. It uses the `lsp` tool's `documentSymbol` operation to map all public declarations within your F# files, such as `src/LegacyModule.fs`. Because F# compiles in file order, Atlas reads this order directly from your `.fsproj` file, ensuring it correctly identifies modules, types, and functions. For each exported symbol, Atlas then performs a `findReferences` operation, enumerating every single callsite across your codebase. This meticulous mapping, powered by AST declarations from tree-sitter, prevents silent breakage at unknown callsites, a common risk in legacy F# refactoring.
Pinning F# module behavior with `dotnet test (Expecto)`
Before any structural changes are applied, Atlas pins the existing behavior of your F# module by running your `dotnet test (Expecto)` suite to establish a green baseline. This critical step, typically taking less than 1 minute for a well-configured test project, ensures that subsequent refactors do not introduce regressions or alter the module's external behavior.
The first concrete step in refactoring an F# module with Atlas is to establish a behavioral baseline. Atlas uses the `bash` tool to execute your existing F# tests. For projects using Expecto, this means running `dotnet test`. Atlas records the output, confirming that all tests pass. This 'green baseline' is paramount: any future change that causes these tests to fail immediately signals a behavioral regression. This approach ensures that the core job of restructuring an old module without changing its behavior is rigorously enforced from the outset, providing a safety net for every modification.
Applying structural changes to F# code with Atlas's `apply_patch`
Atlas applies structural changes to your F# code using its `apply_patch` tool, which anchors on context lines and refuses to apply against a drifted file. This robust mechanism ensures that your F# source files, like `src/MyModule.fs`, are modified precisely as intended, preventing 100% of accidental overwrites due to concurrent edits.
Once the F# module's public surface is mapped and its behavior pinned, Atlas proceeds with structural changes. It uses the `apply_patch` tool to modify `.fs` files. This tool is designed for safety: it seeks each hunk's context and `old_lines` within the target file. If the file has drifted,meaning the context lines no longer match,`apply_patch` fails with a `Failed to find context` error. This prevents Atlas from applying changes to an outdated version of your F# code, ensuring integrity. Atlas computes a unified diff for every file edit and surfaces it for approval before writing, giving you full control over every change to your F# codebase.
Iterative testing and F# code quality with `fantomas`
After each hunk of changes lands, Atlas re-runs your `dotnet test (Expecto)` suite, not just once at the end, to immediately verify F# module behavior. Furthermore, Atlas ensures F# code quality by running `fantomas` over the changed `.fs` files, often involving 2-3 formatting passes per refactor, before presenting a unified diff for approval.
A core principle of safe F# refactoring with Atlas is iterative verification. After each `apply_patch` operation successfully modifies an F# file, Atlas immediately re-runs `dotnet test` using the `bash` tool. This rapid feedback loop catches regressions early, preventing a cascade of errors that are harder to debug later. Additionally, Atlas integrates `fantomas`, the F# code formatter. After changes are applied, Atlas runs `fantomas` over the modified `.fs` files, ensuring that all new or altered code adheres to your project's F# style guidelines. The unified diff presented for approval includes both the structural changes and any `fantomas`-driven formatting, providing a complete picture of the proposed modifications.
Tracking F# callsite migrations with `todowrite`
Atlas helps track F# callsite migrations using the `todowrite` tool, which maintains a persistent list of remaining callsites to be updated. This ensures that a partially migrated F# module, even with 50% of its callers updated, is never mistaken for a finished one, providing clear visibility into the refactoring progress.
Refactoring a legacy F# module often involves updating numerous callsites across the codebase. Atlas addresses this challenge with the `todowrite` tool. As Atlas identifies callsites using `lsp findReferences`, it can add them to a `todowrite` list. As each callsite is migrated and verified, it can be marked as complete. This mechanism provides a clear, persistent record of the remaining work, preventing a partially migrated F# module from being mistakenly considered complete. It's an essential tool for managing the complexity of large-scale F# refactoring projects, ensuring every caller is updated correctly.
Review and safety mechanisms for F# refactoring with Atlas
Atlas incorporates multiple review and safety mechanisms to protect your F# codebase during refactoring, including permission-gated tool calls and unified diffs for every edit. Every Atlas tool call, from `lsp` to `bash`, is permission-gated against allow, ask, and deny rules, providing granular control over agent actions 100% of the time.
Atlas is built with safety and transparency at its core, especially crucial for F# refactoring. Every Atlas tool call, whether it's `lsp` for code analysis, `bash` for running `dotnet test`, or `apply_patch` for modifying files, is permission-gated. You can configure allow, ask, or deny rules, ensuring Atlas only performs actions you explicitly approve. Before any file is written, Atlas computes a unified diff for every proposed edit and surfaces it for your approval. This allows you to review every line change to your F# code, ensuring accuracy and preventing unintended modifications. Atlas also snapshots file changes as git patches, so edits can be diffed and rolled back easily, providing multiple layers of protection for your F# project.
Step by step
- 011: Map the F# module's public surface: Use `atlas lsp documentSymbol` on your `src/LegacyModule.fs` file, then `atlas lsp findReferences` on each exported symbol to enumerate all callsites.
- 022: Pin F# behavior: Run existing tests with `atlas bash "dotnet test"` (or `dotnet test --test-adapter-path:. --logger:trx` for specific Expecto setups) and record the green baseline before any changes.
- 033: Restructure F# code: Apply structural changes using `atlas apply_patch --file src/LegacyModule.fs --patch <patch_content>`. Atlas anchors on context lines and fails if the file has drifted.
- 044: Verify F# behavior iteratively: After each `apply_patch` hunk lands, re-run tests immediately with `atlas bash "dotnet test"` to catch regressions early.
- 055: Format F# code: Ensure style consistency by running `atlas bash "fantomas src/LegacyModule.fs"` over the changed `.fs` files.
- 066: Track F# callsite migrations: Use `atlas todowrite add "Migrate callsite X in Y.fs"` to maintain a list of remaining callsites, preventing a partially migrated module from being mistaken for finished.
Frequently asked questions
- How does Atlas handle F# specific constructs like discriminated unions during refactoring?
- Atlas indexes F# code by AST declarations using tree-sitter, which allows it to understand and respect F# specific constructs like discriminated unions, computation expressions, and record types. This deep understanding ensures that refactoring suggestions and changes are semantically correct for F#.
- Can Atlas integrate with my existing `dotnet test (Expecto)` setup for F# projects?
- Yes, Atlas integrates direct with your existing `dotnet test (Expecto)` setup. It uses the `bash` tool to execute `dotnet test`, allowing you to leverage your current test suites for pinning behavior and verifying changes during F# module refactoring.
- What if my F# file has changed since Atlas generated a patch? Will it overwrite my work?
- No, Atlas's `apply_patch` tool is designed for safety. It anchors on context lines and `old_lines` within the patch. If your F# file has drifted or been modified since the patch was generated, `apply_patch` will fail with a `Failed to find context` error, preventing any accidental overwrites.
- How does Atlas ensure F# code formatting is consistent after refactoring?
- Atlas ensures F# code formatting consistency by running `fantomas` over any changed `.fs` files. This happens as part of the workflow, and the `fantomas`-driven changes are included in the unified diff presented for your approval, maintaining your project's F# style guidelines.
- Can I review Atlas's proposed changes to my F# code before they are applied?
- Absolutely. Atlas computes a unified diff for every file edit it proposes and surfaces it for your approval before writing. This gives you full transparency and control, allowing you to review every line change to your F# code and accept or reject them.
- Does Atlas support local F# development environments without sending code to third-party servers?
- Yes, Atlas can build its code index with local Ollama embeddings, keeping your F# code off third-party servers. This ensures that your proprietary F# codebase remains secure and private within your local development environment.
- How does Atlas help manage the migration of numerous F# callsites during a large refactor?
- Atlas uses the `todowrite` tool to help manage F# callsite migrations. After identifying all references with `lsp findReferences`, Atlas can add these to a `todowrite` list. This list tracks remaining callsites, ensuring that a partially migrated F# module is never mistaken for a finished one.
Try Atlas in your terminal
The terminal-native AI coding agent. Free core, single binary.
Install AtlasRelated guides
Refactor a Legacy Module with Atlas in 2026
How to refactor a legacy module with Atlas in 2026: findReferences maps every callsite, apply_patch refuses to apply against a drifted file, and bash proves behavior.
Atlas for F#: A Terminal-Native AI Coding Agent for .fsproj Solutions in 2026
Atlas is a terminal-native AI coding agent for F# in 2026. It respects .fsproj file order, maps discriminated unions, runs dotnet test behind a prompt, and runs Fantomas.
Trace a runtime bug from a stack trace in F# with Atlas in 2026
In 2026, F# developers can use Atlas to trace runtime bugs from production stack traces directly to the responsible line of code and apply a fix, all without attaching a debugger. Atlas integrates with your F#
Run Atlas Headless in CI for F# Projects in 2026
F# developers in 2026 can run Atlas headless in CI pipelines to automate code tasks. Get machine-readable output, integrate with dotnet test (Expecto), NuGet, and fantomas for robust F# development.
Migrate a deprecated API across every callsite in F# with Atlas in 2026
Efficiently migrate deprecated F# APIs across your entire codebase in 2026 using Atlas. Leverage dotnet test (Expecto), NuGet, and fantomas for a verified, complete transition.
Audit an F# Repository with Parallel Subagents in Atlas in 2026
Audit F# repositories in 2026 with Atlas's parallel subagents. Sweep code for problems using `dotnet test (Expecto)` and `fantomas` without blowing your main session's context window.
Review a Pull Request in F# with Atlas in 2026
In 2026, F# developers use Atlas to review pull requests, leveraging its deep understanding of .fsproj files, discriminated unions, and dotnet test (Expecto) to catch subtle bugs.
Upgrade a Dependency and Fix Breakage in F# with Atlas in 2026
Upgrade F# dependencies in 2026 with Atlas. direct bump NuGet packages, fix compile errors, and repair Expecto test failures using dotnet add package and fantomas for a smooth migration.