Recently, we introduced the Scripts To Rule Them All (STRTA) pattern into our development workflow for creating new projects. STRTA handles the heavy lifting when running common setup and build tasks enabling our development teams to work more efficiently across our entire portfolio.
Historically, our projects had no standardised processes in place for bootstrapping a local environment. Each project had a bespoke combination of installed software and package versions. Instead of being able to jump straight into building out new features or bug fixing we had to first manually instantiate a local environment; a frustrating and time consuming task.
We decided to streamline this process as the old implementation, put simply, would not scale.
We rationalised that our developers should be able to get to work without having to worry about the underlying technologies and package versions required to install a project. Equally, they shouldn't have to explicitly document these components for future developers who need to work on the project. This should all be handled by a self-documented bootstrapping experience, consistent across all projects.
We researched the best ways to solve this problem and settled on leveraging “Scripts to rule them all”, a normalised scripting pattern used by GitHub. They have written a blog post describing the idea behind these scripts, so I won't reproduce all of that here.
Each script is responsible for a single unit of work. The end product of each script is consistent across all projects and they do not require intimate knowledge of the application to use.
Fundamentally, our scripts handle responsibility for:
- Bootstrapping an application
- Running tests
- Starting the application / server
For an overview of how scripts are used by GitHub, check out the official repository's README.
Our use cases for implementing these scripts don’t completely align with GitHub's. The main differences are:
- We use Docker and docker-compose in our local environments rather than installing to the host machine
- Because we use Docker with homogeneous configurations we need to clean up old resources when swapping to a different project
An initialisation script intended to be performed the first time a project is cloned, configures environment and performs one-time runs.
Checks that the user running the script has host level dependencies installed (Docker, docker-compose).
Brings up an environment that you built previously (via init).
Builds underlying images before starting containers, useful for installing OS packages e.g. zip, git, bcmath without forcing a complete rebuild.
Performs package manager tasks that are application specific e.g. composer install, yarn install.
Serves our application, generally this means standing up a web server and exposing a port that we can connect to e.g. 0.0.0.0:8000.
Runs front end tasks to detect changes within our assets directory e.g. yarn run watch.
Can perform a single test or an entire suite, usually runs PHPUnit.
Handles cleanup in preparation for switching projects.
As we had already started to use Docker and docker-compose, it was not too difficult to integrate it with this new pattern. We believe that the benefits of STRTA are amplified when used in conjunction with docker-compose because it means our team don't have to remember the longer docker-compose commands or which container to run a command against, the scripts take care of it all.
You can view more working examples of our other scripts in the laravel/baseplate project on our GitHub account.
By implementing “Scripts to rule them all” we have ensured that our development procedures are language agnostic and easily repeatable. We know with confidence that `scripts/serve` will always stand up our application, regardless of whether that is a standalone MySQL container or a cluster of REDIS nodes.
These changes have not only simplified starting new projects, they have integrated well with existing projects. We are able to onboard team members faster without having to teach the specific installation, serve, and test commands of a particular project or language.
Rob is responsible for delivering cutting edge new projects as well as continuously improving some of our longer-lived applications. With an interest in infrastructure as code and being at the forefront of deployment and technical processes he can usually be found telling people that there "is a Docker container for that". In his spare time he loves nothing more than a game of cricket and a night in the pub after.