From 98ed6b36e82ac3b3fa5d1d50e7ea9e3055654f06 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 30 Nov 2023 15:54:43 +0100 Subject: [PATCH 1/6] [doc] ADR 3 - API principles --- docs/pages/pmd/projectdocs/decisions/adr-3.md | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 docs/pages/pmd/projectdocs/decisions/adr-3.md diff --git a/docs/pages/pmd/projectdocs/decisions/adr-3.md b/docs/pages/pmd/projectdocs/decisions/adr-3.md new file mode 100644 index 0000000000..1ad5cda493 --- /dev/null +++ b/docs/pages/pmd/projectdocs/decisions/adr-3.md @@ -0,0 +1,162 @@ +--- +title: ADR 3 - API evolution principles +sidebar: pmd_sidebar +permalink: pmd_projectdocs_decisions_adr_3.html +sidebaractiveurl: /pmd_projectdocs_decisions.html +adr: true +# Proposed / Accepted / Deprecated / Superseded +adr_status: "Proposed" +last_updated: November 2023 +--- + + + +# Context + +The API of PMD has been growing over the years and needed some cleanup. The goal is, to +have a clear separation between a well-defined API and the implementation, which is internal. +This should help us in future development. + +Until now, all released public members and types were implicitly considered part +of PMD's public API, including inheritance-specific members (protected members, abstract methods). +We have maintained those APIs with the goal to preserve full binary compatibility between minor releases, +only breaking those APIs infrequently, for major releases. + +In order to allow PMD to move forward at a faster pace, this implicit contract will +be invalidated with PMD 7.0.0. We now introduce more fine-grained distinctions between +the type of compatibility support we guarantee for our libraries, and ways to make +them explicit to clients of PMD. + + +PMD is used and integrated by many different tools such as IDE plugins or build plugins. +Having a public stable API helps to make this +integration possible without much effort. But freezing the API will prevent further development. + +In order to balance the two needs - stable API vs. space for new development - we need to +document principles on API evolution. + +The actual API development and marking some part of the API as internal or add new API is an ongoing task, +that will need to be done everytime. We won't just define an API and then are done with it. +The API will change as new features want to be implemented. + +This decision document aims to document + +- what the criteria is for a public API + - clear-cut between public APIs and internal + - which packages to use, e.g. `internal` for internal "API" + - when to use package `impl` +- how to deprecate and remove old APIs + - how long to support a given API + - with respect to semantic version +- when to define a new API as experimental + - when to promote an experimental API as stable +- annotations to use, see also +- guidelines for AST classes (package private ctor, final, etc.) + +Clearly defining the API PMD provides, makes sense and will help, if we at some time in the future want to +modularize PMD using java9. That would prevent API leaking then at compile time already... + +# Decision + +## Java packages names and structure +* PMD is mainly developed in the Java programming language. It consists of several modules. +The classes belonging to one module should be in the same package. From the package name, it should be clear to +which module this package belongs (there is a 1:1 mapping). This rule helps to find the source code for any +fully qualified (Java) class name. +* The base package for all PMD source code is `net.sourceforge.pmd`. +* Language modules, such as `pmd-java` will use the package `net.sourceforge.pmd.lang.java`, or in general: + `net.sourceforge.pmd.lang.`. +* The core module `pmd-core` will use the base package as an exception. +* All other modules should use a subpackage of the base package, e.g. `pmd-cli` uses the package `net.sourceforge.pmd.cli`, + or in general: `net.sourceforge.pmd.` + +Sub-package `impl` + +Sub-package `internal` + + +## Criteria for public API + + + +Not public API: + + Inheritance-specific members of AST related classes & interfaces. eg adding a member to an interface shouldn’t be considered api breaking + Setters only used in the parser + Exceptions… (I’ve seen JaxenException in the Node interface…) + XPath, AttributeAxisIterator + RuleSetFactory + +public API + + AST structure + Language Symbol Table / Metrics / Type Resolution …(Not the implementation!) + Implementing custom Rules (AbstractRule, concrete Properties, …) + Renderers + Executing PMD, Ant Task, Configuration + RuleSet XML + +*Internal API* is meant for use *only* by the main PMD codebase. Internal types and methods +may be modified in any way, or even removed, at any time. + +Any API in a package that contains an `.internal` segment is considered internal. +The `@InternalApi` annotation will be used for APIs that have to live outside of +these packages, e.g. methods of a public type that shouldn't be used outside of PMD (again, +these can be removed anytime). + +## Deprecation and removing of old APIs +Use @Deprecated during the transition (from the point we announce decide which APIs will be non-public until 7.0.0). +Update the 7.0.0 branch as we go along + +## Experimental APIs + +New Features, maybe @Incubating or @Experimental at first + +## Guidelines for AST classes +maybe, concrete AST node classes should have a package-private constructor, and be final. +They're not meant to be instantiated by hand, but that could break some tests and rules + +## Summary of the annotations + +* `@InternalApi` +Full name: `net.sourceforge.pmd.annotation.InternalApi` + +* `@ReservedSubclassing` +Full name: `net.sourceforge.pmd.annotation.ReservedSubclassing` + +Types marked with the `@ReservedSubclassing` annotation are only meant to be subclassed +by classes within PMD. As such, we may add new abstract methods, or remove protected methods, +at any time. All published public members remain supported. The annotation is *not* inherited, which +means a reserved interface doesn't prevent its implementors to be subclassed. + +* `@Experimental` +Full name: `net.sourceforge.pmd.annotation.Experimental` + +APIs marked with the `@Experimental` annotation at the class or method level are subject to change. +They can be modified in any way, or even removed, at any time. You should not use or rely +on them in any production code. They are purely to allow broad testing and feedback. + +* `@Deprecated` +Full name: `java.lang.Deprecated` + +APIs marked with the `@Deprecated` annotation at the class or method level will remain supported +until the next major release, but it is recommended to stop using them. + +* `@DeprecatedUntil700` +Full name: `net.sourceforge.pmd.annotation.DeprecatedUntil700` + + + +# Status + +{{ page.adr_status }} (Last updated: {{ page.last_updated }}) + +# Consequences + +What becomes easier or more difficult to do because of this change? + +# Change History + +YYYY-MM-DD: Add xyz. + +YYYY-MM-DD: Proposed initial version. From 1d2db3dfe050ceb8dfe9077d968d827a495bec30 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 1 Dec 2023 09:41:09 +0100 Subject: [PATCH 2/6] [doc] ADR 3 - API principles --- docs/pages/pmd/projectdocs/decisions/adr-3.md | 162 +++++++++++------- 1 file changed, 96 insertions(+), 66 deletions(-) diff --git a/docs/pages/pmd/projectdocs/decisions/adr-3.md b/docs/pages/pmd/projectdocs/decisions/adr-3.md index 1ad5cda493..4ffc7e8959 100644 --- a/docs/pages/pmd/projectdocs/decisions/adr-3.md +++ b/docs/pages/pmd/projectdocs/decisions/adr-3.md @@ -17,109 +17,136 @@ The API of PMD has been growing over the years and needed some cleanup. The goal have a clear separation between a well-defined API and the implementation, which is internal. This should help us in future development. -Until now, all released public members and types were implicitly considered part -of PMD's public API, including inheritance-specific members (protected members, abstract methods). +Until PMD 7.0.0, all released public members and types were implicitly considered part +of public PMD API, including inheritance-specific members (protected members, abstract methods). We have maintained those APIs with the goal to preserve full binary compatibility between minor releases, only breaking those APIs infrequently, for major releases. +PMD is used and integrated in many different tools such as IDE plugins or build plugins. These plugins +use our public API and rely on it being stable, hence we tried to break it only infrequently. + In order to allow PMD to move forward at a faster pace, this implicit contract will -be invalidated with PMD 7.0.0. We now introduce more fine-grained distinctions between +be invalidated with PMD 7.0.0 and onwards. We now introduce more fine-grained distinctions between the type of compatibility support we guarantee for our libraries, and ways to make them explicit to clients of PMD. - -PMD is used and integrated by many different tools such as IDE plugins or build plugins. -Having a public stable API helps to make this -integration possible without much effort. But freezing the API will prevent further development. - -In order to balance the two needs - stable API vs. space for new development - we need to -document principles on API evolution. - The actual API development and marking some part of the API as internal or add new API is an ongoing task, that will need to be done everytime. We won't just define an API and then are done with it. The API will change as new features want to be implemented. -This decision document aims to document - -- what the criteria is for a public API - - clear-cut between public APIs and internal - - which packages to use, e.g. `internal` for internal "API" - - when to use package `impl` -- how to deprecate and remove old APIs - - how long to support a given API - - with respect to semantic version -- when to define a new API as experimental - - when to promote an experimental API as stable -- annotations to use, see also -- guidelines for AST classes (package private ctor, final, etc.) - -Clearly defining the API PMD provides, makes sense and will help, if we at some time in the future want to -modularize PMD using java9. That would prevent API leaking then at compile time already... +This decision document aims to document principles and guidelines that are used for PMD development. # Decision -## Java packages names and structure -* PMD is mainly developed in the Java programming language. It consists of several modules. -The classes belonging to one module should be in the same package. From the package name, it should be clear to -which module this package belongs (there is a 1:1 mapping). This rule helps to find the source code for any -fully qualified (Java) class name. -* The base package for all PMD source code is `net.sourceforge.pmd`. -* Language modules, such as `pmd-java` will use the package `net.sourceforge.pmd.lang.java`, or in general: - `net.sourceforge.pmd.lang.`. -* The core module `pmd-core` will use the base package as an exception. -* All other modules should use a subpackage of the base package, e.g. `pmd-cli` uses the package `net.sourceforge.pmd.cli`, - or in general: `net.sourceforge.pmd.` +## Semantic Versioning -Sub-package `impl` +PMD and all its modules are versioned together. PMD uses [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). +This means, that each PMD version consists of MAJOR.MINOR.PATCH components: -Sub-package `internal` +* MAJOR version is incremented for incompatible API changes +* MINOR version is incremented for added functionality in a backwards compatible way +* PATCH version is incremented for backward compatible bug fixes +Additional labels for release candidates might be used. + +Incompatible API changes shouldn't be introduced lightly. See +[FAQ: If even the tiniest backward incompatible changes to the public API require a major version bump, won’t I end up at version 42.0.0 very rapidly?](https://semver.org/spec/v2.0.0.html#if-even-the-tiniest-backward-incompatible-changes-to-the-public-api-require-a-major-version-bump-wont-i-end-up-at-version-4200-very-rapidly). + +## Project structure and Java base packages names + +PMD is mainly developed in the Java programming language. The build tool is Maven and the PMD build consists +of several maven modules. + +* Classes belonging to one module should be in the same package (or sub-packages). +* Given a package name, it should be easy to figure out to which module this package belongs. There is a 1:1 mapping + between maven module and package. This rule helps to find the source code for any fully qualified (Java) class name. +* Two modules must not define the same packages. That means, it is not allowed that any given package spans more than + one module. Otherwise, the mapping between module and package wouldn't be unambiguous. +* The base package for all PMD source code is `net.sourceforge.pmd`. There are many different sub packages. +* The core module `pmd-core` uses directly the base package as the only module. All other modules must use + specific sub packages. +* Language modules use the base package `net.sourceforge.pmd.lang.`. + E.g. `pmd-java` uses the package `net.sourceforge.pmd.lang.java`. +* All other modules use the base package `net.sourceforge.pmd.`, + E.g. `pmd-cli` uses the package `net.sourceforge.pmd.cli`. ## Criteria for public API +Public API is +* API needed to execute PMD analysis + * Renderers + * RuleSet XML Schema + * Configuration + * Ant Tasks +* API needed to implement custom rules + * AST structure and classes of languages (incl. AST structure for XPath rules) + * XPath functions + * Language Symbol Table / Metrics / Type Resolution (Not the implementation) -Not public API: +**Not** public API is - Inheritance-specific members of AST related classes & interfaces. eg adding a member to an interface shouldn’t be considered api breaking - Setters only used in the parser - Exceptions… (I’ve seen JaxenException in the Node interface…) - XPath, AttributeAxisIterator - RuleSetFactory +* Anything in packages `internal` and `impl` +* Inheritance-specific members of AST related classes and interfaces. E.g. adding a member to an + interface shouldn't be considered API breaking +* Setters in AST classes are private. They are only used in the parser -public API +## Separation between public API, internal and implementation - AST structure - Language Symbol Table / Metrics / Type Resolution …(Not the implementation!) - Implementing custom Rules (AbstractRule, concrete Properties, …) - Renderers - Executing PMD, Ant Task, Configuration - RuleSet XML +All packages are considered to be public API by default, with **two exceptions**: -*Internal API* is meant for use *only* by the main PMD codebase. Internal types and methods -may be modified in any way, or even removed, at any time. +* Any package that contains an `internal` segment is considered internal. E.g. `net.sourceforge.pmd.internal`. + *Internal API* is meant for use *only* by the main PMD codebase. Internal types and methods + may be modified in any way, or even removed, at any time without a MAJOR version change. -Any API in a package that contains an `.internal` segment is considered internal. -The `@InternalApi` annotation will be used for APIs that have to live outside of -these packages, e.g. methods of a public type that shouldn't be used outside of PMD (again, -these can be removed anytime). + The `@InternalApi` annotation will be used for types that have to live outside of + these packages, e.g. methods of a public type that shouldn't be used outside PMD (again, + these can be removed anytime). + +* Any package that contains an `impl` segment is considered internal. E.g. `net.sourceforge.pmd.lang.impl`. + These packages contain base classes that are needed for extending PMD (like adding a new language). + These can change at any time without a MAJOR version change. ## Deprecation and removing of old APIs -Use @Deprecated during the transition (from the point we announce decide which APIs will be non-public until 7.0.0). -Update the 7.0.0 branch as we go along + +* APIs can be deprecated at any time (even in PATCH versions). Deprecated APIs are marked with the + `@Deprecated` annotation. +* Deprecations should be listed in the release notes. +* Deprecated APIs can only be removed with a MAJOR version change. ## Experimental APIs -New Features, maybe @Incubating or @Experimental at first +* New features often introduce new APIs. These new APIs can be marked with the annotation `@Experimental` at + the class or method level. +* APIs marked with the `@Experimental` annotation are subject to change and are considered **not stable**. + They can be modified in any way, or even removed, at any time. You should not use or rely + on them in any production code. They are purely to allow broad testing and feedback. +* Experimental APIs can be introduced or removed with at least a MINOR version change. + These experimental APIs should be listed in the release notes. +* Experimental APIs can be promoted to Public APIs with at least a MINOR version change. ## Guidelines for AST classes -maybe, concrete AST node classes should have a package-private constructor, and be final. -They're not meant to be instantiated by hand, but that could break some tests and rules + +AST classes of the individual language modules are used by custom rule implementations and are considered +Public API in general. Rules only read the AST and do not need to modify it. + +In order to minimize the public API surface of AST classes, the following guidelines apply: + +* Concrete AST classes should be final, to avoid custom subclasses. +* Concrete AST classes should only have a package private constructor to avoid manual instantiation. + Only the parser of the language (which lives in the same package) should be able to create new instances + of AST classes. +* Concrete AST classes should not have public setters. All setters should be package private, so that + only the parser of the language can call the setters during AST construction. + +Non-concrete AST classes (like base classes or common interfaces) should follow similar guidelines: +* Only package private constructor +* Only package private setters ## Summary of the annotations -* `@InternalApi` -Full name: `net.sourceforge.pmd.annotation.InternalApi` +* `@InternalApi` (`net.sourceforge.pmd.annotation.InternalApi`) + * `@ReservedSubclassing` Full name: `net.sourceforge.pmd.annotation.ReservedSubclassing` @@ -153,6 +180,9 @@ Full name: `net.sourceforge.pmd.annotation.DeprecatedUntil700` # Consequences +* Clearly defining the API PMD provides, makes sense and will help, if we at some time in the future want to +modularize PMD using java9. That would prevent API leaking then at compile time already... + What becomes easier or more difficult to do because of this change? # Change History From 8763e1a19fc702ec6253b24beddd9efdd0099ce6 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 1 Dec 2023 11:05:01 +0100 Subject: [PATCH 3/6] [doc] ADR 3 - API principles --- docs/pages/pmd/projectdocs/decisions/adr-3.md | 56 ++++++++++--------- docs/pages/release_notes_pmd7.md | 45 +-------------- .../pmd/annotation/Experimental.java | 8 +-- .../pmd/annotation/InternalApi.java | 9 +-- .../pmd/annotation/ReservedSubclassing.java | 25 +++++---- 5 files changed, 56 insertions(+), 87 deletions(-) diff --git a/docs/pages/pmd/projectdocs/decisions/adr-3.md b/docs/pages/pmd/projectdocs/decisions/adr-3.md index 4ffc7e8959..e6cadb095e 100644 --- a/docs/pages/pmd/projectdocs/decisions/adr-3.md +++ b/docs/pages/pmd/projectdocs/decisions/adr-3.md @@ -6,7 +6,7 @@ sidebaractiveurl: /pmd_projectdocs_decisions.html adr: true # Proposed / Accepted / Deprecated / Superseded adr_status: "Proposed" -last_updated: November 2023 +last_updated: December 2023 --- @@ -147,32 +147,40 @@ Non-concrete AST classes (like base classes or common interfaces) should follow * `@InternalApi` (`net.sourceforge.pmd.annotation.InternalApi`) + This annotation is used for API members that are not publicly supported API but have to live in + public packages (outside `internal` packages). + Such members may be removed, renamed, moved, or otherwise broken at any time and should not be + relied upon outside the main PMD codebase. -* `@ReservedSubclassing` -Full name: `net.sourceforge.pmd.annotation.ReservedSubclassing` +* `@Experimental` (`net.sourceforge.pmd.annotation.Experimental`) -Types marked with the `@ReservedSubclassing` annotation are only meant to be subclassed -by classes within PMD. As such, we may add new abstract methods, or remove protected methods, -at any time. All published public members remain supported. The annotation is *not* inherited, which -means a reserved interface doesn't prevent its implementors to be subclassed. + API members marked with the `@Experimental` annotation at the class or method level are subject to change. + It is an indication that the feature is in experimental, unstable state. + The API members can be modified in any way, or even removed, at any time, without warning. + You should not use or rely on them in any production code. They are purely to allow broad testing and feedback. -* `@Experimental` -Full name: `net.sourceforge.pmd.annotation.Experimental` +* `@ReservedSubclassing` (`net.sourceforge.pmd.annotation.ReservedSubclassing`) -APIs marked with the `@Experimental` annotation at the class or method level are subject to change. -They can be modified in any way, or even removed, at any time. You should not use or rely -on them in any production code. They are purely to allow broad testing and feedback. + Types marked with the `@ReservedSubclassing` annotation are only meant to be subclassed + by classes within PMD. These types are only partially public API. Abstract or protected methods + may be added or removed at any time, which could break compatibility with existing + implementors. -* `@Deprecated` -Full name: `java.lang.Deprecated` + The API that is not inheritance-specific (all public members) is still public API, + unless the public members are marked as `@InternalApi` explicitly. -APIs marked with the `@Deprecated` annotation at the class or method level will remain supported -until the next major release, but it is recommended to stop using them. + The annotation is *not* inherited, which + means a reserved interface doesn't prevent its implementors to be subclassed. -* `@DeprecatedUntil700` -Full name: `net.sourceforge.pmd.annotation.DeprecatedUntil700` + This should be used for example for base rule classes that + are meant to be used in PMD only, or for AST-related interfaces + and abstract classes. +* `@Deprecated` (`java.lang.Deprecated`) + API members marked with the `@Deprecated` annotation at the class or method level will remain supported + until the next major release, but it is recommended to stop using them. These members might be + removed with the next MAJOR release. # Status @@ -180,13 +188,11 @@ Full name: `net.sourceforge.pmd.annotation.DeprecatedUntil700` # Consequences -* Clearly defining the API PMD provides, makes sense and will help, if we at some time in the future want to -modularize PMD using java9. That would prevent API leaking then at compile time already... - -What becomes easier or more difficult to do because of this change? +* Clearly defining the API PMD provides will help to further modularize PMD using the + Java [Module System](https://openjdk.org/jeps/261). +* Simpler decisions when to increase MAJOR, MINOR of PATCH version. +* Refactoring of the implementation is possible without affecting public API. # Change History -YYYY-MM-DD: Add xyz. - -YYYY-MM-DD: Proposed initial version. +2023-12-01: Proposed initial version. diff --git a/docs/pages/release_notes_pmd7.md b/docs/pages/release_notes_pmd7.md index 6ec4ba8b3f..44c15737c0 100644 --- a/docs/pages/release_notes_pmd7.md +++ b/docs/pages/release_notes_pmd7.md @@ -546,49 +546,10 @@ The API of PMD has been growing over the years and needed some cleanup. The goal have a clear separation between a well-defined API and the implementation, which is internal. This should help us in future development. -This however entails some incompatibilities and -deprecations, see also the sections [New API support guidelines](#new-api-support-guidelines) and -[API removals](#api-removals) below. +This however entails some incompatibilities and deprecations. -### New API support guidelines - -Until now, all released public members and types were implicitly considered part -of PMD's public API, including inheritance-specific members (protected members, abstract methods). -We have maintained those APIs with the goal to preserve full binary compatibility between minor releases, -only breaking those APIs infrequently, for major releases. - -In order to allow PMD to move forward at a faster pace, this implicit contract will -be invalidated with PMD 7.0.0. We now introduce more fine-grained distinctions between -the type of compatibility support we guarantee for our libraries, and ways to make -them explicit to clients of PMD. - -**`.internal` packages and `@InternalApi` annotation** - -*Internal API* is meant for use *only* by the main PMD codebase. Internal types and methods -may be modified in any way, or even removed, at any time. - -Any API in a package that contains an `.internal` segment is considered internal. -The `@InternalApi` annotation will be used for APIs that have to live outside of -these packages, e.g. methods of a public type that shouldn't be used outside of PMD (again, -these can be removed anytime). - -**`@ReservedSubclassing`** - -Types marked with the `@ReservedSubclassing` annotation are only meant to be subclassed -by classes within PMD. As such, we may add new abstract methods, or remove protected methods, -at any time. All published public members remain supported. The annotation is *not* inherited, which -means a reserved interface doesn't prevent its implementors to be subclassed. - -**`@Experimental`** - -APIs marked with the `@Experimental` annotation at the class or method level are subject to change. -They can be modified in any way, or even removed, at any time. You should not use or rely -on them in any production code. They are purely to allow broad testing and feedback. - -**`@Deprecated`** - -APIs marked with the `@Deprecated` annotation at the class or method level will remain supported -until the next major release, but it is recommended to stop using them. +See [ADR 3 - API evolution principles](pmd_projectdocs_decisions_adr_3.html) and +[API changes](#api-changes) below. ### Small Changes and cleanups diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/annotation/Experimental.java b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/Experimental.java index 4b61214562..97cb2b6460 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/annotation/Experimental.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/Experimental.java @@ -8,10 +8,10 @@ import java.lang.annotation.Documented; /** - * Indicates the feature is in experimental state: its existence, signature - * or behavior might change without warning from one release to the next. - * The only clients that are "safe" using experimental APIs are the sources - * of PMD itself. + * Tagged API members are subject to change. + * It is an indication that the feature is in experimental, unstable state. + * The API members can be modified in any way, or even removed, at any time, without warning. + * You should not use or rely on them in any production code. They are purely to allow broad testing and feedback. * * @since 6.7.0 */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/annotation/InternalApi.java b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/InternalApi.java index fbcef90350..d66749a932 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/annotation/InternalApi.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/InternalApi.java @@ -8,10 +8,11 @@ import java.lang.annotation.Documented; /** - * Tags API members that are not publicly supported API. - * Such members may be removed, renamed, moved, or otherwise - * broken at any time and should not be relied upon outside - * of the main PMD codebase. + * Tags API members that are not publicly supported API but have to live in + * public packages (outside `internal` packages). + * + *

Such members may be removed, renamed, moved, or otherwise broken at any time and should not be + * relied upon outside the main PMD codebase. * *

Members and types tagged with this annotation will remain * supported until 7.0.0, after which some will be moved to internal diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/annotation/ReservedSubclassing.java b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/ReservedSubclassing.java index 47f10de80c..86976eb1fa 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/annotation/ReservedSubclassing.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/ReservedSubclassing.java @@ -10,25 +10,26 @@ import java.lang.annotation.Target; /** - * Indicates that subclassing this type is not publicly - * supported API. Abstract methods may be added or removed - * at any time, which could break binary compatibility with - * existing implementors. Protected methods are also part of - * the private API of this type. + * Indicates that annotated types are only meant to be subclassed + * by classes within PMD. These types are only partially public API. Abstract or protected methods + * may be added or removed at any time, which could break compatibility with existing + * implementors. * - *

The API that is not inheritance-specific (unless {@linkplain InternalApi noted otherwise}, - * all public members), is still public API and will remain binary- - * compatible between major releases. + *

The API that is not inheritance-specific (all public members) is still public API, + * unless the public members are marked as {@link InternalApi} explicitly. + * + *

The annotation is not inherited, which + * means a reserved interface doesn't prevent its implementors to be subclassed. + * + *

This should be used for example for base rule classes that + * are meant to be used in PMD only, or for AST-related interfaces + * and abstract classes. * *

Types tagged with this annotation will remain supported * until 7.0.0, at which point no guarantees will be maintained * about the stability of the inheritance hierarchy for external * clients. * - *

This should be used for example for base rule classes that - * are meant to be used in PMD only, or for AST-related interfaces - * and abstract classes. - * * @since 6.7.0 */ @Target(ElementType.TYPE) From 32327876fbce4e089b4fde26e70f929a452bedba Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 1 Dec 2023 11:07:28 +0100 Subject: [PATCH 4/6] [doc] ADR - formatting of headers --- docs/pages/pmd/projectdocs/decisions/adr-1.md | 10 +++---- docs/pages/pmd/projectdocs/decisions/adr-2.md | 10 +++---- docs/pages/pmd/projectdocs/decisions/adr-3.md | 26 +++++++++---------- .../pmd/projectdocs/decisions/adr-NNN.md | 10 +++---- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/pages/pmd/projectdocs/decisions/adr-1.md b/docs/pages/pmd/projectdocs/decisions/adr-1.md index 85d750b473..94b624e42c 100644 --- a/docs/pages/pmd/projectdocs/decisions/adr-1.md +++ b/docs/pages/pmd/projectdocs/decisions/adr-1.md @@ -9,7 +9,7 @@ adr_status: "Accepted" last_updated: September 2022 --- -# Context +## Context PMD has grown over 20 years as an open-source project. Along the way many decisions have been made, but they are not explicitly documented. PMD is also developed by many individuals and the original developers might @@ -33,7 +33,7 @@ by Michael Nygard. There are many templates around to choose from. gives a nice summary. The page gives a good overview on ADR and for adr-related tooling. -# Decision +## Decision We will document the decisions we make as a project as a collection of "Architecture Decision Records". In order to keep it simple, we will use only a simple template proposed by Michael Nygard. @@ -49,11 +49,11 @@ The change can be to amend the ADR or to challenge it and maybe deprecate it. A "Change History" section should be added to summary the change. When maintainer consensus is reached during the PR review, then the PR can be merged and the ADR is updated. -# Status +## Status {{ page.adr_status }} (Last updated: {{ page.last_updated }}) -# Consequences +## Consequences Explicitly documenting decisions has the benefit that new developers joining the projects know about the decisions and can read the context and consequences of the decisions. This will likely also improve the overall quality @@ -61,7 +61,7 @@ as the decisions need to be formulated and written down. Everybody is on the sam However, this also adds additional tasks, and it takes time to write down and document the decisions. -# Change History +## Change History 2022-09-30: Status changed to "Accepted". diff --git a/docs/pages/pmd/projectdocs/decisions/adr-2.md b/docs/pages/pmd/projectdocs/decisions/adr-2.md index b3f57efd4f..2af164fde2 100644 --- a/docs/pages/pmd/projectdocs/decisions/adr-2.md +++ b/docs/pages/pmd/projectdocs/decisions/adr-2.md @@ -9,7 +9,7 @@ adr_status: "Accepted" last_updated: September 2022 --- -# Context +## Context We currently use Kotlin only for unit tests at some places (e.g. pmd-lang-test module provides a couple of base test classes). We were cautious to expand Kotlin because of poor development support outside JetBrain's @@ -26,7 +26,7 @@ However - PMD is a tool that deals with many, many languages anyway, so this is Nevertheless, extending the usage of Kotlin within PMD can also increase contributions. -# Decision +## Decision We are generally open to the idea to increase usage of Kotlin within PMD. In order to gain experience and to keep it within bounds and therefore maintainable we came up with the following rules: @@ -50,11 +50,11 @@ and to keep it within bounds and therefore maintainable we came up with the foll not make incompatible changes. If compatibility (binary or source) can't be maintained, then that would be a major version change. -# Status +## Status {{ page.adr_status }} (Last updated: {{ page.last_updated }}) -# Consequences +## Consequences Allowing more Kotlin in PMD can attract new contributions. It might make it easier to develop small DSLs. In the future we might also consider to use other languages than Kotlin, e.g. for `pmd-scala` Scala might make sense. @@ -64,7 +64,7 @@ when Kotlin is used. Eclipse can't be used practically anymore. Maintaining a polyglot code base with multiple languages is likely to be more challenging. -# Change History +## Change History 2022-09-30: Changed status to "Accepted". diff --git a/docs/pages/pmd/projectdocs/decisions/adr-3.md b/docs/pages/pmd/projectdocs/decisions/adr-3.md index e6cadb095e..33ed5c6a35 100644 --- a/docs/pages/pmd/projectdocs/decisions/adr-3.md +++ b/docs/pages/pmd/projectdocs/decisions/adr-3.md @@ -11,7 +11,7 @@ last_updated: December 2023 -# Context +## Context The API of PMD has been growing over the years and needed some cleanup. The goal is, to have a clear separation between a well-defined API and the implementation, which is internal. @@ -36,9 +36,9 @@ The API will change as new features want to be implemented. This decision document aims to document principles and guidelines that are used for PMD development. -# Decision +## Decision -## Semantic Versioning +### Semantic Versioning PMD and all its modules are versioned together. PMD uses [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). This means, that each PMD version consists of MAJOR.MINOR.PATCH components: @@ -52,7 +52,7 @@ Additional labels for release candidates might be used. Incompatible API changes shouldn't be introduced lightly. See [FAQ: If even the tiniest backward incompatible changes to the public API require a major version bump, won’t I end up at version 42.0.0 very rapidly?](https://semver.org/spec/v2.0.0.html#if-even-the-tiniest-backward-incompatible-changes-to-the-public-api-require-a-major-version-bump-wont-i-end-up-at-version-4200-very-rapidly). -## Project structure and Java base packages names +### Project structure and Java base packages names PMD is mainly developed in the Java programming language. The build tool is Maven and the PMD build consists of several maven modules. @@ -70,7 +70,7 @@ of several maven modules. * All other modules use the base package `net.sourceforge.pmd.`, E.g. `pmd-cli` uses the package `net.sourceforge.pmd.cli`. -## Criteria for public API +### Criteria for public API Public API is @@ -91,7 +91,7 @@ Public API is interface shouldn't be considered API breaking * Setters in AST classes are private. They are only used in the parser -## Separation between public API, internal and implementation +### Separation between public API, internal and implementation All packages are considered to be public API by default, with **two exceptions**: @@ -107,14 +107,14 @@ All packages are considered to be public API by default, with **two exceptions** These packages contain base classes that are needed for extending PMD (like adding a new language). These can change at any time without a MAJOR version change. -## Deprecation and removing of old APIs +### Deprecation and removing of old APIs * APIs can be deprecated at any time (even in PATCH versions). Deprecated APIs are marked with the `@Deprecated` annotation. * Deprecations should be listed in the release notes. * Deprecated APIs can only be removed with a MAJOR version change. -## Experimental APIs +### Experimental APIs * New features often introduce new APIs. These new APIs can be marked with the annotation `@Experimental` at the class or method level. @@ -125,7 +125,7 @@ All packages are considered to be public API by default, with **two exceptions** These experimental APIs should be listed in the release notes. * Experimental APIs can be promoted to Public APIs with at least a MINOR version change. -## Guidelines for AST classes +### Guidelines for AST classes AST classes of the individual language modules are used by custom rule implementations and are considered Public API in general. Rules only read the AST and do not need to modify it. @@ -143,7 +143,7 @@ Non-concrete AST classes (like base classes or common interfaces) should follow * Only package private constructor * Only package private setters -## Summary of the annotations +### Summary of the annotations * `@InternalApi` (`net.sourceforge.pmd.annotation.InternalApi`) @@ -182,17 +182,17 @@ Non-concrete AST classes (like base classes or common interfaces) should follow until the next major release, but it is recommended to stop using them. These members might be removed with the next MAJOR release. -# Status +## Status {{ page.adr_status }} (Last updated: {{ page.last_updated }}) -# Consequences +## Consequences * Clearly defining the API PMD provides will help to further modularize PMD using the Java [Module System](https://openjdk.org/jeps/261). * Simpler decisions when to increase MAJOR, MINOR of PATCH version. * Refactoring of the implementation is possible without affecting public API. -# Change History +## Change History 2023-12-01: Proposed initial version. diff --git a/docs/pages/pmd/projectdocs/decisions/adr-NNN.md b/docs/pages/pmd/projectdocs/decisions/adr-NNN.md index b18a9b2866..af7934ac12 100644 --- a/docs/pages/pmd/projectdocs/decisions/adr-NNN.md +++ b/docs/pages/pmd/projectdocs/decisions/adr-NNN.md @@ -11,23 +11,23 @@ last_updated: July 2022 -# Context +## Context What is the issue that we're seeing that is motivating this decision or change? -# Decision +## Decision What is the change that we're proposing and/or doing? -# Status +## Status {{ page.adr_status }} (Last updated: {{ page.last_updated }}) -# Consequences +## Consequences What becomes easier or more difficult to do because of this change? -# Change History +## Change History YYYY-MM-DD: Add xyz. From 4666d22e0970a928edcb6dc1034a488bef612836 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 11 Dec 2023 08:36:24 +0100 Subject: [PATCH 5/6] [doc] ADR 3 - API principles - updates * clarify impl * remove @ReservedSubclassing --- docs/pages/pmd/projectdocs/decisions/adr-3.md | 27 ++++--------- .../pmd/annotation/ReservedSubclassing.java | 38 ------------------- 2 files changed, 8 insertions(+), 57 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/annotation/ReservedSubclassing.java diff --git a/docs/pages/pmd/projectdocs/decisions/adr-3.md b/docs/pages/pmd/projectdocs/decisions/adr-3.md index 33ed5c6a35..99edafd621 100644 --- a/docs/pages/pmd/projectdocs/decisions/adr-3.md +++ b/docs/pages/pmd/projectdocs/decisions/adr-3.md @@ -57,7 +57,7 @@ Incompatible API changes shouldn't be introduced lightly. See PMD is mainly developed in the Java programming language. The build tool is Maven and the PMD build consists of several maven modules. -* Classes belonging to one module should be in the same package (or sub-packages). +* All packages belonging to a given module should have a common package prefix. * Given a package name, it should be easy to figure out to which module this package belongs. There is a 1:1 mapping between maven module and package. This rule helps to find the source code for any fully qualified (Java) class name. * Two modules must not define the same packages. That means, it is not allowed that any given package spans more than @@ -89,7 +89,7 @@ Public API is * Anything in packages `internal` and `impl` * Inheritance-specific members of AST related classes and interfaces. E.g. adding a member to an interface shouldn't be considered API breaking -* Setters in AST classes are private. They are only used in the parser +* Setters in AST classes are private. They are only used in the parser. ### Separation between public API, internal and implementation @@ -107,6 +107,12 @@ All packages are considered to be public API by default, with **two exceptions** These packages contain base classes that are needed for extending PMD (like adding a new language). These can change at any time without a MAJOR version change. + In a later version, the `impl` packages could be promoted as a public API for implementing new + languages for PMD outside the main monorepo. In that sense, e.g. the module `pmd-java` is allowed + to depend on `impl` packages of `pmd-core`, but ideally it doesn't depend on `internal` packages of + `pmd-core` (or any other module). However, for now, the `impl` packages are **explicitly considered + internal** until this decision is revised. + ### Deprecation and removing of old APIs * APIs can be deprecated at any time (even in PATCH versions). Deprecated APIs are marked with the @@ -159,23 +165,6 @@ Non-concrete AST classes (like base classes or common interfaces) should follow The API members can be modified in any way, or even removed, at any time, without warning. You should not use or rely on them in any production code. They are purely to allow broad testing and feedback. -* `@ReservedSubclassing` (`net.sourceforge.pmd.annotation.ReservedSubclassing`) - - Types marked with the `@ReservedSubclassing` annotation are only meant to be subclassed - by classes within PMD. These types are only partially public API. Abstract or protected methods - may be added or removed at any time, which could break compatibility with existing - implementors. - - The API that is not inheritance-specific (all public members) is still public API, - unless the public members are marked as `@InternalApi` explicitly. - - The annotation is *not* inherited, which - means a reserved interface doesn't prevent its implementors to be subclassed. - - This should be used for example for base rule classes that - are meant to be used in PMD only, or for AST-related interfaces - and abstract classes. - * `@Deprecated` (`java.lang.Deprecated`) API members marked with the `@Deprecated` annotation at the class or method level will remain supported diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/annotation/ReservedSubclassing.java b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/ReservedSubclassing.java deleted file mode 100644 index 86976eb1fa..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/annotation/ReservedSubclassing.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; - - -/** - * Indicates that annotated types are only meant to be subclassed - * by classes within PMD. These types are only partially public API. Abstract or protected methods - * may be added or removed at any time, which could break compatibility with existing - * implementors. - * - *

The API that is not inheritance-specific (all public members) is still public API, - * unless the public members are marked as {@link InternalApi} explicitly. - * - *

The annotation is not inherited, which - * means a reserved interface doesn't prevent its implementors to be subclassed. - * - *

This should be used for example for base rule classes that - * are meant to be used in PMD only, or for AST-related interfaces - * and abstract classes. - * - *

Types tagged with this annotation will remain supported - * until 7.0.0, at which point no guarantees will be maintained - * about the stability of the inheritance hierarchy for external - * clients. - * - * @since 6.7.0 - */ -@Target(ElementType.TYPE) -@Documented -public @interface ReservedSubclassing { -} From a8426fd996f5926c101dc5d056631f9562c03fcf Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 12 Jan 2024 09:16:45 +0100 Subject: [PATCH 6/6] [doc] Update release notes (#995) --- docs/pages/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 41e89d2ab9..3246537a19 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -91,6 +91,7 @@ in the Migration Guide. * [#1027](https://github.com/pmd/pmd/issues/1027): \[core] Apply the new PropertyDescriptor<Pattern> type where applicable * [#4750](https://github.com/pmd/pmd/pull/4750): \[core] Fix flaky SummaryHTMLRenderer * doc + * [#995](https://github.com/pmd/pmd/issues/995): \[doc] Document API evolution principles as ADR * [#3175](https://github.com/pmd/pmd/issues/3175): \[doc] Document language module features * [#4659](https://github.com/pmd/pmd/pull/4659): \[doc] Improve ant documentation * [#4669](https://github.com/pmd/pmd/pull/4669): \[doc] Add bld PMD Extension to Tools / Integrations @@ -537,6 +538,7 @@ See also [Detailed Release Notes for PMD 7]({{ baseurl }}pmd_release_notes_pmd7. * [#4594](https://github.com/pmd/pmd/pull/4594): \[cli] Change completion generation to runtime * [#4723](https://github.com/pmd/pmd/issues/4723): \[cli] Launch fails for "bash pmd" * doc + * [#995](https://github.com/pmd/pmd/issues/995): \[doc] Document API evolution principles as ADR * [#2501](https://github.com/pmd/pmd/issues/2501): \[doc] Verify ANTLR Documentation * [#3175](https://github.com/pmd/pmd/issues/3175): \[doc] Document language module features * [#4294](https://github.com/pmd/pmd/issues/4294): \[doc] Migration Guide for upgrading PMD 6 ➡️ 7