Checklist for starting a project as a team

It's that time again when you are starting a new project, perhaps leading a team or being a part of the founding team. Earlier in your experience as a software engineer, you would dive into code and start building, building up standards and patterns as you go. But now, you are better, wiser, stronger, more vicious, and with a greater sense of that paranoia and pragmatism that comes with experience and shark fights.

Now, you know that you dare not start without a standard or a checklist for this project, or else things will go to hell. You have searched highway and byway and you want something opinionated, justifiable, and easy to replicate as the years go by.

I have put together a checklist I use when working in a team. To be honest, this checklist has a collective experience of 2 years and 5 projects, so you can trust it. It has also been vetted by software engineers of > 5 years of experience.

Checklist

This is an exhaustive checklist and I will update things and add justifications when I remember to do so. In the meantime, please work with this.

Tooling

First, great team work is facilitated by having the right tools: collaboration tools, task management and dev tooling.

I highly recommend the following:

  • Code collaboration: GitHub

  • Note-taking and specs writing: Google Docs. It's just too simple to use!

  • Project management: Linear. Again, simplicity meets great features.

All of these must find some link into the code collaboration tool - GitHub. Linear provides integration for GitHub.

Code review

For code review, 2 people for passes is good enough. Let reviews be constructive and not contain phrases like, "My 2-year-old brother would cringe at this" or "Your code made me regret not being a carpenter, you drill too many props!".

Non-trivial comments can be prefixed with "nit" as in "nit: you should add a log here" or "nit: Lambda functions generally shouldn't have to be named, but I understand why you needed that in a function".

Ensure every team member writes code to prevent godly see-finish.

I highly recommend 10 commandments for navigating code reviews.

Branch existence

Prayer: God help me explain this without having to drop these diagrams usually used by articles on Git 🥹

TL; DR

Irregular release schedule:

feature-branches -> dev -> staging -> main

Regular release schedule:

feature-branches -> dev -> staging -> release-branches -> main

The following branches must exist:

  • dev: This should be the default branch set for the repository and all PRs should point to it. There are exceptions I'll explain below. The development branch should have its deployment environment set up.

  • staging: When features are ready to be tested by the team or the QA team, the dev branch is periodically merged into the staging branch. The staging branch should have its deployment environment set up.

  • main: This branch contains the code deployed and faces the end users. Code from staging branch is usually merged into this branch when it is time to release a feature to the end users.

Other branches

  • Feature branches are branches created by developers when working on features or making general code changes.

  • Hotfix branches are generally pointed to the main branches and are intended to fix bugs in production that require urgent attention. Bugs that are in staging can have their hotfix branch pointed to staging. They should have the format hotfix/<ticket_id>.

  • release branches existence depends on the product development methodology. In cases where features are released to users in batches and at the end of sprints, release branches are used. When release branches exist, staging no longer point to main but resolves to a branch, release-<version_number> where the version number is auto-incremented. When staging is good to go by the QA team, a new branch is created out of it for that release and this branch is usually called the beta branch. Note that main still serves end users, hence the use of the term, "beta". Beta is deployed and users who opt for beta features get to use this to play around. When this branch is stable, it gets merged to the main branch and everyone is happy.

God answered my prayer, haters!

Commits

Commit convention should follow semantic commit guidelines which I'll break down in brevity:

A great format is <branch_name or ticket_id> | <commit_type>: <commit_message>. The advantage of this is that the commit messages when viewing logs gives you an idea of:

  • The branch this commit was made on

  • The type of change introduced

  • The actual change introduced

in one short glance. A good commit message looks like so:

MMM-419 | Chore: Added tests for login feature

Commit type is one of:

  • Feature: For new features, breaking changes

  • Fix: Bug fixes

  • Refactor: Like replacing needless abstract classes with interfaces

  • Chore: For simple changes that don't impact features. Such as formatting a file, installing a dependency, and renaming variables. Use this if you don't know where to categorize your change.

  • Buggy: This should be rarely used, but in cases where a commit deliberately has bugs. This happens when you desperately need to save your working state

  • Doc: Addition to documentation.

Commits should be granular as much as possible. Avoid committing 25+ files as a single commit.

Naming conventions

Naming must be predictable and consistent. It is like the battle plan that everyone knows.

Branch names

Branch names should be directly tied to the task. You can use <ticket_id> which is usually made up of the project three letter code and the ticket number such as, "MMM-419". For additional information, it could be prefixed with the type of ticket: "feature/MMM-419". Types include feature, bug, hotfix, and improv. Personally, I love the format: <first_name>/<ticket_id> like so: lordsarcastic/MMM-419. This gives at first glance who "owns" that ticket without having to consult the project management tool.

PR/MR names

PR titles should use the format: <ticket_id> | <ticket title>. This is a sweet spot between project management collaboration tool integration and knowing what a PR does.

Note: I'll use "PR" moving forward

PR specifications

PR specification

I don't have much to say here since I don't enforce it. I think PRs should only contain explanations as to why a certain decision was taken when it can't be a comment in the code. PR description is already in the ticket description. If for frontend, should contain screenshots of features added. Breaking or deprecation changes should also be noted in the PR description.

Merging the PR

When a PR is merged, the branch should be deleted, this way you don't have stale branches. Only major branches: dev, staging and main are never deleted.

You can try deleting them and lose your job.

Merge strategy

Merge strategies come in handy for certain teams when the codebase becomes large. At this point, you want to choose the "Squash" option when merging. This compresses all commits in the PR into a single commit. This makes rollbacks easy when a branch introduces some serious bugs that cannot be resolved peacefully but must be rolled back.

Keeping track of history and merge conflicts

The author of a branch is responsible for keeping the branch up to date and resolving conflicts.

git rebase should be avoided at all costs to resolve merge conflicts or to update the state of a branch. git merge origin/<parent_branch should be used instead. Else when branches diverge, it will be a nightmare ensuring everyone is in sync. Rebase simply ensures a single, linear commit history regardless of what everyone is working on and this means commits across several branches can be erased in favour of what the branch author prefers. I don't see any advantage of such in a large codebase. Rebase is only good when working alone.

I recommend this section of Git Rebase vs Merge: A complete guide to see why you don't want to rebase in a large codebase.

RFCs

Also called "Tech specification" (hello Matchday).

For giant features with questionable changes to the codebase, it is good that someone write a tech spec concerning the feature which documents the technical side of the feature before it is worked on. It serves as a reference document when writing code. It will contain schema propositions, contracts between two communicating systems, and so on.

I highly recommend The Art of Writing RFCs to understand this in detail. It helps you make the decision on when you need RFCs and when you don't.

CI

Ensure you've got GH actions going. Ideally, it should do the following:

  • Check that the code is formatted properly

  • Check that the code is linted

  • Check that all tests pass

CD

It is good to set up preview URLs for branches so that each branch has a live URL that can be used to test it out before being merged. Vercel has this feature baked in for the frontend folks. Heroku also has the same feature. If you're using something more custom, you'll have to set this up yourself.

Makefile

Please use Makefiles for repeated commands in the application. Makefiles help shorten long commands into short ones you can remember. All you have to do is create a file in the root:

command:
    @long command

The "@" symbol ensures that the command text is not spilled on the terminal when you call the command with make <command>. Refer to the Makefile section of an article of mine to see use cases.

Conclusion

This is a highly opinionated checklist I have used over time. Feel free to reuse and customize to meet your team needs. Ensure to recommend this article to your friends and teammates at work and drop a comment if you think there is something that can be improved on.

Eat well, sleep well, rest well and join me on DevMeets on Sunday!