Skip to content

Stabilize ModuleDependency descriptions - #1648

Merged
hankem merged 1 commit into
TNG:mainfrom
DragonFSKY:fix-module-dependency-description-order
Aug 2, 2026
Merged

Stabilize ModuleDependency descriptions#1648
hankem merged 1 commit into
TNG:mainfrom
DragonFSKY:fix-module-dependency-description-order

Conversation

@DragonFSKY

@DragonFSKY DragonFSKY commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Fixes #1630.

This canonicalizes the emitted class-dependency descriptions lexicographically before joining them, so the same dependency set produces stable ModuleDependency report text independently of the backing set encounter order. The ordering is defined at the textual representation boundary and does not depend on Dependency's line-number-first natural order.

The regression test imports real classes, forces opposite dependency encounter orders, and verifies that Dependency natural order conflicts with description order. It therefore fails both without sorting and with dependency-object sorting, while passing with description-string sorting.

Tested with:

./gradlew :archunit:clean :archunit:test --tests com.tngtech.archunit.library.modules.ModuleDependencyTest
./gradlew :archunit:spotlessCheck
./gradlew :archunit:check

@DragonFSKY
DragonFSKY force-pushed the fix-module-dependency-description-order branch 2 times, most recently from f934965 to 13ac918 Compare July 1, 2026 02:06
@hankem

hankem commented Jul 9, 2026

Copy link
Copy Markdown
Member

Thank you so much for your contribution, and first of all sorry for the late feedback!

The only thing I'd like to test is how much sorting affects the performance, but other than that, I have no concerns that we can integrate your fix soon.

@DragonFSKY
DragonFSKY force-pushed the fix-module-dependency-description-order branch 2 times, most recently from c47468c to 77dfee9 Compare July 17, 2026 09:36
@DragonFSKY

Copy link
Copy Markdown
Contributor Author

Thanks for taking a look, and no worries about the timing! I fixed the DCO issue and normalized both commits to the same GitHub identity/sign-off.

I also ran a targeted local benchmark on JDK 25 using real ModuleDependency instances built from ArchUnit classes. It compared the previous unsorted description rendering with the sorted rendering in this PR; the figures below are medians from seven alternating rounds:

Class dependencies Unsorted Sorted Added time
81 17.3 us 22.0 us 4.7 us
926 126.4 us 455.7 us 329.3 us
5,449 1.35 ms 3.41 ms 2.05 ms

So the relative cost is visible for large dependency sets, but the absolute overhead stayed around 2 ms even for the largest real module dependency in this sample, and it is limited to the getDescription/reporting path. I hope this helps with the performance assessment.

Comment on lines 87 to 88
.sorted()
.map(HasDescription::getDescription)

@hankem hankem Aug 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your performance measurements already!

As expected, sorting adds some time. 🤷‍♂️

In an even more extreme test case of

JavaClasses classes = new ClassFileImporter().importPackages("org.hibernate");
ArchModules<?> modules = ArchModules.defineByPackages("org.hibernate.(*)..").modularize(classes);
long t0 = System.nanoTime();
modules.stream()
	.flatMap(m -> m.getModuleDependenciesFromSelf().stream())
	.forEach(md -> md.getDescription());  // this is where .sorted() now runs
long duration = System.nanoTime() - t0;

on hibernate-core 7.4.5.Final (where one ModuleDependency consists of 11 418 classDependencies),

I measured duration to increase from 30±4 ms to 114±6 ms.

We could, however, easily save some time by not sorting Dependency objects, but just their description Strings:

Suggested change
.sorted()
.map(HasDescription::getDescription)
.map(HasDescription::getDescription)
.sorted()

In my case that at least brings us down to 88±6 ms.

What do you think?

@DragonFSKY DragonFSKY Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion and the measurements! I’ve updated the PR to sort the rendered descriptions and added a regression test for stable ordering. The checks pass locally.

Sort rendered class dependency descriptions lexicographically so module violation text does not depend on set encounter order. Add a regression test that distinguishes description order from Dependency's line-number-first natural order.

Fixes TNG#1630

Signed-off-by: DragonFSKY <38503900+DragonFSKY@users.noreply.github.com>
@DragonFSKY
DragonFSKY force-pushed the fix-module-dependency-description-order branch from 77dfee9 to c62a6d8 Compare August 2, 2026 12:17

@hankem hankem left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution and the test that clearly demonstrates which order is considered!

@hankem
hankem merged commit 1a33eea into TNG:main Aug 2, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nondeterministic violation description for ModuleDependency: leads to failing test when used as frozen rule

2 participants