Stacks

Extract a Shared Helper From Duplicated Code in Ruby With Atlas (2026)

Updated 8 min read

Atlas extracts a shared Ruby helper by treating duplication as a semantic problem rather than a textual one. Because copies usually differ in variable names, Atlas asks codebase_search for the behavior instead of the exact code, confirms the hits with read, creates the new module with write, swaps each duplicate for a call using apply_patch one file per patch, and runs the RSpec suite with bash after every swap. Bundler resolves the Gemfile, RuboCop keeps the extracted module in house style, and every write shows a unified diff before it lands.

How does Atlas find duplicated logic in a Ruby codebase?

Atlas finds duplicated Ruby logic with codebase_search, which fuses 2 retrieval strategies, semantic and keyword, with reciprocal rank fusion. Duplication is a semantic problem, not a textual one: 2 copies of the same tax calculation in app/services and lib/ rarely share variable names, so grep misses them.

Ask codebase_search for the behavior, not the source. A query like "computes a prorated refund amount" surfaces PromotionCalculator#prorate in app/services/promotion_calculator.rb and a near-identical private method in lib/billing/refunds.rb even though neither file contains the word prorated. Atlas indexes Ruby by AST declarations using tree-sitter, not blind line windows, so each hit comes back as a whole def block with its file path and line range rather than a truncated window that cuts a method in half. That matters in Ruby, where a method body and its guard clauses only make sense together. Once codebase_search has ranked the candidates, use grep with a real regex to catch the mechanical cases too: constants, string keys, and any call sites in spec/ that pinned the duplicated behavior.

How do you confirm two Ruby methods are really duplicates before merging them?

Confirming duplicates in Ruby takes one Atlas tool: read. Open each of the 2 or 3 files codebase_search ranked highest and compare the method bodies line by line before collapsing them, because Ruby idioms like keyword arguments, blocks, and safe navigation can hide a real behavioral difference behind identical-looking code.

Read each hit in full, not just the matched hunk. Look specifically at what a Ruby copy tends to diverge on: a default value in a keyword argument, a `rescue` clause one copy has and the other does not, a `freeze` on a constant, or a block that yields where the sibling returns an array. Also read the specs. If spec/services/promotion_calculator_spec.rb asserts a rounding behavior that spec/billing/refunds_spec.rb does not, the two implementations are not interchangeable and the extraction has to preserve both cases. Atlas is a terminal-native TUI, so all of this happens in the same session where you will make the edit, and nothing is written yet. read is a read-only tool and every Atlas tool call is permission-gated against allow, ask, and deny rules before it runs.

How does Atlas create the shared Ruby module?

Atlas creates the shared Ruby module with the write tool, which shows the full diff in the permission prompt before the file is created. A new lib/billing/proration.rb holding 1 `module Proration` with a single `module_function` is the typical shape, and Atlas surfaces it for approval first.

Give Atlas the target path and the contract. In a Bundler project the natural home is lib/ for a gem or app/services/ for a Rails app, and Atlas will follow whichever convention it already saw in the repo. The write tool computes a unified diff for the new file and puts it in front of you before anything touches disk, so you review the module's public surface, its `module_function` versus instance-method choice, and its frozen string literal comment before approving. Write the RSpec example for the new module in the same pass: spec/billing/proration_spec.rb should cover the union of the behaviors the duplicates had, including the rounding case only one of them tested. Run `bundle exec rspec spec/billing/proration_spec.rb` before you touch a single caller, so the helper is proven green on its own.

How do you replace each duplicate with apply_patch without breaking the Ruby suite?

Replace each Ruby duplicate with apply_patch, 1 file per patch, so each swap is independently reviewable and revertible. Atlas computes a unified diff for every file edit and surfaces it for approval before writing, and running `bundle exec rspec` between patches means a failure points at exactly 1 file.

The rhythm is patch, test, patch, test. apply_patch turns app/services/promotion_calculator.rb into a `require` plus a call to `Proration.prorate(...)` and deletes the old private method. Run `bundle exec rspec spec/services/promotion_calculator_spec.rb` immediately. Then do lib/billing/refunds.rb and run its spec. Because Atlas snapshots file changes as git patches, an extraction that turns out to be wrong can be diffed and rolled back rather than hand-reverted. Do not batch all the swaps into one patch: a five-file patch that fails leaves you guessing which caller broke. When every caller is migrated, run the full suite with `bundle exec rspec`, run `bundle exec rubocop` so RuboCop is satisfied with the new module's style, and finish by grepping for any surviving copy of the old method name to prove there are zero remaining hits.

How does review and safety work when Atlas edits Ruby files?

Atlas gates every tool call in a Ruby project against 3 rule types, allow, ask, and deny, before it runs. Atlas also computes a unified diff for every file edit and surfaces it for approval before writing, and snapshots file changes as git patches, so any extraction can be rolled back.

A helper extraction touches several files, which is exactly when an agent's edits need to be gated. Atlas reads git branches, status, and diffs, and can stage and create commits on your behalf, so you can see the whole extraction as a working diff before committing it. If you want the code index built without sending Ruby source to a third party, Atlas can build its code index with local Ollama embeddings, keeping code off third-party servers, which is often the deciding factor for a private Bundler-managed gem. For research passes that should never write, Atlas drafts a plan in a read-only plan agent and asks before switching to a build agent. In practice that means you can let Atlas hunt for the duplicates freely and only grant edit permission at the moment the new module is written.

Step by step

  1. 01Start Atlas in the Ruby project root, the directory that holds the Gemfile, so Bundler and the app tree are both in scope.
  2. 02Ask codebase_search for the duplicated behavior in plain language, for example "prorates a refund by days remaining", to surface near-duplicate Ruby methods that grep would miss because the copies use different variable names.
  3. 03Open each candidate with read and confirm the method bodies are genuinely equivalent, checking Ruby specifics like keyword-argument defaults, rescue clauses, and whether one copy yields a block.
  4. 04Create the shared module with write, for example lib/billing/proration.rb, and review the unified diff in the permission prompt before the file is created.
  5. 05Write the RSpec example for the new module at spec/billing/proration_spec.rb and prove it green with `bundle exec rspec spec/billing/proration_spec.rb` before touching any caller.
  6. 06Replace each duplicate with apply_patch, one file per patch, so every swap is independently reviewable and revertible.
  7. 07Run the suite with bash after every swap, using `bundle exec rspec` on the affected spec file first, then the full suite.
  8. 08Run `bundle exec rubocop` so RuboCop passes on the extracted module, then grep for the old method name to confirm no surviving copy remains.

Frequently asked questions

how to find duplicate code in a Ruby project with AI
Ask Atlas's codebase_search for the behavior rather than the code. Atlas fuses semantic and keyword retrieval with reciprocal rank fusion and indexes Ruby by AST declarations using tree-sitter, so two copies of the same method with different variable names both rank, even though grep would match neither.
does Atlas run bundle exec rspec after refactoring Ruby code
Yes. Atlas's documented extraction workflow runs the suite with bash after every swap, not once at the end. In a Bundler project that means `bundle exec rspec` on the affected spec after each apply_patch, so a red test points at exactly one file.
can Atlas create a new Ruby module in lib without me approving it
No. Every Atlas tool call is permission-gated against allow, ask, and deny rules before it runs, and the write tool shows the full diff in the permission prompt before the file is created. The new lib/ module is only written after you approve that diff.
how do I undo an Atlas refactor in a Ruby repo
Atlas snapshots file changes as git patches, so edits can be diffed and rolled back. Because apply_patch applies one file per patch, you can revert a single caller's swap without unwinding the whole extraction, then re-run `bundle exec rspec`.
will Atlas keep my extracted Ruby module RuboCop clean
Run `bundle exec rubocop` with Atlas's bash tool after the module lands and have Atlas fix what RuboCop reports. RuboCop is the formatter in the documented Ruby setup, alongside RSpec as the test runner and Bundler as the package manager.
can I use Atlas on a private Ruby gem without sending code to a cloud provider
Atlas can build its code index with local Ollama embeddings, keeping code off third-party servers. That lets codebase_search work over a private Bundler-managed gem while the index stays on your machine.
why does Atlas patch one Ruby file at a time instead of all at once
Atlas replaces each duplicate with apply_patch one file per patch so each swap is independently reviewable and revertible. A single patch spanning five callers that fails leaves you guessing which one broke; one patch per file plus `bundle exec rspec` between them does not.
how do I start Atlas in a Ruby project
Run atlas in a project with a Gemfile. Atlas then reads your modules, gems, and Rakefile tasks, and you can ask it to write RSpec examples or extract a module, reviewing the diff before it is written.

Try Atlas in your terminal

The terminal-native AI coding agent. Free core, single binary.

Install Atlas

Related guides

Extract a Shared Helper from Duplicated Code with Atlas (2026 Workflow)

How to extract a shared helper from duplicated code with Atlas in 2026: codebase_search finds the copies by meaning, write creates the module, apply_patch swaps each call.

Review a Pull Request in Ruby with Atlas (2026)

Review a Ruby pull request in 2026 with Atlas: bash produces the raw patch, read pulls whole files, findReferences checks callers, and RSpec and RuboCop close the loop.

Locate Where a Behavior Is Implemented in Ruby with Atlas (2026)

Find the exact Ruby file and method behind a behavior in 2026. Atlas combines codebase_search, grep, read, and lsp across your Gemfile, app tree, and RSpec suite.

Diagnose a hanging or long-running command in Ruby with Atlas (2026)

How Atlas diagnoses a hanging or long-running Ruby command in 2026: read the shell_metadata block, tell a blocked Bundler prompt from a genuinely slow RSpec run.

Self-review your working diff before committing in Ruby with Atlas in 2026

In 2026, Ruby developers use Atlas to self-review uncommitted diffs, catching mistakes before CI. Leverage RSpec, Bundler, and RuboCop with Atlas's terminal-native AI agent.

Trace a Runtime Bug From a Stack Trace in Ruby with Atlas (2026)

How to go from a Ruby backtrace to the responsible line with Atlas in 2026: read each frame at its offset, grep the error string, walk callers, then lock it with RSpec.

Rename a Symbol Across a Ruby Repo with Atlas (2026)

How Atlas renames a Ruby method, class, or constant repo-wide in 2026: lsp findReferences for the true reference set, grep for strings, edit with replaceAll, RSpec to prove it.

Document a Ruby Module with a README in 2026 using Atlas

For Ruby developers in 2026, Atlas generates accurate README documentation for modules by analyzing live code, integrating with Bundler and RSpec, and ensuring all examples are verified.

Browse this resource hub