Skip to main content

How I work with Version Control: Tools, Practices, and Personal Workflow

How I work with Version Control: Tools, Practices, and Personal Workflow #

Sharing experience is a great way to reflect on your own practices and learn. I’ve decided to record my practices of working based on last 8 years of experience in software engineering. This is a series of notes where I document how I work with version control, what tools I use, how do I manage my workstations, and how do I engineer software.

The first note in this series is about the core of the software engineering and the most important tool in the developer’s toolbox - version control systems. I am going to share how I work with version control, what practices I follow, what habits I have. Hope you will find it useful.

Version control #

It’s hard to imagine writing software without a version control. It’s a nutshell of the software engineering and it took me a while to develop what works best for me.

Let me start with my understanding of why should a good engineer use version control.

  • Version control is a source of truth. What’s in the repository is what you get in production.

  • Version control is a great audit tool. It helps you to understand what has been changed, when and why.

  • Version control in a team of software engineers is a collaboration tool. It helps to coordinate work, to review changes, and finally build things.

  • Version control is a tool. Never let your version control get in the way-configure it to work for you.

Perhaps, this is not a complete list of why, but this is what important for me.

Git #

I use Git. It’s the standard de facto, and a skill to work with Git is a must-have for a software engineer.

Git CLI vs GUIs #

I feel more productive with the CLI. I tried GUIs, several times, including GitHub Desktop, GitKraken, GitButler, VCS in IntelliJ IDEA, and others. Yet, once I mastered the CLI, I could not find a reason to switch back. CLI gives you more freedom by automation and scripting.

Branching model #

A short article describing what I’ve lead Trunk-based development with short lived branches. It cannot be easier. It cannot be more streamlined. It requires discipline within a team. You need to ensure and communicate the importance of these approaches and then establish the habits.

Trunk with rebase is the best I’ve tried so far. I don’t want to start another holy war and argue why you should use rebase, but probably it’s worth trying.

Atomic commits #

I am doing my best to make commits atomic. Make changes self-contained, as small as posbbile, and logically separated. It’s easier to review, easier to understand, easier to revert. A great practice I really recommend to follow.

Commit messages #

Until I am not an author of a library, that is used by millions of developers, I would not bother to follow a commit message specification or convention. I think simple and clear message describing what has been changed and why is more than enough.

Stacked pull / merge requests #

In a situation where I can’t make an atomic change within a single PR, I prefer to stacked PRs. Learn how to do so. Make life of your reviewers easier. Reviewing a PR with a small set of changes is much easier than a PR that contains tons of different additions and deletions.

General practices and habits #

Frequently pull changes from trunk. How frequent depends on the structure of repository and how often your team pushes the changes to main / master. I start my day with pulling changes and resolving conflicts if any. Communicate with your team, avoid integration challenges.

Consice commit messages. A commit message is not a novel and it’s not just a “fix”. Communicate what have been changed, explain why you did it.

GitHub, and other platforms #

I keep my personal repositories on GitHub. I want to at least mirror some of them to Codeberg, but I am not sure if I will do it in the nearest future. I like GitHub a lot. Collaboration features became better in the last years.

I was a little nerdy and pedantic about the configuration once I learned Terraform / OpenTofu, so all my repositories are managed in OpenTofu. Even this very repository is created using one-line change in this file.

Configuration #

My .gitconfig is splitted into the several files. A personal.gitconfig I use when I work on my personal projects, and a work.gitconfig I use when I work for my daily job. To achieve this, I use the following configuration in my .gitconfig:

[includeIf "gitdir:~/personal/"]
	path = .config/git/personal.gitconfig
[includeIf "gitdir:~/work/"]
	path = .config/git/work.gitconfig
[user]
	name = Nikita Barskov

This is really great, because you can configure different email addresses, use different signing methods.

1Password SSH agent is a great tool to manage your SSH keys and use it with Git.

Global ignore files #

I’ve learned that from my collegue. And it’s actually great. Create a global .gitignore and keep there things you don’t want to see in your repositories.

  • OS-specific files,
  • IDE-specific files,
  • and other things.

Create a file .config/git/ignore and populate it with what you think is right for you.

Configuration for trunk-based development #

There are parts of my .gitconfig that I configured specifically to make my life with a trunk-based development easier.

  1. Set auto remote setup to true. This is a great feature that allows you to push your branch to the remote and set up the tracking branch with a single command.
[push]
  autoSetupRemote = true
  1. Set rebase by default for pull.
[pull]
  rebase = true
  1. Use SSH to access remotes and for signing commits and tags.
[gpg]
  format = ssh
[gpg "ssh"]
  program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign"
[commit]
  gpgsign = true
  signoff = true
[tag]
  gpgsign = true
  signoff = true
[user]
  signingkey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAWKGfG1hnaUAyfKkCa2ELVwO+2Z9FMCj3ysC0I6uTzi
[url "ssh://[email protected]/"]
  insteadOf = https://github.com/

Useful aliases #

In addition to configuration, I use several aliases to be more productive.

  1. git bt - creates a branch with a prefix. In my scenario I use my name and current datetime:
[alias]
  bt = "!bt() { git checkout -b nikita.barskov-$(date +"%Y%m%dT%H%M%S")-$1;}; bt"
  1. git gone - deletes all branches that are gone on the remote:
[alias]
  gone = ! "git fetch -p && git for-each-ref --format '%(refname:short) %(upstream:track)' | awk '$2 == \"[gone]\" {print $1}' | xargs -r git branch -D"

Working with Pull Requests and code reviews #

The most important rule of a Pull Request for me: a change should be profitable. It’s not about the number of lines of code, or elegance of the solution. It’s always about the value.

I really like a Playbook from Google and agree with the points there. Try to follow them as much as possible.

Code Review are also supposed to be a respectful and constructive environment. You should realise that a person made a change did it with the best abilities and intentions, while a review has limited context and should first understand the reasons before commenting.

Pull Request Automations #

I rely on GitHub CLI a lot. An extremely powerful and useful tool to enhance your workflow.

For instance, I rely on Dependabot, and sometimes I find it useful to list specifically Dependabot PRs. This is how you do it:

gh pr list --authot "app/dependabot"

Sometimes, I want to batch close PRs:

gh pr list --json "number" --author app/dependabot | jq -r '.[].number' | xargs -I {} gh pr close

Deployment and version control #

I am trying my best to set the correct foundation for the good engineering, therefore I prefer to have CI / CD pipelines in place beforehand. Some things machines can do better than humans, and continuous integration and continuous deployment are for sure among them.

Conclusion #

I prefer simplicity and pragmatism in everything I do. Finding a balance between the over-engineering and poor design is a key. I hope you find this note useful and can take something for yourself.

In the next article I will share how I manage to incorporate LLMs into my version control routines.