Sharing features among our portfolio companies (ventures) is one of the major economic benefits of Yves & Zed. However, its practical realization can sometimes be tricky, and we had to find clever ways to handle packages, their dependencies, and versioning.
We share simple features including catalogs, CMS, discounts, and we also share advanced features including order processing and data warehousing. Whenever we feel a feature could contribute to our portfolio, and is required by at least two ventures, we consider to implement it in the core.
As you can imagine, a decision like this must be taken with care, and on a solid experiential basis with a clear vision of future projects in mind. However, the major challenge lies in the efficient, trouble-free management of shared code. So we think it’s worth taking a look at how we do it.
Separated Code (Levels)
As you can read in our post on venture internationalization, we use factories to switch from general to country specific classes. We apply the same concept to switch from core to project code, and to make project-specific extensions. As a result, our code is divided into three levels; core, project and store that can extend one another. The following image shows different scenarios of class extensions:
Experience has shown that it’s useless to force a complete systems update for startups against their will. This is because they often request updates for certain features instead of the whole system. The latter is expensive, inefficient and leaves little room for their individual development. To avoid that, they tend to skip some releases and sooner or later end up with unstable systems. That is why we have split Yves & Zed into functional units called packages, e.g. “auth-package“,“catalog-package“ or “dwh-package”. Each has a version number, e.g. ‘3.2.6’ which is updated after every major, minor or patch-release.
Yves & Zed consists of 29 packages at the moment, some of them mandatory to implement the system (e.g. acl), others are additional features (e.g. marketing) or features required only for certain shop types (e.g. catalog). Although most of our packages are loosely coupled, dependencies between certain packages are inevitable. To manage them properly, we have to look at the characteristics of dependencies from various angles:
1. Severity of dependencies:
- a package requires another package (e.g. payment-package cannot work without sales-package)
- a package can work with another package (e.g. dwh-package can work without sales-package; but in case there is a sales package, it calculates sales reports)
2. Modality of dependencies:
- a class interacts with a class from another package
- an interface from another package is implemented
- there is a relation in the database schema
- only certain package versions are compatible with one another
Example: If a venture wants to update the catalog-package to version 3.1.2, they also must update the infrastructure-package to version 1.0.0.
To sum up: Yves & Zed consists of a number of versioned, often interdependent packages (functional units), some of them optional, some of them mandatory to get the system up and running.
Ways to Avoid the Dependency Hell
Besides keeping the amount of dependencies as low possible, we strive to make sure our staff and our ventures can keep track of existing dependencies. For this purpose, we have taken quite a few measures.
Package-separation. Each package has an own Git-repository (generated with Satis) and is equipped with its own user manual. Whenever we update a package, we adapt the corresponding manual chapter and prepare a changelog-email, which is then distributed to our IT department and our ventures.
Package-visibility. We lodge all relevant information on a simple webpage on top of our repository. This page is accessible to our staff as well as our ventures and lists all packages and package versions in one table for each package, like the one below:
One click on the link to the according Graphviz chart under ‘Requirements’ will create and display a graph like the one below for each package version:
Our dependency graphs are created on the basis of static code analysis which we use to detect dependencies.
Package- maintenance. We use Composer to safeguard the smooth implementation and updating of packages. On deployment we run php composer.phar install to install requested packages and their external dependencies (including all frameworks) into the vendor directory. Each of our ventures maintains their own composer.json file to pull the package-updates of their choice with little effort. Moreover, every package ships with its own Composer.json file which displays its dependencies as shown in the sample file below:
"description": "Access control list and authentification",
There are many tools, approaches and opinions around on how to tackle the challenge of dependency management. Among a wealth of opportunities it can be difficult to figure out the right method, and we hope this post has provided some inspiration, especially for handling larger projects and complex software in a highly dynamic environment. The following readings might help you to dig a little deeper:
http://semver.org/ (some good reasons and instruction for semantic versioning)
https://getcomposer.org/doc/00-intro.md (manual for dependency management tool Composer)
Copyright 2014 Project A Ventures | All code in this post is licensed under the MIT License unless otherwise declared.