runtimeanalyz.ing

← All capabilities

dead_api

LSP · CLI Pro

Find dead public API. The public methods that no caller reached in any recorded run, including those whose only caller is their own spec. Shown as a hint in your editor.

Enrollment app code
unlocked_for ( student Student )
42 app runs
Curriculum
class Curriculum
  def unlocked_for(student) = …
  def archived_books = …
end
curriculum_spec.rb:40 its own spec
archived_books
its only caller
nothing no caller
legacy_totals
never reached
Report
class Report
  def legacy_totals = …
end
Every public method, with its recorded callers. archived_books is reached by the spec written for it and by no other code, and legacy_totals has no caller, so both are shown with a hint in your editor.

The problem

A public method is hard to remove, because it is hard to be sure that nothing still calls it. If nobody knows whether Curriculum#archived_books has a caller, it stays, and it goes on being tested, documented and maintained.

A static tool cannot answer the question in Ruby. A method can be called through send, public_send, a callback or a name built from a string, so a search that finds no references does not show that the method is unused.

Test-first development adds a second kind of dead method: one you wrote a test for and made pass, which no other code then needed. Its spec calls it, so a search for callers finds one. This is speculative generality, and such a method is cheapest to delete while it is still new.

The fix

In your editor, opening app/models/curriculum.rb publishes a hint on each dead method’s line:

Curriculum#name: no caller in any recorded run
Curriculum#archived_books: only caller: its own spec (spec/models/curriculum_spec.rb:39)

The command line gives the same report for a file or a directory:

$ ra dead_api app/models/curriculum.rb

dead_api app/models/curriculum.rb
· 13 public methods · 1 dead · 1 spec-only
· Curriculum#name: no caller in any recorded run
  app/models/curriculum.rb:11
· Curriculum#archived_books: only caller: its own spec (spec/models/curriculum_spec.rb:39)
  app/models/curriculum.rb:37

Over all of app/models it finds 107 public methods, 10 dead and 15 spec-only. Among them are Report#legacy_totals, which has no caller in any recorded run, and Report#csv_rows, whose only caller is spec/models/report_spec.rb:7.

Delete the method along with the spec that called it. The suite still passes, because that spec was the only code that needed the method. If a rake task or a job you didn’t record does need it, you will find out when you record that run.

How it works

The static index lists every public method the source declares. The recorder wraps every method defined in your app, so a call is recorded however it was made: directly, through send or public_send, or from a callback. dead_api joins the index to the recording.

It flags two kinds of method:

  • A method with no recorded caller.
  • A method whose only callers are its own spec examples. Each call tree is stamped with the example that produced it, so the tool can tell curriculum_spec.rb calling archived_books directly apart from app code calling it.

Run it on your app

The output on this page comes from our example Rails app, and it is regenerated whenever a tool changes. These are the commands behind it. On your own app, run ra record first, then put your own class, method or file in their place.

ra dead_api app/models
ra dead_api app/models/curriculum.rb

The flagged methods are app/models/curriculum.rb:37 and app/models/report.rb:6 and :14.

Reading the result

  • The result is as complete as your recording. A method called by a rake task, a rarely hit path or a job you didn’t run is reported as dead. Record those runs, or treat each result as a method to check before you delete it.
  • It publishes hints. It does not remove a method and it does not fail a build.
  • prod_dead_api asks the same question of the app-run trees only, which gives proof from traffic.

Every tool reads the same recording. What gets recorded and how far to trust an answer are on the questions page.

split_interface

CLI · LSP · MCP Pro

The role interfaces in a class. Groups a class's public methods by which callers used them in the recording, and lists each group as a role interface to extract.

See more →

specs_that_reach

CLI · LSP · MCP Pro

Run only the specs that reach your change. The spec examples whose recorded runs reach a method. After a change you rerun those examples alone, and the red-green loop takes seconds.

See more →

observed_shape

CLI · LSP · MCP Pro

The interface a call depends on. The messages your code sends to a value, read from recorded runs. They are the interface a call depends on, whether or not anyone declared it.

See more →