Skip to content

Display hints when the glue packages are not efficiently configured#3151

Open
jkronegg wants to merge 10 commits intomainfrom
loading_glue_hint
Open

Display hints when the glue packages are not efficiently configured#3151
jkronegg wants to merge 10 commits intomainfrom
loading_glue_hint

Conversation

@jkronegg
Copy link
Contributor

@jkronegg jkronegg commented Jan 24, 2026

🤔 What's changed?

When the JavaBackend loads many classes, but only a few steps/hooks, then we log an hint message to improve the situation.
The hint message can suggest (when applicable and by decreasing efficiency):

  • to configure the glue package property
  • to remove classes from the glue packages
  • to make public static inner classes private
  • to remove non-public static inner classes from the glue packages

This replaces the WARN message which occurs when the glue property is not configured.

Here is an example:

[INFO]      [java] INFO: Scanning the glue packages took 1163 ms for 3016 classes, but only 1 of them are Cucumber glue items. You could gain about 1163 ms by cleaning the glue package. Some advices (by decreasing efficiency):
[INFO]      [java] 1) By default Cucumber scans the entire classpath for step definitions.
[INFO]      [java] You can restrict this by configuring the glue path.
[INFO]      [java] 
[INFO]      [java] Examples:
[INFO]      [java]  - @CucumberOptions(glue = "com.example.application")
[INFO]      [java]  - @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "com.example.application")
[INFO]      [java]  - src/test/resources/junit-platform.properties   cucumber.glue=com.example.application
[INFO]      [java]  - src/test/resources/cucumber.properties         cucumber.glue=com.example.application
[INFO]      [java] 
[INFO]      [java] 2) remove the classes that do not contain cucumber step/hooks/injectors, e.g.:
[INFO]      [java] net.bytebuddy.implementation.EqualsMethod$ValueComparator$1
[INFO]      [java] net.bytebuddy.implementation.bytecode.assign.Assigner$1
[INFO]      [java] net.bytebuddy.implementation.bytecode.Removal$1
[INFO]      [java] net.bytebuddy.dynamic.loading.ByteArrayClassLoader$PersistenceHandler$1
[INFO]      [java] net.bytebuddy.description.annotation.AnnotationValue$ForConstant$PropertyDelegate$ForNonArrayType$4
[INFO]      [java] net.bytebuddy.implementation.bytecode.member.Invokedynamic
[INFO]      [java] net.bytebuddy.build.Plugin$Engine$ErrorHandler$Failing$2
[INFO]      [java] net.bytebuddy.matcher.AccessibilityMatcher
[INFO]      [java] net.bytebuddy.utility.dispatcher.JavaDispatcher$1
[INFO]      [java] net.bytebuddy.implementation.HashCodeMethod$ValueTransformer$4
[INFO]      [java] 
[INFO]      [java] 3) for classes that contain steps/hooks/injectors, change public static inner classes to private (or remove them from the glue package), e.g.:
[INFO]      [java] net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider
[INFO]      [java] net.bytebuddy.implementation.attribute.FieldAttributeAppender$ForInstrumentedField
[INFO]      [java] net.bytebuddy.description.method.ParameterDescription$InGenericShape
[INFO]      [java] net.bytebuddy.pool.TypePool$Default$LazyTypeDescription$GenericTypeToken$Resolution
[INFO]      [java] net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods
[INFO]      [java] net.bytebuddy.asm.Advice$OffsetMapping$Sort
[INFO]      [java] net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Validator
[INFO]      [java] net.bytebuddy.implementation.Implementation$Context$Default
[INFO]      [java] net.bytebuddy.asm.Advice$BootstrapArgumentResolver$ForDefaultValues
[INFO]      [java] net.bytebuddy.asm.MemberSubstitution$Substitution$Chain$Step$ForDelegation$BootstrapArgumentResolver$Factory
[INFO]      [java] 
[INFO]      [java] 4) for classes that contain steps/hooks/injectors, remove non-public static inner classes from the glue package, e.g.:
[INFO]      [java] net.bytebuddy.agent.builder.AgentBuilder$Default$Transforming
[INFO]      [java] net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector$ForRetransformation
[INFO]      [java] net.bytebuddy.pool.TypePool$Default$GenericTypeExtractor$ForSignature$OfRecordComponent
[INFO]      [java] net.bytebuddy.asm.Advice$Dispatcher$RelocationHandler$ForType$Bound
[INFO]      [java] net.bytebuddy.implementation.bytecode.constant.MethodConstant$CanCacheIllegal
[INFO]      [java] net.bytebuddy.pool.TypePool$Default$WithLazyResolution$LazyResolution
[INFO]      [java] net.bytebuddy.agent.builder.AgentBuilder$Default$Redefining
[INFO]      [java] net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForOwnerType$AnnotatedType
[INFO]      [java] net.bytebuddy.implementation.bytecode.constant.MethodConstant$PrivilegedLookup
[INFO]      [java] net.bytebuddy.pool.TypePool$Default$LazyTypeDescription$GenericTypeToken$ForGenericArray$LazyGenericArray

The behavior can be controlled through properties:

  • cucumber.glue.hint.enabled: determine if the hint is displayed or not (by default true)
  • cucumber.glue.hint.threshold: the expected gain threshold in milliseconds (by default 100)

⚡️ What's your motivation?

Helping the developer to configure Cucumber helps to get good performances.
Contributes to #3141.

🏷️ What kind of change is this?

  • ⚡ New feature (non-breaking change which adds new behaviour)

♻️ Anything particular you want feedback on?

Up to 10 classes are displayed in each hint category: the idea is to give an idea to the developer, not to be exhaustive.
The log message is displayed only if the expected gain is higher than 100 ms.

📋 Checklist:

  • I agree to respect and uphold the Cucumber Community Code of Conduct
  • I've changed the behaviour of the code
    • I have added/updated tests to cover my changes.
  • My change requires a change to the documentation.
    • I have updated the documentation accordingly.
  • Users should know about my change
    • I have added an entry to the "Unreleased" section of the CHANGELOG, linking to this pull request.

@jkronegg jkronegg requested a review from mpkorstanje January 24, 2026 22:42
@jkronegg jkronegg marked this pull request as draft January 24, 2026 22:52
@jkronegg jkronegg marked this pull request as ready for review January 25, 2026 00:52
@jkronegg
Copy link
Contributor Author

@mpkorstanje any suggestion on how to solve the semver issue ?

@mpkorstanje
Copy link
Contributor

For now don't worry about it.

Copy link
Contributor

@mpkorstanje mpkorstanje left a comment

Choose a reason for hiding this comment

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

Looks good.

  1. Please reduce the incidental details in loadGlue. At a high level should read something like:
if options.isGlueHintEnabled():
    if glueMetrics.expectedGains() > options.getGlueHintThreshold():
        log.info(glueMetrics.performanceHintMessage())

A separate class might be needed to track the metrics and information. But that is okay.

  1. The changes to BackendProviderService are breaking. But we can provide the Options by extracting a request object from loadGlue and provide the options there. This would dovetail nicely into #3120. So for now just keep BackendProviderService as is.

Julien Kronegg added 3 commits February 17, 2026 13:45
# Conflicts:
#	CHANGELOG.md
#	cucumber-core/src/main/java/io/cucumber/core/backend/Options.java
#	cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptions.java
#	cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptionsBuilder.java
#	cucumber-core/src/test/java/io/cucumber/core/runtime/ObjectFactoryServiceLoaderTest.java
#	cucumber-junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberConfiguration.java
@mpkorstanje mpkorstanje self-requested a review February 18, 2026 14:52
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.

2 participants