Working with Git and different projects – repository (part 1)

Brown binder lot

I’m involved into several projects, luckily all of them are using Git as VCS for code storage.

When working in a team, it’s very important to pre-define certain rules that everyone will follow to make everything straightforward and expected. This will allow to simplify general workflow and to decrease cognitive load when switching between tasks/branches/projects.

Before going into branch management in some of my projects, I would like to first cover the repository usage for your projects.

Project as a monorepo with single build tool and common dependencies

For those unfamiliar – that’s when you have several libraries/plugins/addons, that are part of a single product, in the same repository.

Example: you are developing various plugins for WooCommerce. That means your repository has a structure like this (and locally you pull it into wp-content/plugins/ directory:

.github/
.gulp/
node_modules/
plugin1-for-woocommerce/
plugin2-for-woocommerce/
vendor/
woocommerce/
query-monitor/
.gitignore
composer.json
gulpfile.js
package.json

As you can see, all plugins share the same build tool (.gulp directory and associated main gulpfile.js file),
composer and node_modules directories. .lock files should be in the repository as well, I’m just no including them for sanity.

This approach is the most disc space efficient, but requires a very careful and accurate build tool rules and dependencies version management. You will also need to have a unified way to build things (translations, zip files, SCSS->CSS, etc) across all of your plugins inside the repository, and make sure that every one uses certain correct non-conflicting version of dependencies.

If you use Composer autoloading – you will have a terrible headache when you make a distribution zip archive – you don’t want to include in plugin1 dependencies from plugin2. Of course, this is possible, but you might end with writing lots of custom scripts for composer and/or npm.

Project as a monorepo with multiple build tools and independent dependencies

That’s basically the same as above, with a small but very important difference in directory structure and the way build tools (gulp in my example) are used.

.github/
plugin1-for-woocommerce/
├── .gulp/
├── node_modules/
├── vendor/
├── composer.json
├── gulpfile.js
└── package.json
plugin2-for-woocommerce/
├── .gulp/
├── node_modules/
├── vendor/
├── composer.json
├── gulpfile.js
└── package.json
woocommerce/
query-monitor/
.gitignore

Each plugin has its own copy of build tools, composer and node_modules directories.

This approach:

  • simplifies building and managing dependencies for each plugin – as they are independent;
  • is very good, when you have a core plugin, say plugin1, and all others: plugin2, plugin3 etc – depend on it (aka addons). So quite often you need to make a big change in a core plugin, and all “addons” will require a minor change in behavior (like filter usage).

For both of these monorepo approaches:

  • you make only 1 commit when you modify something similar (or the same) for both plugins: adding a footer link in the admin area of a plugin, for example;
  • you can automate running composer install and npm install for each of the plugins by using a Git hook post-checkout – so developer initially runs a single command git clone and everything is set up after its done.

Product with several repositories (projects)

You take each custom pluginN-for-woocommerce directory from above and put it into own repository. That will mean that when you make changes in admin area of your plugins (like the new footer link) – you end up with multiple commits to each repository.

This approach is also more complex at the beginning, because every developer (or when you change your working machine: home/office, Mac/Windows etc) will need to:

  • request access to each of the separate repositories;
  • pull them one by one into specific places;
  • run composer install and npm install for each of them.

So several repositories provide more granular access control (which needs to be managed), complicates initial development, requires lots of similar commits across your various repositories, might duplicate common build steps.

What to select?

There is no an easy answer. You select the road to take depending on your vision and your project.

I think that highly coupled projects might benefit from being in a single repository (aka monorepo), but with own dependencies. You can always make a mix of those 2 monorepo examples:

  • the single build tool (like a top-level .gulp directory and a single gulpfile.js) and a special top level package.json just for building (of course if you are using node, because you can have a Robo too);
  • each plugin has its own composer.json AND even package.json (which just won’t have gulp and everything related to actually building it).

But if all of your plugins are totally separate and do not relate to each other – let them be in separate repositories. In case something is in common, like an admin area handling (basic skeleton) – you can make it a library, release via the Packagist and composer require in each separate plugin.

Featured image by Sear Greyson.

By Slava Abakumov

// Be good, have fun, create things.

Leave a comment

Your email address will not be published. Required fields are marked *