Victoria Skalrud leads the team responsible for developing and maintaining the Atlassian Support Troubleshooting tools app at Atlassian.
She’ll share the development patterns that her team has used to support compatibility across product versions whilst maintaining a high release velocity.
8. Why developing to support multiple versions of
a product is a good idea
9. Why developing to support multiple versions of
a product is a good idea
10. Platform version (aka Major
release)
Contain features and API breaking changes.
Feature version(aka Minor
release)
Contain features but no API breaking changes
Bugfix version
Primarily intended to fix problems without
altering behaviour
Versioning @
Atlassian
Server
11. Platform version (aka Major
release)
Contain features and API breaking changes.
Feature version(aka Minor
release)
Contain features but no API breaking changes
Bugfix version
Primarily intended to fix problems without
altering behaviour
Versioning @
Atlassian
Server
12. Platform version (aka Major
release)
Contain features and API breaking changes.
Feature version(aka Minor
release)
Contains features but no API breaking changes
Bugfix version
Primarily intended to fix problems without
altering behaviour
Versioning @
Atlassian
Server
13. Platform version (aka Major
release)
Contain features and API breaking changes.
Feature version(aka Minor
release)
Contain features but no API breaking changes
Bugfix version
Primarily intended to fix problems without
altering behaviour.
Versioning @
Atlassian
Server
14.
15. CHALLENGE #1
I WANT TO INTRODUCE A
FEATURE THAT WILL ONLY
SHOW FOR LATER HOST
PRODUCT VERSIONS
16. interface Feature {
boolean shouldDisplay();
}
Limiting
features to
version
Feature Flags
Web resource
conditions
17. interface Feature {
boolean shouldDisplay();
}
public class MyNewFeature implements Feature
{
private static int MIN_SUPPORTED_VERSION = 5;
boolean shouldDisplay() {
return getCurrentVersionOfHostProduct() >= MIN_SUPPORTED_VERSION &&
isEnabled("myNewFeature");
}
}
// In another class somewhere
// Gets current version of the host product
// - does not rely on version plugin was compiled with
getCurrentVersionOfHostProduct()
Limiting
features to
version
Feature Flags
Web resource
conditions
18. // In Confluence, in atlassian-plugin.xml
<condition
class="com.atlassian.confluence.plugin.descriptor.web.conditions.BuildNumberCondition">
<param name="minBuildNumber">8200</param>
</condition>
Limiting
features to
version
Feature Flags
Web resource
conditions
19. // In Confluence, in atlassian-plugin.xml
<condition
class="com.atlassian.confluence.plugin.descriptor.web.conditions.BuildNumberCondition">
<param name="minBuildNumber">8200</param>
</condition>
// In Bitbucket, in atlassian-plugin.xml
<condition
class=“com.atlassian.bitbucket.web.conditions.IsApplicationVersionCondition">
<param name=“operator">gte</param>
<param name=“version”>6.4</param>
</condition>
Limiting
features to
version
Web resource
conditions
Feature Flags
22. public interface JiraApi {
// doing useful stuff
// @since 6.4
String someMethod();
}
Version
Resilience
Jira API
Using the API
Ch Ch Change
Trouble in paradise
Try
23. public interface JiraApi {
// doing useful stuff
// @since 6.4
String someMethod();
}
public void myFunction(){
jiraApi.someMethod();
}
Version
Resilience
Jira API
Using the API
Ch Ch Change
Trouble in paradise
Try
24. // Newer version of Jira
public interface JiraApi {
@deprecated
String someMethod();
String shinyNewUsefulMethod();
}
Version
Resilience
Jira API
Using the API
Ch Ch Change
Trouble in paradise
Try
25. // Newer version of Jira
public interface JiraApi {
@deprecated
String someMethod();
String shinyNewUsefulMethod();
}
Version
Resilience
Jira API
Using the API
Ch Ch Change
Trouble in paradise
Try
26. // Even newer version of Jira
public interface JiraApi {
String someMethod();
String shinyNewUsefulMethod();
}
Version
Resilience
Jira API
Using the API
Ch Ch Change
Trouble in paradise
Try
27. // Even newer version of Jira
public interface JiraApi {
String someMethod();
String shinyNewUsefulMethod();
}
// Will only work in newer versions
public void myFunction(){
jiraApi.shinyNewUsefulMethod();
}
Version
Resilience
Jira API
Using the API
Ch Ch Change
Trouble in paradise
Try
28. // Even newer version of Jira
public interface JiraApi {
String someMethod();
String shinyNewUsefulMethod();
}
// Do not fear failure but rather fear not trying
public void myFunction() {
try {
jiraApi.shinyNewUsefulMethod();
} catch (final NoSuchMethodError nsme) {
// fallback
}
}
Version
Resilience
Jira API
Using the API
Ch Ch Change
Trouble in paradise
Try
30. 1 Create a clear separation of concerns
Building bridges
Your app that does
cool things and
accesses API
31. 1 Create a clear separation of concerns
Building bridges
Your business
logic
COM.MYSTUFF
32. 1 Create a clear separation of concerns
Building bridges
Your business
logic
COM.MYSTUFF
Things you need
that you get via API
COM.MYSTUFF.ACCESS
33. 1 Create a clear separation of concerns
Building bridges
2 Define your bridge blueprint
34. 1 Create a clear separation of concerns
Building bridges
2 Define your bridge blueprint
public interface SearchServiceBridge {
Collection<Issue> search(Query query) throws PlatformException;
}
35. 1 Create a clear separation of concerns
Building bridges
2 Define your bridge blueprint
3 Build out your bridges
36. 1 Create a clear separation of concerns
Building bridges
2 Define your bridge blueprint
3 Build out your bridges
37. 1 Create a clear separation of concerns
Building bridges
2 Define your bridge blueprint
3 Build out your bridges
38. 1 Create a clear separation of concerns
Building bridges
@SupportedVersions(minimumInclusive = "7.4.0")
final class SearchServiceBridge74 implements SearchServiceBridge {
public Collection<Issue> search(Query query){
// Access the Jira APIs here - 7.4 specific code
}
}
@SupportedVersions(minimumInclusive = "7.12.0")
final class SearchServiceBridge712 implements SearchServiceBridge {
public Collection<Issue> search(Query query){
// Access the Jira APIs here - 7.12 specific code
}
}
2 Define your bridge blueprint
3 Build out your bridges
39. Building bridges
1 Create a clear separation of concerns
2 Define your bridge blueprint
3 Build out your bridges
4 Build a way to select which bridge should be used
41. Building
bridges
final class BridgeManager<Bridge> {
public Optional<Bridge> getSupportedBridge(Version version) {
return bridgeBeans.stream()
.filter(bridgeBean ->
isSupported(bridgeBean, version)).findFirst();
}
}
Versioning
Manager
Bridges
Using the bridge
42. Building
bridges
final class BridgeManager<Bridge> {
public Optional<Bridge> getSupportedBridge(Version version) {
return bridgeBeans.stream()
.filter(bridgeBean ->
isSupported(bridgeBean, version)).findFirst();
}
private boolean isSupported(Bridge bridgeBean, Version version) {
return Stream.of(bridgeBean.getClass()
.getAnnotation(SUPPORTED_VERSIONS_ANNOTATION))
.anyMatch(annotation ->
isSupported(annotation, version));
}
private boolean isSupported(SupportedVersions supportedVersions, Version version)
final Version minimumInclusive = parse(supportedVersions.minimumInclusive());
final Version maximumExclusive = parse(supportedVersions.maximumExclusive());
return minimumInclusive.isLessThanOrEqualTo(version) &&
maximumExclusive.isGreaterThan(version);
}
}
Versioning
Manager
Bridges
Using the bridge
55. Introducing a feature
1 Introduce required API in the ATST api module
2 If implementation is shared, add it to ATST common module
56. Introducing a feature
1 Introduce required API in the ATST api module
2 If implementation is shared, add it to ATST common module
3 If implementation is product specific, add it to the corresponding
product module
57. Introducing a feature
1 Introduce required API in the ATST api module
2 If implementation is shared, add it to ATST common module
3 If implementation is product specific, add it to the corresponding
product module
4 If a feature is not required for a particular product, include a
NoOp implementation
58. Tips for cross product development
1 Use of SAL (Shared Access Layer) APIs to simplify development
59. Tips for cross product development
1
<dependency>
<groupId>com.atlassian.sal</groupId>
<artifactId>sal-api</artifactId>
<version>2.0.17</version>
<!-- Uses the application's SAL instead
of bundling it into the plugin. -->
<scope>provided</scope>
</dependency>
Use of SAL (Shared Access Layer) APIs to simplify development
60. Tips for cross product development
1 Use of SAL (Shared Access Layer) APIs to simplify development
2 Use provided scope to use the host product’s libraries instead
of bundling own
62. Developing for Data Centre
1 Keep the concepts of ‘licenced for data-center’,
‘clustering available’ and ‘clustering active’ as
separate and do not mix
63. Developing for Data Centre
Keep the concepts of ‘licenced for data-center’,
‘clustering available’ and ‘clustering active’ as
separate and do not mix
2 Every time you introduce a new feature, consider if this feature
needs to operate per cluster or per node
1
64. Developing for Data Centre
1 Keep the concepts of ‘licenced for data-center’,
‘clustering available’ and ‘clustering active’ as
separate and do not mix
2 Every time you introduce a new feature, consider if this feature
needs to operate per cluster or per node
3 Take a look at the Atlassian documentation for developing data
centre applications