Shipyard’s role in the software development lifecycle is squarely in the “post-commit” portion. In other words, our work starts the second you push a commit.
What happens “pre-commit” matters, though. That’s the space in which the engineer does their work. So having local workflows line up all of your integrations is very worthwhile, but can be a pain to set up. That’s why we’ve released starter repos as starting points for developers making new apps.
Today, we’ll focus on one of the key pieces of the starter repo: the Makefile.
Why Makefiles?
Languages usually have their own task runners, often embedded in the language’s package manager (e.g. npm
for node, poetry
for Python). If you use those programs to run the development workflows, though, that means you’ll need to learn a new tool for each language with which you interact.
Using Makefiles as an abstraction above any language-specific tooling allows for easy upgrading of that tooling, without having to teach your team how to use a new CLI. This way, they can just keep running the same Make commands they’re used to.
A more subtle benefit is that Makefiles serve as documentation for the workflows and actions needed for any project.
Finally, Makefiles have been in use since 1976, so they’re as battle-tested as can be.
Makefile example
Let’s take a look at the actual Makefile for our repo, section by section.
1. Main development workflow
develop: clean build migrations.upgrade run
clean:
docker compose rm -vf
build:
docker compose build
run:
docker compose up
These are the core functions that developers will be running over and over:
make clean
to stop any running servicemake build
to re-build your images with updated codemake run
to run the containers
We can combine each of those actions to form the main command that developers will run over and over:
make develop
which runsclean
,build
, andrun
(and upgrades migrations)
2. Shell commands
frontend-shell:
docker compose run frontend \
sh
backend-shell:
docker compose run worker \
sh
python-shell:
docker compose run worker \
poetry run flask shell
- These are convenience methods to get each of the shells you’ll need for this project:
make frontend-shell
gets you a Bash shell in the node containermake backend-shell
gets you a Bash shell in the Python containermake python-shell
gets you a Python shell in the Python container
3. Database commands
postgres.data.delete: clean
docker volume rm $(VOLUME)_postgres
postgres.start:
docker compose up -d postgres
docker compose exec postgres \
sh -c 'while ! nc -z postgres 5432; do sleep 0.1; done'
- Each of these deals with the database:
make postgres.data.delete
to stop containers (notice thestop
to the right of the colon) and clears datamake postgres.start
to start the database in the background, and wait for it to start up
4. Migrations
migrations.blank: postgres.start
docker compose run worker \
poetry run flask db revision
migrations.create: postgres.start
docker compose run worker \
poetry run flask db migrate
migrations.upgrade: postgres.start
docker compose run worker \
poetry run flask db upgrade
migrations.heads: postgres.start
docker compose run worker \
poetry run flask db heads
- Finally, each of these handles the usual migration tasks that a developer needs to do (note that each command starts postgres in the background with
postgres.start
):make migrations.blank
creates a blank Alembic migrationmake migrations.create
generates a migration off of any detected schema changesmake migrations.upgrade
applies any unapplied migrationsmake migrations.heads
shows the latest the latest migration (and any forks in the migration path)
The oldies are great
Makefiles may have been invented over 40 years ago, but that doesn’t make them any less fit for the job. Using Make as the interface for project workflows allows for ease of knowledge sharing, quick onboarding of new developers, and straightforward refactoring use of other CLI tools.
And if you containerize your projects, onboarding engineers into a new project in any language just becomes:
- Securely sharing credentials
- Installing Make and Docker
- Running
make develop
So grab one of the starter repos and starting building that new app you’ve been thinking about. And of course, the starter projects are all Shipyard-ready, so start your trial and deploy your project as an ephemeral environment today!