The ways of working with git
Are you working in a team? Or maybe you are working alone? It does not matter how you work, you should always think about how to improve…
Are you working in a team? Or maybe you are working alone? It does not matter how you work, you should always think about how to improve your process. In this article, I’m describing different ways of working with git that worked for me in the past. I am also sharing my current way of working with git, which will probably not fit your team, but it might give you some ideas on how to proceed when trying to improve your process. And after that, I have two stories from the past for you about how things were done without git.
First step: Git and no code reviews
This is the first step. You have a Git repository, but you don’t know how to use it. You have a single branch, maybe collaborating with someone, but you’re not using any of the git features besides pull, push, and commit. This is a good starting point. This is the best way to start, but it’s nonsustainable long-term.
Starting small: develop and main branch
The next step is to start using branches. Let’s say you have some application being deployed manually to the production. And by this, I mean that you have to either copy files to the server or click some button in your IDE. You have no CICD pipeline, no automation, etc., but you’d like still to be able to make a hotfix to production while working on the new features. The flow then would be the following:
you have a
main
branch, which is always in the state of the productionyou have a
develop
branch, which is the branch where you're working on the new featuresat every moment you can switch from the
develop
branch to themain
branch, make a hotfix, and then switch back todevelop
without much disruption. You don't have to make sure that thedevelop
works fine, there is no heavy testing required before deploying a hotfix
Slow but steady: develop, main, and feature branches
This is the approach that works pretty well if you have multiple contributors. The flow is similar to the previous one:
you again have a
main
branch which is in the state of productionyou have a
develop
branch that should be working at all times, but it might happen that sometimes it's broken or untestedyou have feature branches, which you create to develop new features
once you finish the feature, you merge it into the
develop
branchonce you have enough changes, you merge the
develop
branch into themain
branch, do some extensive testing, and then deploymain
to the production
This approach is good because you push conflicts between the developers into the merge phase. If multiple people are working on the same branch, there is a high chance that they will have to constantly resolve conflicts. Especially if they’re working on similar areas of the code. The second advantage is that you can review and test features separately, before merging them into the develop
branch. This way you need to spend less time on testing the whole application, and if one developer decides to break the build for a week because of a big change (which I don't think is a good approach, but it happens), the other developers can still work on their features without disruptions.
Trunk-based development
Trunk-based development is the feature branches approach but with a little twist. You have main
, develop
and feature branches, but the goal is to merge feature branches as soon as possible into the develop
branch, and possibly also into the main
branch. This way you avoid the long-living branches, which are hard to merge. Of course, you need to make sure that your changes will not affect the production, so usually feature toggles are used to ensure that the code is unreachable, but it's still in the codebase. This approach is good not only because of fewer conflicts but also because the code reviews are smaller, so there is a higher chance of getting the code reviewed right away, without the need to wait days for the review. Have you ever had a situation where you've waited for a week to get your code reviewed? If yes then your process is broken. This is a deeper topic, but one of the ways to try and fix it is to lower the friction on the code reviews. So instead of working for 3 weeks on a single feature, and having 100 files changed, you split your work into small batches of let's say 20 files which you produce over 3 days. This way you might get your code reviewed faster, as the code reviewer will get less anxiety from seeing how many files have changed.
Release branches
Release branches are useful in two situations:
When you don’t have an automated CICD pipeline and you want to track when and which version has been deployed to production, or
When you have a versioned product, like a desktop application with multiple major versions (version 1, 2, 3, etc.) with long-term support for each of them
In the first case, I’m sorry for you. In the second one, I’m also sorry. These two situations are hard to work with. In the first case, you need to track releases in case of rollback or hotfixes — it might be that you have merged some changes into main already but you found a bug both on main and production, and these are two different bugs. Then it’s easier to just checkout the release branch and fix the bug, without the need to worry about the changes which are already in the main branch. This can also be done with tags. It’s a bit more complicated (just slightly), but the general principle is the same.
In the second case imagine a scenario in which you have 3 versions of your application that you support — versions 4, 5, and 6. And in version 5 you’ve found a security vulnerability. You need to fix it in versions 5 and 6. Usually how it’s done is that you fix the bug in version 5 (start with the lowest version you have) and then depending on the changes and if it’s possible, you either cherry-pick the fix to subsequent versions, or you manually re-apply the changes. You repeat the process for each version having this bug.
My current way
I’m currently working in a company where all developers are working mostly on separate services. We have a lot of services and each service is developed by a single developer. The workflow I am using is the following:
I have the
main
branch, which is the productionI have feature branches that are long-living and are merged into the
main
branch once they're doneFeature branches take a lot of time to develop — sometimes it happens that it’s even a week or two
If I have a feature taking more than 2 weeks, I usually split the work into smaller feature branches, trying to deliver some value as soon as possible, slowly expanding the feature
Of course, this approach has its downsides. The biggest one is that code reviews are sometimes massive. It’s pretty usual for us to have 100 or even 300 files changed in a single code review. But on the other hand, the process is lightweight. We need to review these 300 files anyway, so why not batch them together and do one single round of review instead of 10 rounds of 30 files each? What helps a lot is the fact that there is most of the time a single person working on the service, so we don’t have to worry about conflicts.
Summary
The process of code reviews is very team- and codebase-dependent. There will be a different process for a team working on a monolith, and possibly a different one for a team working on microservices. What I am looking for while working on the improvements of the process is simplicity. The process should be as lightweight as possible. But sometimes it’s just not possible to have a simple process. But always try to make it just a bit simpler.
Bonus: Stories from the past
Welcome to the 90s: SVN and no code reviews
As I’m typing this, I feel old. However, I don’t consider myself old. I started my professional career in an old-style company, writing Delphi code stored in SVN. The approach of SVN is different than the one used by Git — instead of being distributed, it’s centralized. This means that two people can’t edit the same file at the same time. It was enforced by locks — everyone could lock the file at the start of the work, and then release the lock after doing the changes. As I think of it right now, I find it pretty funny that I needed to walk to my coworker’s desk and ask him when he was done with the file so that he’d release the lock on the file and I could edit it. I remember that when I was opening any file with an active lock on it, I was getting a message that the file was locked by someone else. I was able to see who and when locked it. The worst part was when someone was on vacation. Then we had to do the ancient way of merging changes — I could steal the lock, and then the person had to copy their changes to the separate file, download my changed file, and then manually apply their changes from the separate file. No one thought about any form of code review. It was just chaos.
Version Control From Hell: Dropbox as a version control system
I once had the pleasure of working with a self-taught programmer who did not need to collaborate with anyone. He used Dropbox as his version control system. As he implemented the feature, or after some time, he just zipped the whole project and sent it to the Dropbox account. And to avoid paying for the storage, he had dozens of accounts with free tiers. He had the whole system of managing this whole mess and I’m impressed that it worked for him — he knew what is happening and when he had any problems, he was able to restore the old version. But enough of the funny stories from the past, let’s move on to present times.