Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Multi-Variance: API Versioning For Software Libraries

Multi-Variance: API Versioning For Software Libraries

One of the key features of Composer - the PHP community's package manager - is that you can't have two different versions of a Composer package in your project at the same time. Your project - and all the packages you depend upon - must agree on which versions to install, otherwise Composer cannot continue. Anyone who has used popular packages like Guzzle or PHPUnit over the last few years will have seen first-hand the problems this can cause.

In this talk, Stuart will introduce you to 'multivariant packages', a technique he uses with his own Composer packages to make sure his packages aren't contributing to the problem. He'll show you how to organise code inside your packages, and how to version your packages to work best with Composer. And finally, he'll look at how to handle devtools that install new Terminal commands inside your project.


Stuart Herbert

August 28, 2018


  1. A presentation by @stuherbert
 for @GanbaroDigital Multi-Variance API Versioning For

    Software Libraries
  2. Industry veteran: architect, engineer, leader, manager, mentor F/OSS contributor since

    1994 Talking and writing about PHP since 2004 Chief Software Archaeologist Building Quality @GanbaroDigital About Stuart
  3. Follow me I do tweet a lot about non-tech stuff

    though :) @stuherbert
  4. @GanbaroDigital ?? ?? Do you create and publish Composer packages?

  5. @GanbaroDigital ?? ?? What versioning scheme do you use?

  6. @GanbaroDigital ?? ?? Have you ever put out a new

    version of your package with substantial b/c breaks?
  7. @GanbaroDigital I'm here to talk about the consequences of b/c

    breaks in packages.
  8. @GanbaroDigital And to share a different approach from my own

  9. @GanbaroDigital This is just my own approach. Other approaches exist.

    I'm here to learn from you too!
  10. @GanbaroDigital In This Talk ... 1. Semantic Versioning 2. Modularity

    3. Consequences 4. Library Packages 5. Dev Tools
  11. @GanbaroDigital Current Practice

  12. @GanbaroDigital Semantic Versioning

  13. @GanbaroDigital semver.org

  14. @GanbaroDigital X.Y.Z versioning scheme

  15. @GanbaroDigital X.Y.Z major.minor.patchlevel

  16. @GanbaroDigital X.Y.Z major.minor.patchlevel

  17. @GanbaroDigital Fix a bug? Increment Z. e.g. 1.1.0 -> 1.1.1

  18. @GanbaroDigital X.Y.Z major.minor.patchlevel

  19. @GanbaroDigital Add a new feature? Increment Y, reset Z. e.g.

    1.1.1 -> 1.2.0
  20. @GanbaroDigital X.Y.Z major.minor.patchlevel

  21. @GanbaroDigital Add a major feature? Or break backwards-compability? Increment X,

    reset Y & Z. e.g. 1.2.0 -> 2.0.0
  22. @GanbaroDigital semver is a modern take on old-skool versioning.

  23. @GanbaroDigital ?? ?? Why semver?

  24. @GanbaroDigital The semver rules are a contract between package maintainers

    and library users.
  25. @GanbaroDigital “Library users should be able to upgrade to the

    latest X.*.* release without changing their code.
  26. @GanbaroDigital e.g. 1.0.0 -> 1.59.46 should be a hands-off upgrade.

  27. @GanbaroDigital That is a very high bar for package maintainers

    to meet.
  28. @GanbaroDigital Why? Because of backwards-compatibility breaks.

  29. @GanbaroDigital ?? ?? What is a backwards-compatibility break?

  30. @GanbaroDigital Changing any existing API.

  31. @GanbaroDigital Changing the behaviour of any existing API.

  32. @GanbaroDigital Fixing a bug.

  33. @GanbaroDigital Adding deprecated warnings to any existing API.

  34. @GanbaroDigital Changing the package's dependencies.

  35. @GanbaroDigital Changing the supported PHP version(s).

  36. @GanbaroDigital Breaking b/c has major consequences for library-users.

  37. @GanbaroDigital That's why the semver rules require package maintainers to

    bump the 'X' version number.
  38. @GanbaroDigital ?? ?? What's the knock-on effect if you change

    your package's major version number?
  39. @GanbaroDigital Hold that thought. We'll answer that a little later

  40. @GanbaroDigital Modularity

  41. @GanbaroDigital Most mainstream programming languages are not modular.

  42. @GanbaroDigital Libraries are loaded into a shared symbol table.

  43. @GanbaroDigital Schemes like namespacing still use the shared symbol table.

  44. @GanbaroDigital Schemes like 'friends' simply add access controls. They don't

    partition the shared symbol table.
  45. @GanbaroDigital ?? ?? Which mainstream programming language is modular?

  46. @GanbaroDigital JavaScript*

  47. @GanbaroDigital ?? ?? The reason your node_modules folders are gigabytes

    in size?
  48. @GanbaroDigital npm* supports installing different versions of the same package

    into the same app.
  49. @GanbaroDigital Composer cannot. Because the language isn't modular.

  50. @GanbaroDigital This is not a PHP problem. Nearly all mainstream

    programming languages share this limitation.
  51. @GanbaroDigital I'm not here to debate modular vs non-modular.

  52. @GanbaroDigital PHP is a non-modular language. It's going to stay

    that way for the foreseeable future.
  53. @GanbaroDigital I want to look at how we work with

    the cards we've been dealt.
  54. @GanbaroDigital Consequences

  55. @GanbaroDigital ?? ?? What's the knock-on effect if you change

    your package's major version number?
  56. @GanbaroDigital If you've used popular Composer packages such as Guzzle,

    or PHPUnit ...
  57. @GanbaroDigital ... you'll have seen errors like this: "Can only

    install one of: guzzlehttp/guzzle[6.1.0, 5.3.0]"
  58. @GanbaroDigital “Two different major versions of the same package cannot

    co-exist inside the same app.
  59. @GanbaroDigital The more popular your package, the more likely it

    is to be pulled into the same app multiple times.
  60. @GanbaroDigital ?? ?? As a library-user, how do you solve

  61. @GanbaroDigital You can't.

  62. @GanbaroDigital ?? ?? As a package maintainer, how can you

    avoid this?
  63. @GanbaroDigital Multi-variance

  64. @GanbaroDigital Library Packages

  65. @GanbaroDigital In any non-modular language, only one version of your

    package can exist inside any one app.
  66. @GanbaroDigital That's our design constraint. We cannot change it. We

    have to work with it.
  67. @GanbaroDigital ?? ?? Do you build web APIs?

  68. @GanbaroDigital If you're building a web API, you support multiple

    versions of your API at the same time.
  69. @GanbaroDigital So why not do the same with library APIs?

  70. @GanbaroDigital There's nothing stopping you from having multiple versions of

    your API inside one package.
  71. @GanbaroDigital my-package

  72. @GanbaroDigital my-package src

  73. @GanbaroDigital my-package src/V1

  74. @GanbaroDigital my-package src/V1 src/V2

  75. @GanbaroDigital "autoload": { "psr-4": { "GanbaroDigital\\TextParser\\V1\\": "src/V1", "GanbaroDigital\\TextParser\\V2\\": "src/V2" }

  76. @GanbaroDigital We can't use semver for our package version numbers.

    But package managers expect semver-like version numbers.
  77. @GanbaroDigital Version numbers change from X.Y.Z to X.datereleaseno

  78. @GanbaroDigital e.g. from 1.2.0 to 1.2018082601

  79. @GanbaroDigital e.g. from 1.2.0 to 1.2018082601

  80. @GanbaroDigital X is the minimum supported API version

  81. @GanbaroDigital Bump X when you drop support for an API

  82. @GanbaroDigital (but be prepared for all the consequences I've already

  83. @GanbaroDigital the datereleaseno is the year, month, day + how

    many releases you've made that day
  84. @GanbaroDigital e.g. 1.2018082601 1.2018082602 1.2018082801

  85. @GanbaroDigital e.g. 1.2018082601 1.2018082602 1.2018082801

  86. @GanbaroDigital e.g. 1.2018082601 1.2018082602 1.2018082801

  87. @GanbaroDigital ?? ?? Why not X.date.releaseno? e.g. 1.20180826.01?

  88. @GanbaroDigital You want the dependency rules to always pull in

    new releases of X.*.
  89. @GanbaroDigital e.g. 1.2018082601 -> 1.2018082602 1.2018082602 -> 1.2018082801

  90. @GanbaroDigital With X.date.release, there's a risk of users adding dependency

    rules that lock them into X.date.* releases!
  91. @GanbaroDigital e.g. 1.20180826.01 would they get 1.20180828.01?

  92. @GanbaroDigital This approach is what I call multi-variant packages: multiple

    API versions inside the same package.
  93. @GanbaroDigital ?? ?? Does anyone remember how Guzzle solved this?

  94. @GanbaroDigital Dev Tools

  95. @GanbaroDigital Here, we're talking about tools like PHPUnit. Something you

    install that provides new CLI commands.
  96. @GanbaroDigital ?? ?? What problems do these CLI tools cause

    w.r.t. package version clashes?
  97. @GanbaroDigital They pull in dependencies of their own, which can

    clash with your app's dependencies. (Or with the dependencies' dependencies!)
  98. @GanbaroDigital ?? ?? How would you solve that?

  99. @GanbaroDigital Package maintainers: Ship these tools as PHAR files.

  100. @GanbaroDigital End-users: download and use the PHAR files.

  101. @GanbaroDigital

  102. Thank You How Can We Help You? A presentation by

 for @GanbaroDigital