The search for the perfect setup script

How to onboard your developers as smoothly as possible.

Glynn Forrest
Monday, August 31, 2020

We’ve all been there. You open up a project for the first time, or a project you haven’t used in a while:

Wait, how do I run this again?

If you’re lucky, the project will have a README, or your company will keep a wiki. But what if the instructions are out of date?

Enter the setup script. The approach is simple - a script that automates setting up the project on your machine for development. It could install packages, create docker containers, start development servers, etc:

$ ./bin/dev.sh

Installing packages...

Starting docker containers...

Starting development server...

Ready! Go to http://localhost:8000 to get started.

An easy setup like this can make a huge difference to your team’s productivity, especially if your project is complicated or you switch projects frequently.

Like anything in software, this process can always be optimised for maximum benefit. Here are some tips and tricks I’ve learned over the years.

Use a common pattern for all projects

There are many ways to write a setup script, and many ways to run it, for example:

  • ./bin/dev.sh
  • ./bin/setup (made popular by thoughtbot)
  • npm run dev, composer run-script dev (language-specific)

It doesn’t really matter, but what does matter is having a common interface for your developers. If, like us, your team works on many different projects, having a single command to run for every project keeps things easy.

We always turn to Makefiles for this. Create a dev make target that wraps whatever setup procedure you need:

.PHONY: dev
dev:
	./bin/dev.sh
.PHONY: dev
dev:
	npm install
	run dev

It doesn’t matter how your setup script works, the only thing developers need to know is make dev.

Break the script into discrete sections

A single script that sets up everything is great, until it breaks! Splitting the script into sections makes the whole setup resilient to failure, even if a part of it is broken.

.PHONY: dev
dev: packages docker-start server

.PHONY: packages
packages:
	pip install -r requirements.txt
	npm install

.PHONY: docker-start
docker-start:
    docker-compose up -d

.PHONY: docker-stop
docker-stop:
    docker-compose stop

.PHONY: server
server:
	./bin/server.py

It also makes the workflow more flexible for developers. With these new targets, the developer can choose to just reset the database while keeping the server running:

make dev

# Oh, I need to reset the database!
make docker-stop docker-start

Make targets are also discoverable. make [TAB] will often work out of the box on MacOS and Linux. For bonus points, add a help target at the top of the file:

.PHONY: help
help:
	@echo 'Usage: make [target]'
	@echo 'Available targets:'
	@echo
	@grep -Eo '^[-a-z]+' Makefile | sort

This will list all the targets in the Makefile when a developer types make. Make sure the regex used for grep -E matches your target names - here we’re using kebab-case.

Guide them through the manual steps

Sometimes you can’t automate absolutely everything - there may be manual steps involved, like adding keys to an external system, grabbing OAuth credentials, etc.

Dan Slimmon suggested an approach in Do-nothing scripting that elegantly solves the problem - simply print the manual instructions to the terminal and wait for the user to confirm.

Dan uses Python for his setup script. Here’s a simple bash implementation:

#!/bin/bash

set -eu

wait() {
    echo
    echo $1
    read -p "Press [enter] when ready. "
}

make packages docker-start

wait "Make sure you have an account at https://example.com."

wait "Go to https://example.com/oauth and create a new set of client credentials, then add them to '.env'."

make server

echo
echo "Ready! Go to http://localhost:8000 to get started."
echo

The beauty of this approach is the ability to combine automated and manual steps in one process. Over time, these manual steps can be automated, but the overall workflow will never change.

Compared to a README or wiki, a guided setup script has the advantage of always being up to date. When used every day, a developer will immediately notice if a manual setup instruction is wrong or outdated.

Making it friendly

Thoughtbot have some excellent tips in Shell Script Suggestions for Speedy Setups to make your setup script as friendly as possible. Top tips include:

  • Highlight things with colours, line breaks, and emojis 🎉
  • Have the script check that dependencies are installed, such as python or docker.
  • Follow the rule of silence. Use @ to apply this to your make targets:
.PHONY: packages
packages:
# Use '@' before a command to hide it from the output
	@pip install -r requirements.txt
	@npm install

Do you have any setup tips to share? Let us know on twitter!

More from the blog

Webpack hot module replacement in server-rendered apps cover image

Webpack hot module replacement in server-rendered apps

Get the benefits of webpack-dev-server without building an SPA.


Glynn Forrest
Friday, July 31, 2020

Logged out ajax requests in Symfony applications cover image

Logged out ajax requests in Symfony applications

Handling logged out ajax requests properly using Symfony’s security features.


Glynn Forrest
Monday, June 29, 2020

Configuring Prometheus targets with Consul cover image

Configuring Prometheus targets with Consul

How to configure dynamic Prometheus targets using Consul.


Glynn Forrest
Sunday, May 31, 2020

Subscribe to our mailing list

Receive periodic updates about our products, services, and articles.

View recent emails