No matter what web-based thing I'm building, there are some bits of code I've come to rely on. It started with css reset (am I alone in never having designed a website with an eight-pixel margin on the page and a forty-pixel margin to the left of all bulleted lists?). Then, once I was pleased with a few Sass mixins I had written, I found myself copy-pasting them at the start of every new project. But since I was improving and fixing them all the time, I'd have to make sure I was copying from the most recent project. Going back to work on older projects meant copying back all the newer changes, and praying it all held together. As one might imagine, it often didn't.
The only reason I got away with this was that I was a one-man-team. I can't imagine inflicting this anti-system on a fellow developer.
"There must be a better way!", I thought. "I need a way to place the same code in every project, and synchronize changes everywhere". Fast forward some trial and lots of errors, and here's the solution I've landed on:
Among its many hard-to-discover tools, git supports something called submodules — essentially allowing us to use one repo (for our purposes, let's call it the toolbox) inside another (say, our project) and track it independently. You can read all about it in the docs, if you have the time, but it basically goes like this:
Have a repo you want to add into your project.
git submodule initin your project folder to tell git you're going to track submodules. This creates a
.gitmodulesfile, that'll track what submodules you're using.
git submodule add <repo origin url>in your project folder, and watch the magic happen.
The magic, in this case, is git cloning the toolbox's repo into a subfolder of your project. By default it'll use the repo's name, but you can change that by specifying the desired path at the end of the
Now the project has access to everything in the submodule. The project files and submodule files live side-by-side, so you can easily make changes without needing to change contexts. These changes will be tracked by git separately, so the project repo will only know if any changes were made to the toolbox repo — it doesn't care what they are. Conversely, you can add the toolbox repo to any number of projects, and it won't know anything about them.
Once you've made changes to the toolbox, you can commit and push them to the toolbox's origin by
cding into the subfolder and running the usual git commands. Once you've pushed changes to the toolboxou can run
git submodule update --remote --merge in the root folder of any other project that uses it — that'll pull in changes from the remote origin and sync everything up.
The final technicality I should mention happens when cloning projects that already have submodules added to them. You'll need to add the
--recurse-submodules flag to the
git clone command, to make sure the submodule is cloned in together with the project.
For example, I've found that most websites I build end up having a sticky header that changes style once the user has scrolled a certain amount. It's a common pattern, and the kind of code you can easily find yourself writing again and again. Instead, I've written a generic
headerClass function that sets it all up, targeting elements with data attributes. To make it versatile, the function uses parameters with defaults with an optional offset and data attribute overrides.
Now, when I build a new kind of functionality on a site and I think I might need it in the future, I'll write it as part of my toolbox right from the start. And later, when the next project inevitably requires expanding it or add customization options, I know I'll be able to safely pull in these changes into older projects as well.
Another great use for a toolbox like this is Sass functions, variables and mixins. For example, I can't count how many times I've used my simple
=flex-center($direction: column) display: flex flex-direction: $direction justify-content: center align-items: center
Sure, I could write those four lines each time. But it's just the kind of thing I want to solve once and, if possible, never think about again.
As with all things software, the one major thing to be aware of is the potential damage of breaking changes. It can be a bit of a footgun for your project code to depend on toolbox code that can change on pull. If you can avoid breaking changes by writing smart, resilient and backwards-compatible toolbox code, go ahead. For us humans, though, a measure of control can be achieved by checking out a specific branch, tag or commit of the toolbox. Remember — inside the toolbox's subfolder, you're essentially in a separate repo.