Now if that seemed far fetched for you I'm still sure you've come into contact with another form of something that resembles a SCM of sorts, and it's called 'undo', or you have used change tracking in a text editor (paraphrased from Tiffany Conroy).
So now that we have established the basic usefulness of being able to go back over your changes let's explore the benefits of a full-fledged system. Here at Zweitag we use git for that, which's advantages we'll also cover here.
When we have completed a unit of work, that we know is in a good state, we make a commit out of it. "Now what does that mean?" you might ask. Well usually it's a small set of changes made to an existing code base—part of many changes to achieve a certain goal. Committing our changes lets us define this point in time and document it with a message. This message is quite important for future reference. The committer can explain which changes she made and why.
Over time this provides a complete log of all the changes made and their respective authors. So if there is ever any doubt or confusion as to why a certain change was made, it can usually be inferred from the commit message—or you know who might be able to help you out. This history is kept for every line of code (or of a document in general), making this even more useful.
Commits provide you with the ability to go back in time. If you made some changes you are not happy about, the commits provide you with a way of going back to a point where you were, and this is not limited to a single file. A commit can be made up of as many files as needed to document the change. Instead of reverting to a certain state you also have the ability to revert commits from the change history. This is very useful when you have pinpointed one or many commits inbetween, that are e.g. responsible for a bug and you need to undo those changes. Git can also aid the user when searching for such a bug with the git-bisect command. It will guide the user to the first bad commit via a binary search algorithm.
Branches and Merges
This is one of the points where using git becomes important to us—let's briefly explain what a branch is first though.
Essentially it's like the figurative in speech "branching off from something". Let's take our previous example from the commits. Say you have gone back a few commits and want to take the code in a new direction from there, but you also don't want to toss away the commits you've made after that point. You want to diverge from the linear path. That's what branching allows you to do. You branch off from a chosen commit and work from there forward, leaving the main branch to be developed in its own manner.
We branch religiously i.e. every new feature or bugfix gets its own branch. Well, git makes this utterly effortless. Branching is very quick and lightweight. It allows one to make all sorts of code changes without jeopardizing the stability of the currently deployed version at all.
If a fix or small change needs to be made to the main code base it can be done without having to consult others or undoing a heap of changes. The update can be deployed quickly and efficiently.
Branches aren't meant to just dangle in the wind indefinitely—metaphorically speaking. The changes in a branch are usually made to flow back into the development of the main version. This is another point for git: merging branches is almost as straight forward and easy as branching-off is. This is even the case when working in parallel on a code base, even when working on the same file, in most cases, the SCM is smart enough to merge the changes made together without any necessary intervention. Having this strong merge feature gives team members the freedom and space to work on a feature or fix without having to stop multiple times and coordinate changes on a file to file basis with others. Since they know their changes can be merged back into a single code base, with little effort, changes can be made with greater confidence. If there are changes made to the same file that can not be merged automatically, git will prompt the user to resolve the conflicts. It will mark the conflicts directly in the files that need to be edited, in a way that both versions are displayed with their origins for the user to choose a version, or mix and match if needed.
Now the great part about git is that it allows us to do a great part of the work offline. You do not need an internet connection to commit, branch, merge, or view the commit history. Unless instructed otherwise, git keeps a full copy of the entire repository on every developers machine. This essentially means that every working copy also doubles as a distributed backup.
Being able to branch offline also affords you the ability to create throw-away branches or try out a quick idea without polluting everybody's working copy with every little branch that ever existed. You can choose which branches get pushed upstream.
Teams & Collaboration through Pull/Merge Requests - Where it all comes together
Working with git also affords us the ability to use GitHub, GitHub Enterprise (or GitLab in some cases), which we rely heavily on. When working in teams, being able to share your work and get feedback from other developers is essential. These tools give us the ability to comment directly on the lines of a commit's code, enabling discussions about technique, viability of the solution and ultimately provide our favorite form of quality assertion–two sets of eyes will mostly spot more issues than a single one set can. Having other team members look over your work gives them the chance to provide feedback on what you have done, or give them insights to your knowledge, the new tool you found, or your technique.
When working on a new feature or bugfix we branch off, work on the code, push the branch back to GitHub, and issue a pull/merge request. This request holds a summary of the changes made, explaining to others what was done and why, this sparks a general discussion and review over all commits made on the branch, but also enabling—as stated above—comments on single commits. Suggestions that are given here lead to the start of a feedback loop. The developer takes the suggestions made into account and changes the code accordingly for another round of review. This is repeated until the reviewer deems the code fit to be merged. This is one of the most important parts to software quality in our minds. It ensures the code we produce is up to our standards, is formatted in a way which we agreed on as a company (making sure everybody can read each other's code without having to adjust for coding styles), has the least possible error margin that is economically feasible, and is on task towards the customer's business value.
Santa's Little Helpers - Continuous integration & static code analysis
Using GitHub also enables us to tie in other services that act on our pull/merge requests and aid our development efforts. In our case these are continuous integration (CI) services and code analyzers.
CI runs our test suites and amends the pull/merge request with the build results. Giving a quick at-glance status of the build to the reviewer. The team will also get informed about build failures and can chime in on solutions or get hands-on.
Code analyzers help find potential code duplications, reveal unnecessary complexity in the codebase, and check for a range of possible security vulnerabilities. We also let it check out code for test coverage. Having a high coverage can enable future changes can be made with less necessary effort, given the tests are comprehensive enough.
Together, running these two types of services gives the developers a quick—almost instant—feedback on their changes. It is also a great starting point for a reviewer. They have the ability to get a first glance—a 10,000 foot overview—of what they are going into, before digging into the details. As alluded to above, the commit messages on the changes are important and very useful. This is also the case when reviewing changes. A good commit message will also document to the reviewer why certain changes were necessary, usually eliminating the need for a reviewer to question the developer on the changes made to the code. This speeds up the review process and provides less overall interruptions in team members' workflow.
Using SCM, specifically git, provides trackable changes to a code base, enabling quality assurance and efficient review workflows, especially through added services. Lightweight branching makes it practical for teams to work on code in parallel and to experiment with different directions inside a code base, giving them peace of mind when changing or writing code.