Magnolia provides both Java APIs, REST APIs and Definitions for Dialogs, Components etc. This page summarizes the ideas, thoughts and best practices around the Magnolia APIs. The intention is to let developers know about the APIs that are safe to use, and to indicate the internal codebase that are subject to change without notice.
This document is applicable to Magnolia 5.6 and later.
Disclaimer
With 5.6 we are actually still in the exploration phase and nothing is either set in stone nor considered the definitive approach for the future. Please consider that when you give the very welcome feedback.
General intention
We want to declare our opinion about what code should be used and extended as explicit as possible. Ideally in a way that leaves no space for mistakes through neglect and a very easy way to validate for compliance. Having said that, there is no intention to either hide internal code nor make it impossible to use. If you have a very unique use case and are aware of the implications when using our internal code you should be able to do so.
The reason why we want to have that boundary between internal code and users is, to keep our internal code flexible so we can innovate quickly without impact on current users. Ideally internal code only consists of code no customers is using anyway, but with making it explicitly internal code we have two huge benefits: We can change it as quickly and heavily as we want without being afraid of potentially breaking a promise to our customers AND we don't spend a lot of effort keeping that code backwards compatible for nobody. Depending on how near that ideal is to reality the transition phase will be shorter or longer.
Best practices
Usually we should consider the following list as potential part of the public API:
- Definitions (by definition all of them are public, maybe not all of them are directly accessible but thought the {{*2Bean}} mapping they are directly exposed)
- Actions
- Models
- Everything exposed through a RESTful service
The following list contains candidates to be usually considered private:
- Data bindings
- Pluming code in general
- Code of custom Vaadin components
The API Artifacts/Sub-Modules
If possible the API classes and interfaces of a Magnolia Module should be separated into a submodule (e.g. magnolia-dam-api
). That module should have no dependencies on other internal submodules but be dependent on by all others. API artifacts should always be postfixed as -api
.
Example needed
Annotations
There is a distinction between different types of API indicated by the presence of different annotations. For the moment we are only using {{@PublicApi}} but others are conceivable like for SPI or experimental APIs.
@PublicApi
API includes interfaces and classes that Magnolia modules need to use to get things done. Modules can safely use there and we will guarantee binary compatibility. This is true as well if you extend or implement these classes if possible.
@PublicSpi
@Experimental
Example code for Annotations needed
Compatibility Policy
Deprecation Policy
Methods or types that are deprecated will have the @deprecated
annotation and according Javadoc comment.
The Javadoc will explain how to replace the usage and list the module version in which it was set to deprecated.
APIs marked as deprecated will continue to work in the following minor releases, but will be potentially removed in the next major release.
Further Information
- see the following concept Concept - Open Closed Code Space which outlines several steps beyond public APIs
- As an example have a look at the API documentation from Atlassian (https://developer.atlassian.com/jiradev/jira-apis/java-api-policy-for-jira)
- Presentation about Public API
Open Points / TBD
- Concrete examples for module structure
- Concrete examples for annotations
- Define policies with clear explanation how releases (both magnolia & module) related to deprecation and removal of APIs (see https://developer.atlassian.com/jiradev/jira-apis/java-api-policy-for-jira as an example)
- Explain good practices to extract public api on existing codebase
- Describe common pitfalls and how to avoid them
- Outline process to validate API consistency during development
- Outline process to assess public API compliancy in customer projects
3 Comments
Christopher Zimmermann
I think our developer community will really appreciate this. I've been in multiple conversations at conferences where developers expressed dissatisfaction with how unstable our API is. - sometimes going as far as to say we have no API.
Christopher Zimmermann
Hey, just some thoughts. Possibly Im totally missing some aspects, but I think this is worth considering:
In thinking about how an external developer will experience our API, I see advantages to having a @Private annotation. If a developer does not look too closely at the Magnolia code - ie not looking at the entire package/class, they might simply use any method that they find - without noticing that it is not part of our @Public API. If we explicitly annotated things as Private, then a developer would see the hint, even if they dont do a thorough research of the package. And they would see it even if they had not read up on the PublicAPI concept in our documentation. (likely some will miss that.)
Also, considering that this is something that is rolling out module by module, some modules will have no annotations, and some will have the Public annotation. Due to this, a developer considering an un-annotated method actually won’t know whether it is Private or Public. They would need to know first whether that module had been annotated.
In light of the fact that the main behaviour of external developers that we are trying to change is to stop them from using some methods, its probably best to make the status of those off-limits methods as clear and noticible as possible.
Having both a @Private and an @Public annotation would of course be redundant in a way, but it would make it abundantly clear to a developer that the module was in the list of “API-refreshed” modules. Another benefit is that it would remove the risk/doubt of whether each method had been considered. Currently it would be possible that we simply forgot to consider a method when choosing whether it should be public or private - whereas if all marked public or private, then if you find an unannotated method, you know it was forgotten. (Which could also help catch things in our reviews.)
If dev team still thinks its over-redundant to mark both private and public, then for the above reasons I think its more likely to achieve its goal if we were to only mark @Private instead of @Public.
(maybe @Private is a bad name since it's already taken, could be anything... @PrivateAPI or whatever.)
Michael Mühlebach
I completely agree that it should be as obvious as possible for any developer. In my opinion it should even be possible to configure the build in a way that using private APIs will fail the build. Ideally private classes should not even show up in the IDE by default.
This would be possible when we go with the idea of api submodules like we already have in some cases.
tbh I don't really like the idea of marking everything as private because of the overhead and that there might still be some ambiguity. I think/hope that we can achieve the same thing with simpler tools.