last updated: 5.11.25
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 4 years and several projects, so you can trust it. It has also been vetted by software engineers with> 5 years of experience.
Tools TL;DR
Below is the list of the tools I recommend
GitHub: Version control and code collaboration
Whimsical: Docs and RFCs
Coolify: Self-host infra
GitHub actions: CI
Port: Internal Developer Portal
Linear: Project management
Snyk: Vulnerabilities
NewRelic: Monitoring and analytics
Docker and Docker Compose: Container orchestration
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: I recommend Whimsical, it is simple enough to keep you focused on docs rather than on fancy features and page organization. Neat balance between features and simplicity; great free plan too. Notion is a great choice if you’re already used to it. Honorable mentions: Google Docs, GitHub wiki.
Project management: Linear. Again, simplicity meets great features. Jira has an unnecessary learning curve.
The project management tooling should have an integration with the collaboration and vcs tooling- 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, thedevbranch is periodically merged into thestagingbranch. The staging branch should have its deployment environment set up.main: This branch contains the code deployed and faces the end users. Code fromstagingbranch 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
mainbranches and are intended to fix bugs in production that require urgent attention. Bugs that are instagingcan have their hotfix branch pointed to staging. They should have the formathotfix/<ticket_id>.releasebranches 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,stagingno longer point tomainbut resolves to a branch,release-<version_number>where the version number is auto-incremented. Whenstagingis 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 thatmainstill 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 themainbranch 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, give you an idea of:
The branch this commit was made on
The type of change introduced
The actual change introduced
In addition, when the team get bigger and responsibilities fan out into multiple domains, you might want to start using an internal developers portal like Port. Having such conventions in place will prove useful in providing context within the portal and also to AI agents working within your workspace.
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. I recommend reading on the concept of atomic commits.
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 commented in the code. PR description is already in the ticket description. If for the frontend, it should contain screenshots of the 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 writes 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.
Deployments
Containerization
Applications, services should be containerized. Use Docker. This allows portability of services across environments without headaches. In development, run all dependencies of the service, including the service itself, database, migration scripts, cache DB, and workers as containers. Ensure this structure is as similar to the production setup as possible. Docker Compose can be used to orchestrate them together. Tbh, you likely will never need k8.
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
As time goes on, you can integrate other tooling and add additional checks:
Static analysis: Checks for common evil patterns out there and calls you out when you err.
AI code review tooling. Consider Codex (by OpenAI), CodeRabbit.
Code vulnerability scanner: Tools like Snyk help keep your code free from common known vulnerabilities.
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. If you self-host, just set up Coolify to manage infra for you. It gives you this feature out of the box and solves 90% of your deployment problems.
Hosting
If you self-host, use Coolify. Helps you handle a whole lot out of the box without pain. You can deploy your backend and frontend like you’d do in a PaaS such as Heroku, Render, DigitalOcean, and Vercel.
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.
AI rules file
Most coding agents allow you to specify guardrails and guidelines for the agent to follow when working on your codebase. Cursor has .cursorrules Claude has CLAUDE.md , Codex uses the generic agents.md file. Use AI, but with very strict code review process. Don’t be like punks who say AI is no good; except you work in critical workload environments like aviation, OS kernel development, healthcare tooling, and anything that directly holds the life of people within hands made of 0s and 1s.
Microservices
If you’re using the microservices architecture (I always advice to start out with monoliths and migrate when the need actually arises. Read this), go the monorepo approach and save yourself from birth pains. It is easy to make changes that cuts across services in one single PR without complex migration and versioning plans.
Internal Developer Portal
While this is relatively new, having one will help standardize workflows and provide visibility into your work as a developer, bringing all the resources from the tools you use into one view. Just check out Port’s demo and you’ll see what I mean.
Conclusion
This is a highly opinionated checklist I have used over time. Feel free to reuse and customize to meet your team's needs. Ensure to recommend this article to your friends and teammates at work, and drop a comment if you think there is something I should add or improve on.
Eat well, sleep well, rest well, and join me on DevMeets on Sunday!

No comments yet. You can start the conversation.