How to store source code
At GDS, we follow the principles set out in the Service Manual for managing the code we write by:
Publish open source code
Wherever possible, we make our source code open and reusable. This means other government departments and people in outside organisations can benefit from our work. We also maintain several open source projects developed for use on GOV.UK and with other work we do, such as GOV.UK Frontend.
It’s not always appropriate to open code. There are sometimes grounds for keeping some data and code closed, for example:
- keys and credentials
- algorithms used to detect fraud
- code or data that makes clear details of unannounced policy
The Service Manual explains how to open previously closed code and your responsibilities for maintaining open code.
When you publish open source code, your project must:
- include a README, using guidance for writing a README
- have useful and informative commit messages about why a change was made
- provide a changelog, for example the specification for CPAN Changes files
- include an MIT and OGL licence file
- link to a public list of known issues and bugs, for example GOV.UK Frontend issues
- have an email address to submit security related bug reports
- list a version number compatible with Semantic Versioning
Your open source code project should:
- publish packages to relevant language specific repositories such as PyPI - the Python Package Index or RubyGems
- post contributors’ guidelines in a contributing file, like the Go repository
- set up any tests to run in a public continuous integration environment using tools such as Github Actions, Travis CI, CircleCI or Jenkins
You could also provide a mailing list so people can discuss your project.
Put new repositories for GDS services in the alphagov organisation on GitHub.
You can use your personal GitHub account to access alphagov but please ensure that you also link your GDS email address to your account. Ask your tech lead or technical architect to invite you. GDS will revoke your access to alphagov when you leave GDS.
To secure your Github repository, make sure you:
- configure two-factor authentication for your account
- have considered encrypting your repository contents
- consider protecting your main branch to prevent changes being committed without a suitable review
You can also consider backing up your Git repositories to another location (this should be a team responsibility). If you are using AWS to host your service AWS CodeCommit is one option.
How to retire applications
If an application is no longer used in production, you should archive its repository.
Update the application’s README to explain why the repository has been archived, and link to a new location if the application has been superseded.
Using Github Actions and workflows
Alphagov repositories can be configured to use GitHub Actions for CI/CD jobs, for example running unit tests or deploying a static site.
Actions and workflows can be powerful, so take care to follow best security practice.
Ensure the repository permissions follow the principle of least privilege. Set workflow permissions to read-only by default, and disable all actions on repositories that are not using them (this will hide the ‘Actions’ tab from the repository menu).
If your repository has external contributors, ensure they do not have permissions to add workflows or trigger workflow runs.
If your workflow interacts with another resource (for example AWS or DockerHub), consider setting up a dedicated account or role, with permissions limited to the scope of the action.
- The provider is verified by GitHub (for example, aws-actions)
- The action is complex enough that you cannot write your own local action
- You have fully reviewed the code in the version of the third-party action you will be using
- You have pinned the specific version in your workflow and in the repository settings, using a Git commit SHA
- The third-party action is actively maintained, well-documented and tested (follow the guidance on third party dependencies).
Note that for public repositories, the output of workflow runs is visible to everyone. Do not use workflows if this output could be considered sensitive.
Consider creating a Workflow Template in the alphagov workflow folder if you need to share a similar workflow between many repositories.
Working with Git
Default branch name
Your repository’s default branch should be called
To configure git to use
main for new repositories, first make sure you are using git 2.28 or later, then run:
git config --global init.defaultBranch main
To migrate existing repositories from
main, you can use
GitHub’s branch renaming
Before you rename your default branch, you should consider the impact this will have on:
- internal users
- external users
- your continuous integration (CI) system (for example, Concourse pipelines or GitHub actions)
You can configure some CI systems to check out the default branch rather than hardcoding a branch name:
- GitHub Actions supports a
- concourse-git-resource: if you do not set
branch, it will check out the default branch
Writing good commit messages is important. Not just for yourself, but for other developers on your project. This includes:
- new (or recently absent) developers who want to get up to speed on progress
- interested external parties who want to follow progress of the project
- people in the public (remember, we code in the open) who want to see our work, or learn from our practices
- any future developers (including yourself) who want to see why a change was made
Capturing context around a change allows people to understand why a particular implementation decision was made, much like an architecture decision record. We’re being kinder to our future selves.
Recommended blog posts on this topic
- How to Write a Git Commit Message
- 5 useful tips for a better commit message
- Every line of code is always documented
A good commit message briefly summarises the “what” for scanning purposes, but also includes the “why”. If the “what” in the message is not enough, the diff is there as a fallback. This is not true for the “why” of a change - this can be much harder or impossible to reconstruct, but is often of great significance.
Set cache headers
Set cache headers IE 6 was doing foo, so we need to do X. See http://example.com/why-is-this-broken for more details.
Links to issue trackers
A link to a ticket in an issue tracker should not be seen as an alternative to writing a commit message.
While a link can add some extra context for people reviewing a pull-request, the commit message should stand on its own. There’s no guarantee that the link will continue to work in the future when someone is looking through the commit history to understand why a change was made.
If you are adding a link to a publicly viewable repository, ensure that the linked ticket is publicly viewable (and likely to remain so).
Commit messages should start with a one-line summary no longer than 50 characters. Various Git tools (including GitHub) use this as the commit summary, so you should format it like an email subject, with a leading capital and no full stop. The Git convention is to write these in the present tense. For example:
Leverage best-of-breed synergies going forward
You should leave a blank line before the rest of the commit message, which you should wrap at around 72 characters: this makes it easier to view commit messages in a terminal.
Taken from Tim Pope’s guidelines.
Capitalized, short (50 chars or less) summary
More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of an email and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); tools like rebase can get confused if you run the two together.
Write your commit message in the present tense: “Fix bug” and not “Fixed bug.” This convention matches up with commit messages generated by commands like git merge and git revert.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here
- Use a hanging indent
You may often choose to work on a particular feature on a “feature branch”
rather than directly on
main. Indeed, given how cheap branches are in Git,
using branches is positively encouraged.
You are encouraged to make liberal use of Git’s history rewriting
features while working
locally, in order to arrange your commits into appropriate logical chunks that
will make sense to your fellow developers. In particular, you may find
git rebase --interactive very useful. You are also encouraged to avoid merge
commits and use
git rebase main instead. However, you should not rewrite commits
that have been pushed unless you:
- are very sure that no-one else will be affected by you rewriting the branch history
- have an extremely good reason. For example: someone has committed sensitive information (personally identifiable data, passwords and suchlike) and it needs purging from history
When in doubt you should err towards smaller commits, which can be rebased together later. It’s harder to break large commits out into smaller chunks.
The smaller commits should still be logical chunks, but this will give context
for a more specific change and make git tools like
When merging from a feature branch to main (or any other mainline development
branch), in particular one that has previously been shared with colleagues, you
--no-ff option to preserve evidence of your feature
branch in the repository history. This advice may be freely ignored for smaller
local feature branches for which a fast-forward merge will look like any other
routine development work on
Do not use
git push -f, use
Force pushing in git is a subject that attracts all kinds of religious
battles. This advice is not about the merits of force pushing. This is about how
git push -f if and when you do use it.
Let’s say you’re working on a branch, 'foobar’, and you decide to force push to the remote. So you do this:
$ git push -f
If anyone else has committed anything to your branch since you last pulled, you will blow their changes away.
So for a bit of safety, if you ever need to force push please instead do:
$ git push --force-with-lease
--force-with-lease refuses to update a
it is the state that we expect, which is that nobody has updated the remote branch.