SlideShare a Scribd company logo
VICTORIA SKALRUD | SENIOR DEVELOPMENT TL | ATLASSIAN
Practical Patterns for Developing a Cross-
product Cross-version App
Lessons from working on the
Atlassian Troubleshooting &Support Tools
(ATST)
Developing
cross
version
Developing
cross
product
Developing
DC features
Q & A
Agenda
Developing
cross
version
Developing
cross
product
Developing
DC features
Q & A
Agenda
Developing
cross
version
Developing
cross
product
Developing
DC features
Q & A
Agenda
Developing
cross
version
Developing
cross
product
Developing
DC features
Q & A
Agenda
Developing cross version
Why developing to support multiple versions of a product is a good idea
Why developing to support multiple versions of
a product is a good idea
Why developing to support multiple versions of
a product is a good idea
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
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
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
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
Practical Patterns for Developing a Cross-product Cross-version App
CHALLENGE #1
I WANT TO INTRODUCE A
FEATURE THAT WILL ONLY
SHOW FOR LATER HOST
PRODUCT VERSIONS
interface Feature {
boolean shouldDisplay();
}
Limiting
features to
version
Feature Flags
Web resource
conditions
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
// 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
// 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
Practical Patterns for Developing a Cross-product Cross-version App
CHALLENGE #2
I WANT TO WRITE MY
FEATURE TO BE RESILIENT
ACROSS API CHANGES
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
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
// 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
// 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
// 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
// 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
// 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
You can build a
bridge!
BETTER WAY
1 Create a clear separation of concerns
Building bridges
Your app that does
cool things and
accesses API
1 Create a clear separation of concerns
Building bridges
Your business
logic
COM.MYSTUFF
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
1 Create a clear separation of concerns
Building bridges
2 Define your bridge blueprint
1 Create a clear separation of concerns
Building bridges
2 Define your bridge blueprint
public interface SearchServiceBridge {
Collection<Issue> search(Query query) throws PlatformException;
}
1 Create a clear separation of concerns
Building bridges
2 Define your bridge blueprint
3 Build out your bridges
1 Create a clear separation of concerns
Building bridges
2 Define your bridge blueprint
3 Build out your bridges
1 Create a clear separation of concerns
Building bridges
2 Define your bridge blueprint
3 Build out your bridges
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
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
Building
bridges
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportedVersions {
String minimumInclusive() default “0.0.0";
String maximumExclusive() default "99999.99999.99999";
}
Versioning
Manager
Bridges
Using the bridge
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
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
Building
bridges
public interface SearchServiceBridge {
Collection<Issue> search(Query query) throws PlatformException;
}
Versions
Manager
Bridges
Using the bridge
Building
bridges
public interface SearchServiceBridge {
Collection<Issue> search(Query query) throws PlatformException;
}
@ExportAsService({SearchServiceBridge.class})
@Named("com.atlassian.bridges.search.SearchServiceBridge74")
@SupportedVersions(minimumInclusive = "7.4.0", maximumExclusive = “7.12.0")
final class SearchServiceBridge74 implements SearchServiceBridge {
@Override
public Collection<Issue> search(Query query)
throws PlatformException {
…
}
}
Versions
Manager
Bridges
Using the bridge
Building
bridges
public interface SearchServiceBridge {
Collection<Issue> search(Query query) throws PlatformException;
}
@ExportAsService({SearchServiceBridge.class})
@Named("com.atlassian.bridges.search.SearchServiceBridge74")
@SupportedVersions(minimumInclusive = "7.4.0", maximumExclusive = “7.12.0")
final class SearchServiceBridge74 implements SearchServiceBridge {
@Override
public Collection<Issue> search(Query query)
throws PlatformException {
…
}
}
@ExportAsService({SearchServiceBridge.class})
@Named("com.atlassian.bridges.search.SearchServiceBridge712")
@SupportedVersions(minimumInclusive = "7.12.0", maximumExclusive = "8.0")
final class SearchServiceBridge712 implements SearchServiceBridge {
@Override
public Collection<Issue> search(Query query)
throws PlatformException {
…
}
}
Versions
Manager
Bridges
Using the bridge
Building
bridges
Versions
Manager
public class MyCode {
Supplier<SearchServiceBridge> searchService;
void doMyThing() {
searchService.get().search(query);
}
}
Bridges
Using the bridge
Building a bridge
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-api</artifactId>
<version>7.6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-api</artifactId>
<version>7.12.0</version>
<scope>provided</scope>
</dependency>
BRIDGES 7.12
BRIDGES 7.6
BRIDGE INTERFACES
pom
APP
pom
Don’t depend
on core/
internal if you
can help it
Developing cross product
ATST Product & Version supported matrix
ATST Project structure
API COMMON
ATST
ATST Project structure
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-api</artifactId>
<scope>provided</scope>
</dependency>
PLUGIN-JIRA
API
pom
COMMON
ATST
ATST Project structure
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.confluence</groupId>
<artifactId>confluence-api</artifactId>
<scope>provided</scope>
</dependency>
PLUGIN-CONF
PLUGIN-JIRA
API
pom
pom
COMMON
ATST
Introducing a feature
1 Introduce required API in the ATST api module
Introducing a feature
1 Introduce required API in the ATST api module
2 If implementation is shared, add it to ATST common module
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
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
Tips for cross product development
1 Use of SAL (Shared Access Layer) APIs to simplify development
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
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
Developing for Data Centre
Developing for Data Centre
1 Keep the concepts of ‘licenced for data-center’,
‘clustering available’ and ‘clustering active’ as
separate and do not mix
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
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
Thank you!
VICTORIA SKALRUD | SENIOR DEVELOPMENT TL | ATLASSIAN

More Related Content

PDF
Leaning into Server to Cloud App Migration
PDF
Scaling Indexing and Replication in Jira Data Center Apps
PDF
Building a Cerberus App Without Losing Our Heads: The Passage to a Cross-Plat...
PDF
Spec-first API Design for Speed and Safety
PDF
Declaring Server App Components in Pure Java
PDF
Take Action with Forge Triggers
PDF
From AUI to Atlaskit - Streamlining Development for Server & Cloud Apps
PDF
Integration Testing on Steroids: Run Your Tests on the Real Things
Leaning into Server to Cloud App Migration
Scaling Indexing and Replication in Jira Data Center Apps
Building a Cerberus App Without Losing Our Heads: The Passage to a Cross-Plat...
Spec-first API Design for Speed and Safety
Declaring Server App Components in Pure Java
Take Action with Forge Triggers
From AUI to Atlaskit - Streamlining Development for Server & Cloud Apps
Integration Testing on Steroids: Run Your Tests on the Real Things

What's hot (20)

PDF
The New & Improved Confluence Server and Data Center
PDF
How to Build a Better JIRA Add-on
PDF
Designing and Running a GraphQL API
PDF
What's New in Jira Cloud for Developers
PDF
What Does Jira Next-Gen Mean for Cloud Apps?
PDF
The User Who Must Not be Named: GDPR and Your Jira App
PDF
4 Changes We're Making to Help you be Successful in the Cloud
PDF
Creating Your Own Server Add-on that Customizes Confluence or JIRA
PDF
Atlassian Connect on Serverless Platforms: Low Cost Add-Ons
PDF
Integrate CI/CD Pipelines with Jira Software Cloud
PDF
Serverless Analytics and Monitoring For Your Cloud App
PDF
Access to User Activities - Activity Platform APIs
PDF
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
PDF
Server Add-ons for Front-end Developers
PDF
Preparing for Data Residency and Custom Domains
PDF
Integrating Jira Software Cloud With the AWS Code Suite
PDF
Meet the Forge Runtime
PDF
Ten Battle-Tested Tips for Atlassian Connect Add-ons
PDF
Discover the Possibilities of the Jira Cloud Asset API
PDF
Forge: Under the Hood
The New & Improved Confluence Server and Data Center
How to Build a Better JIRA Add-on
Designing and Running a GraphQL API
What's New in Jira Cloud for Developers
What Does Jira Next-Gen Mean for Cloud Apps?
The User Who Must Not be Named: GDPR and Your Jira App
4 Changes We're Making to Help you be Successful in the Cloud
Creating Your Own Server Add-on that Customizes Confluence or JIRA
Atlassian Connect on Serverless Platforms: Low Cost Add-Ons
Integrate CI/CD Pipelines with Jira Software Cloud
Serverless Analytics and Monitoring For Your Cloud App
Access to User Activities - Activity Platform APIs
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
Server Add-ons for Front-end Developers
Preparing for Data Residency and Custom Domains
Integrating Jira Software Cloud With the AWS Code Suite
Meet the Forge Runtime
Ten Battle-Tested Tips for Atlassian Connect Add-ons
Discover the Possibilities of the Jira Cloud Asset API
Forge: Under the Hood
Ad

Similar to Practical Patterns for Developing a Cross-product Cross-version App (20)

PDF
apidays LIVE Paris 2021 - Lessons from the API Stewardship Journey in Azure b...
PPTX
Lessons learned on the Azure API Stewardship Journey.pptx
PDF
Microservices and the Art of Taming the Dependency Hell Monster
PDF
#ATAGTR2020 Presentation - Microservices – Explored
PDF
Building Software That Lasts
PPTX
apidays LIVE New York 2021 - Service API design validation by Uchit Vyas, KPMG
PDF
Service api design validation & collaboration
PDF
Microservices Architecture
PDF
Feature Bits at LSSC10
PDF
SOA Latam 2015
PDF
Top REST API Desgin Pitfalls @ Devoxx 2024
PPTX
How to deal with REST API Evolution
PDF
LF_APIStrat17_REST API Microversions
PDF
Get Ready for JIRA 5 - AtlasCamp 2011
PPTX
Building a REST API for Longevity
PPTX
Developing for the Atlassian Ecosystem
PDF
Microservices: The Best Practices
KEY
EcoSystem Tools for Admins - AtlasCamp 2011
PDF
Versioning in a microservice architecture
PPTX
apidays LIVE Paris 2021 - Inside API delivery Pipeline, the checklist! - Fran...
apidays LIVE Paris 2021 - Lessons from the API Stewardship Journey in Azure b...
Lessons learned on the Azure API Stewardship Journey.pptx
Microservices and the Art of Taming the Dependency Hell Monster
#ATAGTR2020 Presentation - Microservices – Explored
Building Software That Lasts
apidays LIVE New York 2021 - Service API design validation by Uchit Vyas, KPMG
Service api design validation & collaboration
Microservices Architecture
Feature Bits at LSSC10
SOA Latam 2015
Top REST API Desgin Pitfalls @ Devoxx 2024
How to deal with REST API Evolution
LF_APIStrat17_REST API Microversions
Get Ready for JIRA 5 - AtlasCamp 2011
Building a REST API for Longevity
Developing for the Atlassian Ecosystem
Microservices: The Best Practices
EcoSystem Tools for Admins - AtlasCamp 2011
Versioning in a microservice architecture
apidays LIVE Paris 2021 - Inside API delivery Pipeline, the checklist! - Fran...
Ad

More from Atlassian (20)

PPTX
International Women's Day 2020
PDF
10 emerging trends that will unbreak your workplace in 2020
PDF
Forge App Showcase
PDF
Let's Build an Editor Macro with Forge UI
PDF
Forge UI: A New Way to Customize the Atlassian User Experience
PDF
Observability and Troubleshooting in Forge
PDF
Trusted by Default: The Forge Security & Privacy Model
PDF
Designing Forge UI: A Story of Designing an App UI System
PDF
Design Your Next App with the Atlassian Vendor Sketch Plugin
PDF
Tear Up Your Roadmap and Get Out of the Building
PDF
Nailing Measurement: a Framework for Measuring Metrics that Matter
PDF
Building Apps With Color Blind Users in Mind
PDF
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
PDF
Beyond Diversity: A Guide to Building Balanced Teams
PDF
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
PDF
Building Apps With Enterprise in Mind
PDF
Shipping With Velocity and Confidence Using Feature Flags
PDF
Build With Heart and Balance, Remote Work Edition
PDF
How to Grow an Atlassian App Worthy of Top Vendor Status
PDF
Monitoring As Code: How to Integrate App Monitoring Into Your Developer Cycle
International Women's Day 2020
10 emerging trends that will unbreak your workplace in 2020
Forge App Showcase
Let's Build an Editor Macro with Forge UI
Forge UI: A New Way to Customize the Atlassian User Experience
Observability and Troubleshooting in Forge
Trusted by Default: The Forge Security & Privacy Model
Designing Forge UI: A Story of Designing an App UI System
Design Your Next App with the Atlassian Vendor Sketch Plugin
Tear Up Your Roadmap and Get Out of the Building
Nailing Measurement: a Framework for Measuring Metrics that Matter
Building Apps With Color Blind Users in Mind
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Beyond Diversity: A Guide to Building Balanced Teams
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
Building Apps With Enterprise in Mind
Shipping With Velocity and Confidence Using Feature Flags
Build With Heart and Balance, Remote Work Edition
How to Grow an Atlassian App Worthy of Top Vendor Status
Monitoring As Code: How to Integrate App Monitoring Into Your Developer Cycle

Recently uploaded (20)

PDF
Encapsulation theory and applications.pdf
PDF
Machine learning based COVID-19 study performance prediction
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Modernizing your data center with Dell and AMD
PDF
cuic standard and advanced reporting.pdf
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PPTX
MYSQL Presentation for SQL database connectivity
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PPT
Teaching material agriculture food technology
PDF
Empathic Computing: Creating Shared Understanding
PDF
NewMind AI Monthly Chronicles - July 2025
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Encapsulation theory and applications.pdf
Machine learning based COVID-19 study performance prediction
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Modernizing your data center with Dell and AMD
cuic standard and advanced reporting.pdf
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
The Rise and Fall of 3GPP – Time for a Sabbatical?
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
MYSQL Presentation for SQL database connectivity
NewMind AI Weekly Chronicles - August'25 Week I
Chapter 3 Spatial Domain Image Processing.pdf
Building Integrated photovoltaic BIPV_UPV.pdf
Reach Out and Touch Someone: Haptics and Empathic Computing
Mobile App Security Testing_ A Comprehensive Guide.pdf
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Teaching material agriculture food technology
Empathic Computing: Creating Shared Understanding
NewMind AI Monthly Chronicles - July 2025
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx

Practical Patterns for Developing a Cross-product Cross-version App

  • 1. VICTORIA SKALRUD | SENIOR DEVELOPMENT TL | ATLASSIAN Practical Patterns for Developing a Cross- product Cross-version App
  • 2. Lessons from working on the Atlassian Troubleshooting &Support Tools (ATST)
  • 7. Developing cross version Why developing to support multiple versions of a product is a good idea
  • 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
  • 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
  • 21. CHALLENGE #2 I WANT TO WRITE MY FEATURE TO BE RESILIENT ACROSS API CHANGES
  • 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
  • 29. You can build a bridge! BETTER WAY
  • 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
  • 40. Building bridges @Retention(RetentionPolicy.RUNTIME) public @interface SupportedVersions { String minimumInclusive() default “0.0.0"; String maximumExclusive() default "99999.99999.99999"; } Versioning Manager Bridges Using the bridge
  • 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
  • 43. Building bridges public interface SearchServiceBridge { Collection<Issue> search(Query query) throws PlatformException; } Versions Manager Bridges Using the bridge
  • 44. Building bridges public interface SearchServiceBridge { Collection<Issue> search(Query query) throws PlatformException; } @ExportAsService({SearchServiceBridge.class}) @Named("com.atlassian.bridges.search.SearchServiceBridge74") @SupportedVersions(minimumInclusive = "7.4.0", maximumExclusive = “7.12.0") final class SearchServiceBridge74 implements SearchServiceBridge { @Override public Collection<Issue> search(Query query) throws PlatformException { … } } Versions Manager Bridges Using the bridge
  • 45. Building bridges public interface SearchServiceBridge { Collection<Issue> search(Query query) throws PlatformException; } @ExportAsService({SearchServiceBridge.class}) @Named("com.atlassian.bridges.search.SearchServiceBridge74") @SupportedVersions(minimumInclusive = "7.4.0", maximumExclusive = “7.12.0") final class SearchServiceBridge74 implements SearchServiceBridge { @Override public Collection<Issue> search(Query query) throws PlatformException { … } } @ExportAsService({SearchServiceBridge.class}) @Named("com.atlassian.bridges.search.SearchServiceBridge712") @SupportedVersions(minimumInclusive = "7.12.0", maximumExclusive = "8.0") final class SearchServiceBridge712 implements SearchServiceBridge { @Override public Collection<Issue> search(Query query) throws PlatformException { … } } Versions Manager Bridges Using the bridge
  • 46. Building bridges Versions Manager public class MyCode { Supplier<SearchServiceBridge> searchService; void doMyThing() { searchService.get().search(query); } } Bridges Using the bridge
  • 48. Don’t depend on core/ internal if you can help it
  • 50. ATST Product & Version supported matrix
  • 54. Introducing a feature 1 Introduce required API in the ATST api module
  • 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
  • 65. Thank you! VICTORIA SKALRUD | SENIOR DEVELOPMENT TL | ATLASSIAN