Managing dependencies in complex API ecosystems is challenging, but the problems are often addressed by semantic versioning (SemVer). While SemVer / Minimum Version Selection (MVS) is effective on a smaller scale, its reliance on predicting change severity without knowledge of how APIs are consumed becomes increasingly problematic as dependency networks grow.
The chapter proposes a change in perspective by advocating for a shift from relying on compatibility estimates provided by maintainers to embracing an evidence-based approach. In this approach, compatibility is determined by running tests on downstream packages that are affected by the changes. This aims to enhance the fidelity of dependency networks, offering a potential solution to the challenges of version selection in large-scale ecosystems.
Key Points:
Why Dependency Management is Difficult:
It is due to conflicting requirements, evolving dependency networks, and the impact of time.
Different models offer varying levels of compatibility and stability, and choosing the right approach depends on the specific needs and context of the project or organization.
Compatibility Promises:
Dependency management involves considering ongoing maintenance costs, security vulnerabilities, changing platforms, and evolving dependency networks. Clear compatibility promises from dependency providers help manage the impact of updates and changes over time.
Examples of Compatibility Models:
C++ Standard Library: Demonstrates indefinite backward compatibility, providing API and ABI compatibility across versions.
Go Language: Prioritizes source compatibility between releases but offers no binary compatibility.
Abseil (Google's Project): Allows changes, especially in implementation details and ABI, without promising ABI compatibility. Ensures a limited form of API compatibility and provides automated refactoring tools for breaking changes.
Boost C++ Library: Makes no promises of compatibility between versions, emphasizing an experimental proving ground rather than stability.
Google's Dependency Management:
It is crucial to consider factors such as tests, compatibility promises, popularity, maintenance expectations, and potential upgrade difficulties. Asking relevant questions helps evaluate the suitability of a dependency for long-term use.
Internal dependencies are often prioritized, and third-party dependencies are added to a separate directory labeled third_party in the monorepo.
It is preferred to reuse existing infrastructure rather than building it from scratch to save development costs and promote code quality.
Dependency Management Models:
Nothing Changes (Static Dependency Model): Assumes no changes to dependencies, prioritizing stability but not sustainable in the long term.
Semantic Versioning (SemVer): Common practice involving version numbers with major, minor, and patch components. Ensures compatibility with specified version ranges but may lead to dependency hell.
Problem comes with scale: Overconstraining and overpromising in version constraints, combined with human fallibility, can result in dependency hell.
Hyrum’s Law: “With a sufficient number of users, every observable behavior of your system will be depended upon by someone.”
Alternatives - Minimum Version Selection (MVS): attempt to address some of these challenges, but the debate on SemVer's effectiveness continues.
Bundled Distribution Models: Involves packaging compatible versions of dependencies together for distribution. Distributors play a crucial role in selecting and testing versions.
Live at Head: Google's proposed model where dependencies are always at the latest version. Relies on API providers testing against the entire ecosystem and using tools for smooth updates.
Live at Head Model:
Philosophy: Dependent on always using the current version of dependencies and avoiding changes that make adaptation difficult.
Responsibility Shift: Places the burden on API providers to test against and change downstream customers, requiring strong testing and CI practices.
Incentive Structures: Incentivizes API providers to make changes smoothly migratable and consumers to keep tests working.
Version Selection: Relies on the most recent stable version of dependencies, assuming responsible changes from providers.
Dependency Management with Infinite Resources:
SemVer's Current Reliance:
SemVer relies on local information, but the need for this requirement is questioned.
SemVer doesn't assume the ubiquity of tests, CI systems, or compute resources for running tests.
SemVer is the current industry practice, and alternatives are explored through the lens of infinite compute resources.
Improved Dependency Management:
Visibility and Coordination: With infinite resources, dependency information could be readily available, improving coordination and visibility.
Downstream Usage Knowledge: Even in current scenarios, significant information about downstream usage is theoretically available, indicating SemVer's effective dominance.
Practical Experience over Estimation:
Evidence-Based Changes: Dependency changes accompanied by evidence of safety through extensive testing in downstream dependencies could replace SemVer's estimation.
Statistical Argument for Safety: Running tests for a representative sample of dependencies provides a statistical argument for the safety of proposed changes.
Challenges in Implementing the Model:
Required Changes: Implementing this model would necessitate changes like ensuring all dependencies provide unit tests, understanding the entire OSS dependency network, and availability of compute resources.
OSS Case Studies on Exporting Dependencies:
Considerations:
Community Over Code: Open sourcing involves considerations beyond code quality, emphasizing community engagement.
Long-Term Maintenance: Open sourcing without a plan for long-term support can lead to reputation loss and engineering inefficiency.
Case Study: open sourcing gflags:
Release Challenges: Google's open sourcing of the gflags library faced challenges due to the inability to execute large-scale refactorings and legal issues around accepting external contributions.
Portability Issues: Maintaining portability across different platforms became challenging, leading to the divergence of internal and external versions.
Case Study: AppEngine:
Forced Compatibility: Upgrading the Python runtime alongside C++ dependencies forced compatibility across versions, delaying optimizations and creating challenges for users.
Competing Priorities: Users' reluctance to update due to business constraints led to a delay in transitioning to newer language and compiler versions.
Considerations for Exposing Dependencies:
Costs of Sharing: Exposing dependencies, whether open source or closed source, has long-term risks and can become more expensive to modify over time.
Hyrum's Law Inertia: External users, with different priorities, may exert unforeseen constraints and dependencies on shared code.
Caution in Release Decisions: When deciding to release dependencies, awareness of long-term risks and the potential for increased maintenance costs is crucial.
Book: SE@Google Chapter: 21 Link
Summary:
Managing dependencies in complex API ecosystems is challenging, but the problems are often addressed by semantic versioning (SemVer). While SemVer / Minimum Version Selection (MVS) is effective on a smaller scale, its reliance on predicting change severity without knowledge of how APIs are consumed becomes increasingly problematic as dependency networks grow.
The chapter proposes a change in perspective by advocating for a shift from relying on compatibility estimates provided by maintainers to embracing an evidence-based approach. In this approach, compatibility is determined by running tests on downstream packages that are affected by the changes. This aims to enhance the fidelity of dependency networks, offering a potential solution to the challenges of version selection in large-scale ecosystems.
Key Points:
Why Dependency Management is Difficult:
Compatibility Promises:
Dependency management involves considering ongoing maintenance costs, security vulnerabilities, changing platforms, and evolving dependency networks. Clear compatibility promises from dependency providers help manage the impact of updates and changes over time.
Examples of Compatibility Models:
Google's Dependency Management:
third_party
in the monorepo.Dependency Management Models:
Live at Head Model:
Dependency Management with Infinite Resources:
SemVer's Current Reliance:
Improved Dependency Management:
Practical Experience over Estimation:
Challenges in Implementing the Model:
OSS Case Studies on Exporting Dependencies:
Considerations:
Case Study: open sourcing gflags:
Case Study: AppEngine:
Considerations for Exposing Dependencies: