SlideShare a Scribd company logo
Rewriting a Plugin Architecture 3 Times to Harness the API Economy
Rewriting a Plugin Architecture 3 Times
to Harness the API Economy
A couple of Atlassians who are passionate about plugins
Tim Pettersen
Developer Provocateur
@kannonboy
Ian Buchanan
Developer Partisan
@devpartisan
API
Economy
Economy
$50,000,000
*Paid to ISVs to date.
*2011. http://techcrunch.com/2012/04/24/facebook-revenue-share-apps/
15,000,000 paying
users
$1,000,000,000
*Paid to ISVs in 2013. http://www.partner-path.com/2013/12/salesforce-partner-ecosystem/
Blue logos?
REST? OAuth?
API
Economy
SPI
API Application
Programming
Interface
SPI Service
Provider
Interface
Atlassian
Plugins
(2004)
P2
(2008)
Atlassian
Connect
(2014)
Rewriting a Plugin Architecture 3 Times to Harness the API Economy
Rewriting a Plugin Architecture 3 Times to Harness the API Economy
Atlassian
Plugins
(2004)
Open(-ish)
Source
[2001]
Why it’s awesome!
Customers can “scratch their own itch”
Atlassian devs can focus on the 90%
Customization == stickier product
Devs who customize, evangelize
Why it sucks..
Developer support load
Poor plugin UX == poor product
perception
Pain to build the entire product
Massive barrier to upgrade
Atlassian
Plugins
(2004)
JIRA 3.0
How?
Wherever there is a collection of
type FOO, let the end user inject
their own implementations of FOO.
“Web fragments”
<ul>
</ul>
<li><a href=“/dashboard.jspa”>Home</a></li>
<li><a href=“/project.jspa”>Browse Project</a></li>
<!-- … -->
<li><a href=“/admin.jspa”>Administration</a></li>
interface WebItem {
String getHref();
String getText();
}
class FooWebLink implements WebItem {
}
public String getHref() {
return “http://foo.com”;
}
public String getText() {
return “Foo”;
}
List<WebItem> getModules()
PluginAccessor
<li><a href=“/dashboard.jspa”>Home</a></li>
<li><a href=“/project.jspa”>Browse Project</a></li>
<!-- … -->
<li><a href=“/admin.jspa”>Administration</a></li>
<ul>
</ul>
<% for (WebItem wi : pluginAccessor.getModules()) { %>
<li>
<a href=“<% wi.getHref() %>”>
<% wi.getText() %>
</a>
</li>
<% } %>
Plugins promote abstraction and enforce
modular architecture!
Two plugins
looks great!
3+ plugins.. not
so hot.
Every (pluggable) collection is now potentially
infinitely large.
List<WebItem> getModules()List<Type> getModules(Type)
PluginAccessor
public void transitionIssue(Issue issue, Transition tn) {
// update issue state
// ..
// send email to watchers
for (User watcher : issue.getWatchers()) {
issue.emailTo(watcher);
}
}
public void transitionIssue(Issue issue, Transition tn) {
// update issue state
// ..
}
public static interface PostFn {
void onTransition(Issue issue);
}
public class EmailFn implements PostFn {
public void onTransition(issue) {
for (User watcher : issue.getWatchers()) {
issue.emailTo(watcher);
}
}
}
public void transitionIssue(Issue issue, Transition tn) {
// update issue state
// ..
// trigger post functions
for (PostFn fn : pluginAccessor.get(PostFn.class)) {
fn.onTransition(issue);
}
}
public class EmailFn implements PostFn {
public void onTransition(issue) {
for (User watcher : issue.getWatchers()) {
issue.emailTo(watcher);
}
}
}
Adding a new “plugin point” is easy if you
already have good abstraction!
You SHOULD make “pluginizing” a feature
as simple & quick as possible. Otherwise if
there’s a crunch for time, it won’t happen.
Spike it
Make it
work
Test it
Add an
API / SPI
Ship it
for (PostFn fn : pluginAccessor.get(PostFn.class)) {
fn.onTransition(issue);
}
ArithmeticException
StackOverflowError
while (true) {}
OutOfMemoryError
ClassNotFoundException
ClassCastException
Murphy’s law:
If a plugin can fail, it will.
Horrifically.
for (PostFn fn : pluginAccessor.get(PostFn.class)) {
fn.onTransition(issue);
}
Code defensively
try {
} catch (Exception e) {
log.error(“Plugin post-function failed”, e);
}
log.debug(“Invoking ” + fn);
Corollary:
If an API is public, it will be used.
Often incorrectly.
public void publicApiMethod(List<?> list)
..
}
Show them the way
if (list == null) throw new NullPointerException(
“list must not be null”
);
if (list.isEmpty()) throw new IllegalArgumentException(
“list must not be empty”
) );
/**
* @param list a non-null, non-empty list
*/
Backend
• Custom fields
• JQL functions
• Workflows (post-functions, conditions)
generic
• Servlets
• Filters
• Event listeners
https://bitbucket.org/atlassian/atlassian-plugins
Source (BSD 3-clause licensed)
Embedding Guide
http://bit.ly/embedding-plugins
Atlassian Plugins
• Host application exposes some “plugin point”
interfaces
public static interface PostFn {
void onTransition(Issue issue);
}
• Plugin developer provides concrete implementations
public class EmailFn implements PostFn {
// send email to watchers
for (User watcher : issue.getWatchers()) {
issue.emailTo(watcher);
}
}
How it works (1 of 3)
• Plugin developer adds an XML descriptor (remember
it’s 2004)
<post-function class=“com.foo.EmailFn” weight=“10” />
• Developer packages up the class and XML in a JAR
and puts it on the classpath
atlassian-jira/WEB-INF/lib/my-cool-plugin-1.0.jar/
How it works (2 of 3)
atlassian-plugin.xml
com/foo/EmailFn.class
• On startup, plugin system parses the XML and
instantiates classes with Spring DI
• The PluginAccessor is injected into other
components and used to look up modules.
• Because the class path is static, the PluginAccessor
caches plugins very aggressively.
How it works (3 of 3)
commons-
lang.jar
atlassian-
jira.jar
plugin0.jar plugin1.jar
WEB-INF/lib
interface
com.atlassian.PostFn
class
com.foo.EmailFn
implements
my-cool-
lib.jar
Why it’s awesome!
Easier customization by end-users
Smoother upgrades
Improved customization UX
Architectural benefits
Simple to implement
Why it sucks..
Totally bespoke, non-standard
Need to restart to install plugins
No API delineation
Shared classpath
Re-architecting as plugins
a bunch of
work
Dogfooding APIs?
product
manager
“PM friendly” benefits!
Release plugins out of band
Ship “dark features”
Customers can turn stuff off
Release paid plugins ($$$)
Architecture == velocity
Make sure your plugin
system works.
Nothing turns off a plugin developer like
a broken API or an unstable platform.
P2
(2008)
P2
(2008)
Why plugins suck..
Totally bespoke, non-standard
Need to restart to install plugins
No API delineation
Shared classpath
OSGi
commons-
lang.jar
atlassian-
jira.jar
plugin0.jar plugin1.jar
WEB-INF/lib
my-cool-
lib.jar
commons-
lang-3.1.jar
atlassian-
jira.jar
WEB-INF/lib
Export-Package:
com.atlassian.jira;version=“5.0”,
org.apache;version=“3.1”,
…
MANIFEST.MF
Import-Package:
com.atlassian.jira;version=“5.0”,
MANIFEST.MF
Export-Package:
org.apache;version=“3.3”
plugin1.jar
Import-Package:
org.apache;version=“3.3”
MANIFEST.MF
my-cool-
lib.jar
plugin0.jar
commons-
lang-3.3.jar
Why it sucks..
Totally bespoke, non-standard
Need to restart to install plugins
No API delineation
Shared classpath
Why it sucks..
OSGi is pretty
complicated
175Kb of text
JIRA Agile’s
MANIFEST.MF
P1 P2
<post-function class=“com.foo.EmailFn”
weight=“10” />
public class EmailFn implements PostFn {..}
+
=
a working plugin
<post-function class=“com.foo.EmailFn”
weight=“10” />
public class EmailFn implements PostFn {..}
Import-Package:
com.atlassian.jira;version=“5.0”,
Export-Package:
org.apache;version=“3.3”
<component>
..
</component>
+
+
+
=
a working plugin
… maybe
Abstracting OSGi and a few
other things..( )
C nsPr s
• Less upgrade pain
• Easy onboarding for
new users
• Startup time
• Leaky abstraction
• Non-standard use
of standard tech
• Proxies all the way
down
• Complexities
Was it
worth it?
Probably.
Supporting
your ecosystem
Photo: Nicholas A. Tonelli
Rewriting a Plugin Architecture 3 Times to Harness the API Economy
Good for devs!
Paint-by-numbers marketing
Pricing & licensing APIs
Feedback
Tracking & analytics
Good for customers!
Central authority for add-ons
Less fragmented UX
Ratings & reviews
Unified billing
Focus shifts from user to
vendor customization
Building a marketplace is
non-trivial
Rewriting a Plugin Architecture 3 Times to Harness the API Economy
Tim’s top tip for dev docs
Keep your documentation as close to
the code as possible.
Bind the code..
..to the docs!
master feature
Write the
docs
Code talks!
Make sure you have a simple process
for open sourcing and liberally
licensing example add-ons.
Meta-Developer Tools
Photo: Biser Todorov
SDK
Fully automate plugin ALM
Spin up products in “developer mode”
Tight development loop
Provide realistic test data
Dogfood it!
FREE
/stash/projects
/stash/rest/api/1.0/projects
/stash/rest/api/1.0/projects
/stash/projects
?web.panels&web.items/stash/projects
?web.panels&web.items/stash/projects
Developer Mode
Trim out dev speed killers (websudo, etc)
Expose logs & debug info
Release your own internal
productivity tools
Add in custom “dev mode” plugins
(2014)
What is Atlassian Connect?
Remote Plugins
REST API
• Drive the product via its REST API
WebHooks
• Receive notifications from the product via WebHooks
• Inject content into the product UI via iframes
UI
Atlassian 

Product
Plugin
Service
Add-ons
Why?
Atlassian Cloud (SaaS)
Run 3rd party code out-of-process
Improved customization UX (yet again)
Architectural benefits
Rewriting a Plugin Architecture 3 Times to Harness the API Economy
• Connect is a new layer on
top of the older plugin
model.
• Same Marketplace serves
up Connect plugins.
• Same plugins still available
in the Marketplace.
• Same REST APIs still
available for both Server
and Cloud.
• Connect adds a new mode
of integration.
Building up
http://housebeauty.net/dressy-modern-townhouse-design-with-rooftop-garden-and-glass-walls/eye-catching-green-
roofed-penthouse-apartment-rooftop-garden-with-deck-surrounded-by-glass-balustrades/#image-1
How?
Wherever a module is called, 

let a third-party inject a new feature.
remember web fragments
AC WebPanel
AC IssueTab
• Declare plugin with a descriptor.
• Extend the Atlassian application UI with modules.
• Request appropriate scopes.
• Recognize the user.
• Call the Atlassian application's REST API.
• Respond to the Atlassian application's webhooks.
• Register on the Atlassian Marketplace.
• Respect plugin licensing.
How to author a Connect Plugin
"name": "Hello World", "description": "…",
"key": "com.example.myaddon", "apiVersion": 1,
"baseUrl": "http://example.com/myaddon",
"modules": {
"generalPages": [{
"name": { "value": "Greeting" }, "key": "hello-world",
"location": "system.top.navigation.bar",
"url": "/helloworld.html"
}]
},
"scopes": [ "read", "write" ],
"authentication": { "type": "jwt" },
"lifecycle": { "installed": "/add-on-installed-callback" }
where it is hosted
which modules it uses
which scopes it needs
recognize the user
Example Scopes for JIRA
PROJECT_ADMIN
DELETEWRITE
ADMIN
READ
GET /myaddon?jwt=<jwt-token> http/1.1
POST /jira/rest/api/2/issue/AC-1/attachments http/1.1
Authorization: JWT <jwt-token>
Recognizing the user with JWT
Example REST API for Confluence
• URLs for resources
• HTTP verbs
• Parmeters filter collections
• JSON data
• HAL(ish) links (newer APIs)
Example JIRA WebHooks
• HTTP callback to a URL
• Filtered by JQL
• When specific event occurs
"modules": {
"webhooks": [{
"event": "jira:issue_created",
"url": "/issue-created"
}]
}
Register on the Atlassian Marketplace
• Find: in context
catalog of add-ons
• Try: manage leads
• Buy: manage sales
and licensing
• Vagrant Box for local development

• JSON descriptor validator

• JWT decoder

• Marketplace wizard to create a
Connect listing

• Access Token model for private plugins

• Webhook Inspector
Developer Tools for Atlassian Connect
C nsPr s
• Code in any language
• Use any build & dev
tools
• Use anything:
databases, search
indexes, caching
services, etc
• Host and scale a
service
• No automated
devloop (P2 SDK)
• Provide everything:
databases, search
indexes, caching
services, etc
Why it’s awesome!
BYOS (Bring your own stack)
More services, than products
Sandboxing
UI Design
Aligned with microservice thinking
Why it sucks..
Complex to troubleshoot
Harder to create concrete examples
More developer tools, not just docs
More interfaces must be stable
Remember
SPIs?
P1
open(ish) source
P2
What’s Next?
Consolidate and expand
Vendor
add-on
• Extend Bitbucket and HipChat
• Build complex vertical apps: powered by Atlassian
• Build integrations between Atlassian applications
Atlassian
add-on
Questions?
A couple of Atlassians who are passionate about plugins
Tim Pettersen
Developer Provocateur
@kannonboy
Ian Buchanan
Developer Partisan
@devpartisan

More Related Content

PPTX
Plugin architecture (Extensible Application Architecture)
Chinmoy Mohanty
 
KEY
L0016 - The Structure of an Eclipse Plug-in
Tonny Madsen
 
PPTX
Building Eclipse Plugins
Liran Zelkha
 
ODP
Simplifying RCP Update and Install
susanfmccourt
 
PPT
Introduction To Eclipse RCP
whbath
 
PPT
Eclipse Plug-in Develompent Tips And Tricks
Chris Aniszczyk
 
PPTX
Eclipse RCP Demo
Csaba Toth
 
PPTX
Test automation with php codeception
buddhieash
 
Plugin architecture (Extensible Application Architecture)
Chinmoy Mohanty
 
L0016 - The Structure of an Eclipse Plug-in
Tonny Madsen
 
Building Eclipse Plugins
Liran Zelkha
 
Simplifying RCP Update and Install
susanfmccourt
 
Introduction To Eclipse RCP
whbath
 
Eclipse Plug-in Develompent Tips And Tricks
Chris Aniszczyk
 
Eclipse RCP Demo
Csaba Toth
 
Test automation with php codeception
buddhieash
 

What's hot (20)

PDF
SpringBoot
Jaran Flaath
 
PDF
Codeception
Jonathan Lau
 
PDF
Acceptance testing in php with Codeception - Techmeetup Edinburgh
Engineor
 
PPT
Eclipse IDE
Anirban Majumdar
 
PDF
What's new in Eclipse Mars
Lakshmi Priya
 
PDF
Testing with Codeception
Jeremy Coates
 
PDF
Spring Boot
Jaran Flaath
 
PDF
White paper mbre_en
VisioneerUG
 
PDF
Eclipse plug in development
Martin Toshev
 
PDF
Testing PHP with Codeception
John Paul Ada
 
PPTX
Eclipse Overview
Lars Vogel
 
PDF
Codeception introduction and use in Yii
IlPeach
 
PDF
Eclipse RCP outside of Eclipse IDE - Gradle to the rescue!
Michał Ćmil
 
PDF
Connecting Connect with Spring Boot
Vincent Kok
 
PPT
Eclipse RCP
Vijay Kiran
 
PPTX
Eclipse RCP Overview @ Rheinjug
Lars Vogel
 
PDF
Codeception: introduction to php testing
Engineor
 
ODP
Integration Testing in Python
Panoptic Development, Inc.
 
DOCX
Selenium interview questions
girichinna27
 
PDF
Rediscovering Spring with Spring Boot(1)
Gunith Devasurendra
 
SpringBoot
Jaran Flaath
 
Codeception
Jonathan Lau
 
Acceptance testing in php with Codeception - Techmeetup Edinburgh
Engineor
 
Eclipse IDE
Anirban Majumdar
 
What's new in Eclipse Mars
Lakshmi Priya
 
Testing with Codeception
Jeremy Coates
 
Spring Boot
Jaran Flaath
 
White paper mbre_en
VisioneerUG
 
Eclipse plug in development
Martin Toshev
 
Testing PHP with Codeception
John Paul Ada
 
Eclipse Overview
Lars Vogel
 
Codeception introduction and use in Yii
IlPeach
 
Eclipse RCP outside of Eclipse IDE - Gradle to the rescue!
Michał Ćmil
 
Connecting Connect with Spring Boot
Vincent Kok
 
Eclipse RCP
Vijay Kiran
 
Eclipse RCP Overview @ Rheinjug
Lars Vogel
 
Codeception: introduction to php testing
Engineor
 
Integration Testing in Python
Panoptic Development, Inc.
 
Selenium interview questions
girichinna27
 
Rediscovering Spring with Spring Boot(1)
Gunith Devasurendra
 
Ad

Similar to Rewriting a Plugin Architecture 3 Times to Harness the API Economy (20)

PPT
Plugins 2.0: The Overview
mrdon
 
ODP
Plug yourself in and your app will never be the same (1 hr edition)
Mikkel Flindt Heisterberg
 
PPT
The Web on OSGi: Here's How
mrdon
 
PPTX
Flask
Mamta Kumari
 
PDF
Declaring Server App Components in Pure Java
Atlassian
 
PDF
IBM ConnectED 2015 - BP106 From XPages Hero To OSGi Guru: Taking The Scary Ou...
Paul Withers
 
PPTX
Power Apps Component Framework - Dynamics Power! 365 Paris 2019
Allan De Castro
 
PPTX
SoapUI Pro Plugin Workshop #SoapUIPlugins
SmartBear
 
ODP
Plug yourself in and your app will never be the same (2 hour edition)
Mikkel Flindt Heisterberg
 
ODP
Plug yourself in and your app will never be the same (2 hr editon)
Mikkel Flindt Heisterberg
 
PDF
Google App Engine for Java
Lars Vogel
 
PDF
Jenkins - Automating Yourself Out Of A Job (One That You Don't Want)
lloydbenson
 
ODP
Lotusphere 2011 Jmp103 - Jumpstart Your "Jedi Plug-in Development Skills" wi...
Ryan Baxter
 
PDF
Serverless in production (O'Reilly Software Architecture)
Yan Cui
 
ODP
eXo Platform SEA - Play Framework Introduction
vstorm83
 
PPTX
Developing for the Atlassian Ecosystem
Alex Henderson
 
PPTX
Introduction to Google App Engine with Python
Brian Lyttle
 
PPT
Understanding and extending p2 for fun and profit
Pascal Rapicault
 
PPT
Maven Introduction
Sandeep Chawla
 
PPT
ASP.NET AJAX with Visual Studio 2008
Caleb Jenkins
 
Plugins 2.0: The Overview
mrdon
 
Plug yourself in and your app will never be the same (1 hr edition)
Mikkel Flindt Heisterberg
 
The Web on OSGi: Here's How
mrdon
 
Declaring Server App Components in Pure Java
Atlassian
 
IBM ConnectED 2015 - BP106 From XPages Hero To OSGi Guru: Taking The Scary Ou...
Paul Withers
 
Power Apps Component Framework - Dynamics Power! 365 Paris 2019
Allan De Castro
 
SoapUI Pro Plugin Workshop #SoapUIPlugins
SmartBear
 
Plug yourself in and your app will never be the same (2 hour edition)
Mikkel Flindt Heisterberg
 
Plug yourself in and your app will never be the same (2 hr editon)
Mikkel Flindt Heisterberg
 
Google App Engine for Java
Lars Vogel
 
Jenkins - Automating Yourself Out Of A Job (One That You Don't Want)
lloydbenson
 
Lotusphere 2011 Jmp103 - Jumpstart Your "Jedi Plug-in Development Skills" wi...
Ryan Baxter
 
Serverless in production (O'Reilly Software Architecture)
Yan Cui
 
eXo Platform SEA - Play Framework Introduction
vstorm83
 
Developing for the Atlassian Ecosystem
Alex Henderson
 
Introduction to Google App Engine with Python
Brian Lyttle
 
Understanding and extending p2 for fun and profit
Pascal Rapicault
 
Maven Introduction
Sandeep Chawla
 
ASP.NET AJAX with Visual Studio 2008
Caleb Jenkins
 
Ad

More from Tim Pettersen (6)

PDF
Git and Unity
Tim Pettersen
 
PDF
Tracking large game assets with Git LFS
Tim Pettersen
 
PDF
Code reviews vs Pull requests
Tim Pettersen
 
PDF
Tracking huge files with Git LFS - LinuxCon 2016
Tim Pettersen
 
PDF
Tracking huge files with Git LFS (GlueCon 2016)
Tim Pettersen
 
PDF
Master your metadata
Tim Pettersen
 
Git and Unity
Tim Pettersen
 
Tracking large game assets with Git LFS
Tim Pettersen
 
Code reviews vs Pull requests
Tim Pettersen
 
Tracking huge files with Git LFS - LinuxCon 2016
Tim Pettersen
 
Tracking huge files with Git LFS (GlueCon 2016)
Tim Pettersen
 
Master your metadata
Tim Pettersen
 

Recently uploaded (20)

PDF
On Software Engineers' Productivity - Beyond Misleading Metrics
Romén Rodríguez-Gil
 
PPTX
The-Dawn-of-AI-Reshaping-Our-World.pptxx
parthbhanushali307
 
PPTX
Web Testing.pptx528278vshbuqffqhhqiwnwuq
studylike474
 
PPTX
ConcordeApp: Engineering Global Impact & Unlocking Billions in Event ROI with AI
chastechaste14
 
PPT
Why Reliable Server Maintenance Service in New York is Crucial for Your Business
Sam Vohra
 
PDF
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
PPTX
GALILEO CRS SYSTEM | GALILEO TRAVEL SOFTWARE
philipnathen82
 
PDF
Key Features to Look for in Arizona App Development Services
Net-Craft.com
 
PPTX
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
PDF
10 posting ideas for community engagement with AI prompts
Pankaj Taneja
 
PPTX
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
PDF
Bandai Playdia The Book - David Glotz
BluePanther6
 
PDF
Exploring AI Agents in Process Industries
amoreira6
 
PPTX
Can You Build Dashboards Using Open Source Visualization Tool.pptx
Varsha Nayak
 
PDF
49785682629390197565_LRN3014_Migrating_the_Beast.pdf
Abilash868456
 
PDF
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
PPTX
Visualising Data with Scatterplots in IBM SPSS Statistics.pptx
Version 1 Analytics
 
PPT
Activate_Methodology_Summary presentatio
annapureddyn
 
PPTX
slidesgo-unlocking-the-code-the-dynamic-dance-of-variables-and-constants-2024...
kr2589474
 
PPTX
Odoo Integration Services by Candidroot Solutions
CandidRoot Solutions Private Limited
 
On Software Engineers' Productivity - Beyond Misleading Metrics
Romén Rodríguez-Gil
 
The-Dawn-of-AI-Reshaping-Our-World.pptxx
parthbhanushali307
 
Web Testing.pptx528278vshbuqffqhhqiwnwuq
studylike474
 
ConcordeApp: Engineering Global Impact & Unlocking Billions in Event ROI with AI
chastechaste14
 
Why Reliable Server Maintenance Service in New York is Crucial for Your Business
Sam Vohra
 
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
GALILEO CRS SYSTEM | GALILEO TRAVEL SOFTWARE
philipnathen82
 
Key Features to Look for in Arizona App Development Services
Net-Craft.com
 
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
10 posting ideas for community engagement with AI prompts
Pankaj Taneja
 
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
Bandai Playdia The Book - David Glotz
BluePanther6
 
Exploring AI Agents in Process Industries
amoreira6
 
Can You Build Dashboards Using Open Source Visualization Tool.pptx
Varsha Nayak
 
49785682629390197565_LRN3014_Migrating_the_Beast.pdf
Abilash868456
 
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
Visualising Data with Scatterplots in IBM SPSS Statistics.pptx
Version 1 Analytics
 
Activate_Methodology_Summary presentatio
annapureddyn
 
slidesgo-unlocking-the-code-the-dynamic-dance-of-variables-and-constants-2024...
kr2589474
 
Odoo Integration Services by Candidroot Solutions
CandidRoot Solutions Private Limited
 

Rewriting a Plugin Architecture 3 Times to Harness the API Economy