# RefactorFirst: ranking which cycles to break in a Java codebase

> RefactorFirst analyses a Java or Kotlin repository for class and package cycles and object oriented disharmonies, then uses feedback vertex set and feedback arc set algorithms to tell you which relationships to remove first, rendered as a browsable single page report. It suits a team with a tangled codebase that needs a defensible starting order, and it needs a JDK and Maven, and a great deal of patience on a large repository.

**refactorfirst/RefactorFirst** — Identifies and prioritizes Class and Package Cycles and Anti-pattens in Java & Kotlin codebases you should refactor first.

- Repository: https://github.com/refactorfirst/RefactorFirst
- Website: https://refactorfirst.github.io/
- Stars: 548 · Forks: 46
- Language: Java
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/refactorfirst-refactorfirst

## The problem RefactorFirst solves: cycles and disharmonies in Java code

RefactorFirst is a tool for Java codebases that helps you identify what to refactor first. It finds class and package cycles, and it finds object oriented disharmonies, which the README calls anti-patterns, identified in Object Oriented Metrics in Practice.

The framing of the tool is the interesting part. It is not a style checker and not a lint. It answers an ordering question. In a codebase with hundreds of coupled packages, knowing that a cycle exists is not useful, because there are too many and removing any one of them usually breaks another. What you need is a starting order, and a defensible reason for that order.

The output is a single page application generated by scanning your Git repository. It runs cycle analysis on your source code using the OpenRewrite Java parser and JGraphT, and it runs what-if analysis to identify the most optimal relationships in a class cycle to remove. The code map viewers are powered by 3D Force Graph, sigma.js and GraphViz DOT, with Vizdom rendering the DOT graph.

There is a scale limit worth knowing. If there are more than 4000 classes and relationships, a simplified 3D viewer will be used to avoid page load slowdowns, and the README says features will be toggleable in the 3D UI in a future release.

The project is Apache-2.0 licensed, the homepage is refactorfirst.github.io, the last push to the tree was on 2026-10-17, and the releases have been moving: 0.10.0 on 2026-09-07, 0.9.0 on 2026-07-03 and 0.8.0 on 2026-02-24.

## The feedback set algorithms behind the ranking

The ranking is produced by two graph algorithms, and they are the reason the tool produces a specific order rather than a list of cycles.

Cycle analysis is performed with Directed Feedback Vertex Set and Directed Feedback Arc Set algorithms, used to identify the optimal classes and relationships between classes for removal to get rid of cycles. A feedback vertex set is a set of nodes whose removal leaves an acyclic graph, and a feedback arc set is the equivalent over edges. Computing a minimum such set is hard, and the README points at two papers for the specific algorithms used, one on a directed feedback vertex set and one on a feedback arc set.

The implementation is described as kernelized, and the repository has a `graph-algorithms/` module with two separate flow diagrams in `DIAGRAM.md` files, one for the vertex kernelized algorithm under an `org.hjug.feedback.vertex.kernelized` package and one for the arc kernelized algorithm under `org.hjug.feedback.arc.pageRank`. The arc variant, you will notice, is built on a PageRank-based approach, which is how the algorithm decides which edges carry the most weight to cut.

The README is direct about the cost. These algorithms are powerful and will push your CPU to its limits for large codebases, though they do play nice and shouldn't slow your computer down. The top level directory listing shows the project broken into focused modules that mirror the analysis pipeline: `codebase-graph-builder/`, `change-proneness-ranker/`, `effort-ranker/`, `cost-benefit-calculator/`, `graph-data-generator/`, `coverage/`, `report/` and `cli/`, plus the two plugin directories.

The algorithms are also usable on their own. The README states that these graph algorithms can be used outside of RefactorFirst, which matters if you want the ranking for a different purpose than an HTML report.

## Reading the Relationship Removal Priority table

The Relationship Removal Priority table is the part of the report you will actually act on, and the README explains how to read it in detail.

The table shows the most optimal relationships to remove from your codebase to remove all cycles. It is sorted by the number of cycles that a relationship exists in, and then by the change proneness of the classes in the relationship. The first key is a raw count: a relationship sitting in ten cycles is a better candidate than one sitting in two, because cutting it does more work. The second key is how likely the classes are to change, and that is the tiebreaker that reflects cost, since breaking a dependency in code that never changes is cheap while breaking one in volatile code is expensive.

The formatting encodes a recommendation. Classes that should be broken apart or removed from the codebase have an asterisk. If only one class is bold, the shared functionality should be moved to the non-bold class or classes. If neither class or both classes are bold, the guidance is to examine both classes carefully, reassess the responsibilities of the classes and then refactor to remove the relationship, and if one or both classes are identified as a disharmony, to follow the guidance provided for that disharmony.

That last branch is where the anti-pattern analysis feeds into the cycle analysis. A cycle involving a class that is also a God Class is a different problem from a cycle between two well-factored classes, and the table points you at the disharmony guidance in the first case.

The change proneness ranking has its own module in the repository, `change-proneness-ranker/`, and the effort ranking has `effort-ranker/`, with `cost-benefit-calculator/` presumably combining them. The `coverage/` directory and the presence of test resources in the tree suggest the analysis is also aware of which parts of the codebase are exercised by tests.

## Running it: one Maven goal for the full report

The quick path is a single Maven goal run from the top level directory of the project, and the source code does not need to be built first. You need Git, Java 17 or newer and Maven 3 installed. The same command analyses Maven and non-Maven projects.

```bash
mvn org.hjug.refactorfirst.plugin:refactor-first-maven-plugin:0.10.0:htmlReport
```

The report lands at `target/site/refactor-first-report.html` in your project. The README notes that great effort has been taken to make both the analysis and page rendering times as fast as possible, and it links two sample reports, a JUnit 4 report and an FXGL report which is the Java and Kotlin one.

There is a lighter output for automation. The `simpleHtmlReport` goal generates a simplified HTML report with no graphs or images, designed to be the output of a GitHub Actions step, where the report is appended to the step summary.

```bash
mvn -B clean test \
org.hjug.refactorfirst.plugin:refactor-first-maven-plugin:0.10.0:simpleHtmlReport \
&& echo "$(cat target/site/refactor-first-report.html)" >> $GITHUB_STEP_SUMMARY
```

The distinction between the two goals is the sensible split for a team. The full report is a document for a human to explore, and it carries the 3D and graph views that make the structure legible. The simple report is a text summary for a pull request, where the point is that the numbers moved, not that someone can explore the map.

For a build, the plugin can also be added to the build section of your project, with a `showDetails` option that shows God Class metrics and rankings in the generated table.

## Wiring it into Maven, Gradle and CI

Beyond the command line, the plugin integrates with the build in the conventional ways.

In a Maven build section, the plugin coordinates are the group, the artifact and a version, with an optional configuration block.

```xml
<build>
    <plugins>
        ...
        <plugin>
            <groupId>org.hjug.refactorfirst.plugin</groupId>
            <artifactId>refactor-first-maven-plugin</artifactId>
            <version>0.10.0</version>       
            <!-- optional -->
            <configuration>
                <showDetails>false</showDetails>
            </configuration>
        </plugin>
        ...
    </plugins>
</build>
```

In the reporting section, a RefactorFirst report will show up in the site report when you run `mvn site`, using the same coordinates without the configuration block.

```xml
<reporting>
    <plugins>
        ...
        <plugin>
            <groupId>org.hjug.refactorfirst.plugin</groupId>
            <artifactId>refactor-first-maven-plugin</artifactId>
            <version>0.10.0</version>       
        </plugin>
        ...
    </plugins>
</reporting>
```

The repository also contains a `refactor-first-gradle-plugin/` directory, so a Gradle path exists as well, and the `.refactorfirst/` directory at the root is the tool's own configuration location in the repository. A `CLAUDE.md` and an `AGENTS.md` sit at the root for contributor guidance, and there is a `jreleaser.yml` for the release automation.

The GitHub Actions route is the one that fits a pull request workflow, and the README links to the GitHub blog post about job summaries, which is the feature it leans on.

## The configuration knobs, and what they cost you

The defaults are described as sensible, and the override table is short, which is a good sign for a tool that most people will run with no configuration at all.

`showDetails` shows God Class metrics and rankings in the generated table, and defaults to false. `backEdgeAnalysisCount` is the number of back edges in a cycle to analyze, and defaults to 50. Its behaviour is more subtle than the name suggests: if the total number of back edges is greater than the value specified, it analyzes the number of minimum weight edges specified, and if 0 is specified, all back edges will be analyzed. `analyzeCycles` toggles the cycle analysis, and the README's example of overriding from the command line is `-DbackEdgeAnalysisCount=0` or `-DanalyzeCycles=false`.

Those overrides can be given with `-D` when running from the command line, or in the configuration section when including the plugin in a Maven build.

The `backEdgeAnalysisCount` default of 50 is the one to understand, because it is where the runtime cost is controlled. A cycle with a small number of back edges gets analysed in full, and past the threshold the tool switches to analysing the minimum weight edges instead. Setting it to zero means every back edge is analysed, which is the thorough setting and the expensive one, and it is also the setting that will push the CPU as the README warns.

On the Java version, the requirements are specific. Java 17 or newer is required to run RefactorFirst, and Java 25 codebase analysis is supported: run it on a Java 25 or newer runtime and the Java 25 parser is used automatically, while on older runtimes the analysis falls back to the parser matching your runtime. The Maven and Gradle plugins themselves still only require a Java 17 runtime, so the plugin runs on 17 while the analysis can use a newer parser. The README also asks you to use a recent JDK release of the Java version you are using, warning that an old JDK release of the same version may cause issues during analysis.

## Where the approach stops, and what to do instead

There are three limitations here, and the first is about what the tool is measuring rather than how well it measures it.

A cycle is a structural property, and a cycle in a codebase can be perfectly justified. An interface defined in the domain layer and implemented by an adapter in the infrastructure layer produces a dependency edge that a naive cycle detector will flag and a careful architect will defend. The tool tells you which relationships to cut to break all cycles, which is a well-defined combinatorial answer, but it does not tell you which of those cuts your architecture should tolerate. The README's own guidance for the table makes this point: examine both classes carefully, reassess the responsibilities of the classes and then refactor. The ranking is a starting point for judgement, not a work list.

The second limitation is cost. The algorithms will push your CPU to their limits on large codebases, and the whole pipeline parses the source with OpenRewrite, builds a class graph, runs the feedback set algorithms, computes change proneness and effort, and then renders a page. On a repository with thousands of classes, that is a job for a laptop running overnight rather than a step in a pull request build.

The third is scope. The tool is about cycles and disharmonies in Java and Kotlin, applied through Maven and Gradle plugins. It does not do general architectural analysis, it does not detect design problems that are not cycles, and it is not a substitute for a human deciding what the module boundaries should be.

The alternative approach worth naming is a build-time dependency rule. Instead of finding cycles after the fact and ranking them, you can forbid them at compile time with a tool such as ArchUnit or a forbidden dependency declaration in the build, and prevent new cycles from appearing while you clean up the old ones. The difference in approach is precise: RefactorFirst is diagnostic and runs once to produce a plan, while a dependency rule is preventive and runs on every build. For a codebase that already has hundreds of cycles, the preventive rule is useless on its own because nothing ever compiles. For a codebase that is nearly clean, the rule is cheaper, faster and more durable. The sensible combination is to use RefactorFirst to produce the ranked removal plan, then add a rule to keep the count from growing.

## Conclusion

Adopt RefactorFirst if your Java or Kotlin codebase has accumulated package and class cycles and you need a defensible order for breaking them, because the what-if analysis turns a vague cleanup into a ranked list of specific relationships. Do not adopt it as a general quality gate in a build, since the analysis is expensive and the report is for a human to act on. Before running it on a real repository, run the `htmlReport` goal once on a small module to check the runtime cost, and read the Relationship Removal Priority table as a ranking rather than a prescription, since the guidance in the README sends you back to the classes to reassess responsibilities before changing anything.

## FAQ

### How do I run RefactorFirst on a Java project?

From the top level directory of the project, with Git, Java 17 or newer and Maven 3 installed, run `mvn org.hjug.refactorfirst.plugin:refactor-first-maven-plugin:0.10.0:htmlReport`. The source does not need to be built first, and the report is written to `target/site/refactor-first-report.html`.

### How does RefactorFirst decide which relationships to remove first?

It uses Directed Feedback Vertex Set and Directed Feedback Arc Set algorithms to find the optimal classes and relationships to remove to eliminate cycles. The Relationship Removal Priority table is then sorted by the number of cycles a relationship appears in, and then by the change proneness of the classes involved.

### Can I use RefactorFirst in a GitHub Actions workflow?

Yes, using the `simpleHtmlReport` goal, which produces a simplified HTML report with no graphs or images, and appending it to the GitHub step summary. The README gives the full command that runs the goal and echoes the report into `$GITHUB_STEP_SUMMARY`.

### Which Java versions does RefactorFirst support?

Java 17 or newer is required to run the tool, and the Maven and Gradle plugins themselves still only require a Java 17 runtime. Java 25 codebase analysis is supported, and on a Java 25 or newer runtime the Java 25 parser is used automatically, with older runtimes falling back to the matching parser.

### What does the backEdgeAnalysisCount option do?

It is the number of back edges in a cycle to analyze and defaults to 50. If the total number of back edges is greater than that value, the tool analyses the number of minimum weight edges instead, and if you specify 0 all back edges are analysed. You can set it with `-DbackEdgeAnalysisCount=0` on the command line or in the plugin configuration.

## Sources

- [License: Apache-2.0](https://github.com/refactorfirst/RefactorFirst/blob/main/LICENSE)
- [Project website](https://refactorfirst.github.io/)
- [README](https://github.com/refactorfirst/RefactorFirst/blob/main/README.md)
- [refactorfirst/RefactorFirst on GitHub](https://github.com/refactorfirst/RefactorFirst)
- [Releases](https://github.com/refactorfirst/RefactorFirst/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/refactorfirst-refactorfirst
