About us
Garrett JonesStaff Software Engineer
Google Seattle
Tomohiro SuzukiSoftware EngineerGoogle New York
Agenda
● Intro to diamond dependency conflicts
● Google's Java Library Best Practices
● Linkage Checker
● Q&A
Story
● Google monorepo vs OSS independent libraries
● Megathread prompted by user complaints
● No consensus
● Proposals, summits, proposals, hackathons
● Wrote best practices, created tools
Diamond Dependency Conflicts
Diamond dependency conflicts: A visual representation
D:1
B:1
A:1 Linear dependency graph: everything is happy
A:1 Library A, version 1
Legend
Diamond dependency conflicts: A visual representation
D:1
B:1
A:1
D:2
D introduces a new major version (D:2)
Diamond dependency conflicts: A visual representation
D:1
B:1
A:1
D:2
C:1
Another package, C, declares a dependency on D:2
Diamond dependency conflicts: A visual representation
D:1
B:1
A:1
D:2
C:1
A:2 A new version of A attempts to add C as a dependency. Diamond dependency conflict! Only one of D:1 or D:2 can be chosen.
Diamond dependency conflicts: A visual representation
D:1
B:1
A:1
D:2
C:1
A:2 When D:2 is selected, this breaks B
Diamond dependency conflicts: A visual representation
D:1
B:1
A:1
D:2
C:1
A:2 When D:1 is selected, this breaks C
Diamond dependency conflicts: A visual representation
D:1
B:1
A:1
D:2
B:2
C:1
A:2 The only solution: A has to force B to make a new version that depends on D:2
Google's approach
1) Fix burning conflicts2) Establish best practices for library developers*3) Create tools*4) Create BOMs (bill of materials) for library users
Java Library Best Practices(for library developers)
Google's Java library best practices (JLBP)
● 18 best practices, published at github.com/cloud-opensource-java/library-best-practices
● I will cover only 5 here
JLBP-1: Minimize dependenciesJLBP-2: Minimize API surfaceJLBP-3: Use Semantic versioningJLBP-4: Avoid dependencies on unstable libraries and featuresJLBP-5: Avoid dependencies that overlap classes with other dependenciesJLBP-6: Rename artifacts and packages togetherJLBP-7: Make breaking transitions easyJLBP-8: Advance widely used functionality to a stable versionJLBP-9: Support the minimum Java version of your consumersJLBP-10: Maintain API stability as long as needed for consumersJLBP-11: Stay up to date with compatible dependenciesJLBP-12: Make level of support and API stability clearJLBP-13: Quickly remove references to deprecated features in dependenciesJLBP-14: Do not use version rangesJLBP-15: Produce a BOM for multi-module projectsJLBP-16: Ensure upper version alignment of dependencies for consumersJLBP-17: Coordinate Rollout of Breaking ChangesJLBP-18: Only shade dependencies as a last resort
Problem 1: "Overlapping" classes
A
class X
Problem 1: "Overlapping" classes
B
A
class X
Problem 1: "Overlapping" classes
B
A
class X
C
Problem 1: "Overlapping" classes
B
A
class X
class Y
C
Problem 1: "Overlapping" classes
B
A
class X
class Y
Cclass
Z
Problem 1: "Overlapping" classes
B
class O
A
class X
class Y
C
class O
class Z
Problem 1: "Overlapping" classes
B
class O
A
class X
Classes loaded:● class X [from library A]● class Y [from library B]● class O [from library B]● class Z [from library C]
class Y
C
class O
class Z
Problem 1: "Overlapping" classes
B
class O
A
class X
Classes loaded:● class X [from library A]● class Y [from library B]● class O [from library B]● class Z [from library C]
Classes not loaded:● class O [from library C]
class Y
C
class O
class Z
Problem 1: "Overlapping" classes
B
class O
A
class X
Classes loaded:● class X [from library A]● class Y [from library B]● class O [from library B]● class Z [from library C]
Classes not loaded:● class O [from library C]
What can break?● calls from Z to O (for things not in the version of O
included in B)class
Y
C
class O
class Z
What needs to be true?
Every fully-qualified class name must be present in only one library in the dependency tree
JLBP-5: Avoid dependencies that overlap classes with other dependencies
JLBP-6: Rename artifacts and packages together
Example violation of JLBP-5 & JLBP-6
javax.servlet:javax.servlet-api:3.1.0& javax.servlet:servlet-api:2.5
both contain classes with the same names under javax.servlet.
JLBP-6 corollaries
● Don't combine artifacts together while keeping the same fully-qualified names● Don't split artifacts apart while keeping the same fully-qualified names
Problem 2: Wide scale breakage from changes
guava
Problem 2: Wide scale breakage from changes
guava
F G
Problem 2: Wide scale breakage from changes
guava
F G
B C D E
Problem 2: Wide scale breakage from changes
guava
F G
B C D E
A
Problem 2: Wide scale breakage from changes
guava
F G
B C D E
A
Problem 2: Wide scale breakage from changes
guava
F G
B C D E
A
JLBP-8: Advance widely used functionality to a stable version
(also follow JLBP-3: Use Semantic Versioning - "stable version" = 1.0.0+)
Example violation of JLBP-8
com.google.auth:google-auth-library-java: still uses a 0.x version
Problem 3: finding compatible dependencies1.0
A
X
B
70.0
Y
Problem 3: finding compatible dependencies1.0
A
X
B
70.0
Y
2.0
A
X
B
71.0
Y Z
Problem 3: finding compatible dependencies1.0
A
X
B
70.0
Y
2.0
A
X
B
71.0
Y Z
3.0
A
X
B
72.0
Y Z
Problem 3: finding compatible dependencies1.0
A
X
B
70.0
Y
2.0
A
X
B
71.0
Y Z
3.0
A
X
B
72.0
Y Z
4.0
A
X
B
73.0
Y Z
Problem 3: finding compatible dependencies1.0
A
X
B
70.0
Y
2.0
A
X
B
71.0
Y Z
3.0
A
X
B
72.0
Y Z
4.0
A
X
B
73.0
Y Z
5.0
A
X
B
74.0
Z
Problem 3: finding compatible dependencies
A:1.0
A:2.0
A:3.0
A:4.0
A:5.0
B:70.0
B:71.0
B:72.0
B:73.0
B:74.0
For the latest A (5.0)The latest A (5.0) can use the last 4 versions of B.
This is nice and flexible.
Problem 3: finding compatible dependencies
A:1.0
A:2.0
A:3.0
A:4.0
A:5.0
B:70.0
B:71.0
B:72.0
B:73.0
B:74.0
For the latest B (74.0)The latest B (74.0) can only use the last version of A (5.0).
This is somewhat restrictive!
Problem 3: finding compatible dependencies1.0
A
X
B
70.0
Y
Problem 3: finding compatible dependencies1.0
A
X
B
70.0
Y
2.0
A
X
B
71.0
Y Z
Problem 3: finding compatible dependencies1.0
A
X
B
70.0
Y
2.0
A
X
B
71.0
Y Z
3.0
A
X
B
72.0
Y Z
Problem 3: finding compatible dependencies1.0
A
X
B
70.0
Y
2.0
A
X
B
71.0
Y Z
3.0
A
X
B
72.0
Y Z
4.0
A
X
B
73.0
Y Z
Problem 3: finding compatible dependencies1.0
A
X
B
70.0
Y
2.0
A
X
B
71.0
Y Z
3.0
A
X
B
72.0
Y Z
4.0
A
X
B
73.0
Y Z
5.0
A
X
B
74.0
Z
Problem 3: finding compatible dependencies
A:1.0
A:2.0
A:3.0
A:4.0
A:5.0
B:70.0
B:71.0
B:72.0
B:73.0
B:74.0
For the latest A (5.0)The latest A (5.0) can still use the last 4 versions of B.
No regression, things are going good.
Problem 3: finding compatible dependencies
A:1.0
A:2.0
A:3.0
A:4.0
A:5.0
B:70.0
B:71.0
B:72.0
B:73.0
B:74.0
For the latest B (74.0)The latest B (74.0) can can used with the last 4 versions of A.
Great improvement! This is a lot more flexible.
JLBP-13: Quickly remove references to deprecated features in dependencies
Example violation of JLBP-13
com.google.api:api-common-java: usage of deprecated methods in Guava removed 1 year + 3 months after deprecation (1.7.0), instead of earlier (e.g. 1.2.0)
What about shading?
A
What about shading?
BA
What about shading?
BA
CA
What about shading?
D
BA
CA
A
What about shading?
D
BA
CA
A
E
BA
CA
A
What about shading?
F
D
BA
CA
A
E
BA
CA
A
ABA
CA
What about shading?
F
D
BA
CA
A
E
BA
CA
A
ABA
CA
G
D
BA
CA
A
E
BA
CA
A
ABA
CA
H
What about shading?
F
D
BA
CA
A
E
BA
CA
A
ABA
CA
G
D
BA
CA
A
E
BA
CA
A
ABA
CA
ABA
CA
D
BA
CA
A
E
BA
CA
A
What about shading?
A
B
C
D
E
F
G
H
What about shading?
No shading: 8 units
With shading: 54 units (27 of which are copies of A)
54/8 = 6.75 x the size!
What about shading?
Other problems:
● Bad shading config can create overlapping classes or missing classes● Shaded dependencies can't be overridden to roll out security fixes● Shading doesn't work well with JNI or reflection
JLBP-18: Only shade dependencies as a last resort
This page is intentionally left blank
github.com/GoogleCloudPlatform/cloud-opensource-java
Consistency in OSS Libraries: Google’s Approach
Linkage Checker
Tomohiro Suzuki ([email protected])
Agenda: Linkage Checker
• Demo
• Implementation
• Case Study
Linkage Checker: Demo
Demo: Linkage Checker Maven Enforcer Rule
Problem: a simple project having dependency conflicts
Linkage Checker Enforcer Rule detects the conflict Project
google-api-client grpc-core
guava version: 26.0guava version: 20.0
Demo: Linkage Checker Maven Enforcer Rule
A simple project with two dependencies
Demo: Linkage Checker Maven Enforcer Rule
No issue in compile
Demo: Linkage Checker Maven Enforcer Rule
Runtime Error!
Demo: Linkage Checker Maven Enforcer Rule
Q: Why java compiler does not detect the conflict?
A: the compiler only checks the references from the project
Project
google-api-client
grpc-core
guava version: 26.0guava version: 20.0
App.java
DnsNameResolver.class
Verify.class
Demo: Linkage Checker Maven Enforcer Rule
Add the enforcer rule
Demo: Linkage Checker Maven Enforcer Rule
$ mvn install
Demo: Google Libraries BOM Dashboard
Checks compatibility of artifacts in BOM (a set of libraries)
https://github.com/GoogleCloudPlatform/cloud-opensource-java#google-cloud-platform-java-dependency-dashboard
Linkage Checker: Implementation
Creates dependency tree of Maven artifacts
(Maven’s dependency mediation)
Project
Library Aversion 1.0 Library B
Library C Library AVersion 2.0
Implementation Step 1: Dependency Tree
Library D
Implementation Step 2: JAR, Class files, and Constant Pool
Extracts references from constant pool
• JAR file• Class File
• Constant Pool
(Example)io.grpc.internal.DnsNameResolver constant pool:
- class io.grpc.NameResolver ... - method: “com.google.base.Verify.verify(bool, String, Object)”
Implementation Step 3: Verification of Referents
Verifies the referents of the references
Does the referent (class B) exist?Does the referent have the method with the expected signature?Does the method accessible from class A?etc...
Class A
Constant Pool: ... - ClassB.methodX(String, Object)
Class B
public methodX(String, Object)private methodY(String)(default) methodZ()
Case Study
Case 1: Missing Class
JLBP-18: Only shade dependencies as a last resort
mockito
bytebuddy
grpc-testing
asm-commons
ModuleHashesAttribute
Case 2: Missing Method
Runtime Error from the demoNoSuchMethodError Verify.verify(ZLjava/lang/String;Ljava/lang/Object;)V => void verify(boolean, String, Object)
Case 2: Missing Method Project
google-api-client
grpc-core
guava version: 26.0guava version: 20.0
DnsNameResolver.class
Verify.class public verify(boolean, String, Object...) Verify.class
public verify(boolean, String, Object)
Java Language Specification Chapter 13: Binary Compatibility
Case 3: Missing Constructor
Case 3: Missing Constructor
spring-cloud-gcp-starter
spring-cloud-gcp-starter-trace:1.1.0.RC2 spring-cloud-gcp-starter-pubsub:1.1.0.RC2
...
Project
grpc-netty-shaded:1.16.1 grpc-core:1.17.0
...
class AbstractClientStream { AbstractClientStream(Headers, Option) { ...
class NettyClientStream extends AbstractClientStream { super(headers); ...
Conclusion
Diamond dependency issues: Dependency tree generated by different libraries may have conflicts.
• Java Library Best Practices
• Linkage Checker
https://github.com/GoogleCloudPlatform/cloud-opensource-java/
Q&A