DEV Community

Cover image for Demystifying Git Flow: 'Clean Room' Mode

Demystifying Git Flow: 'Clean Room' Mode

After several years in the world of software development, code management has always been a cornerstone of productivity and quality. However, who has never found themselves trapped in a tangle of merge conflicts, having to resolve them two or three times a week just to get a piece of code into a testing environment? It's a question that, curiously, often leads to silence, and no one dares to throw the first stone.

The malleability of Git Flow, if not carefully managed, can frequently lead to disorganized or unproductive Git flows. This situation isn't just a technical inconvenience; it represents a significant drain on team productivity and morale, confirming that merge conflicts are a frequent obstacle in development environments.

But why do our Git flows get tangled?

The frequency of conflicts and disorganized Git flows are often symptoms of deeper problems in the branching strategy itself. One of the most common pitfalls is the existence of long-lived branches that diverge significantly from the main branch (develop or main). This substantial divergence makes merge operations progressively more complex and conflict-prone.

Another significant challenge, especially in traditional Git Flow implementations, lies in unwanted dependencies between features. When feature branches are created from develop, and one of them (say, Feature A) is integrated into develop, any subsequent feature branch (Feature B) that updates its base from develop will end up inheriting the changes from Feature A, including potential bugs or unfinished work.

Rethinking Our Git Workflow:
The other day, I was reading some complaints on Slack from colleagues who had to review PRs with nearly 100 changes (and that wasn't even the biggest I've seen), and I started to analyze some recent experiences to gather ideas and build a model to be followed that would bring more peace of mind to the daily grind. That's what I'm going to show you, hoping to help you, whether by following it to the letter or adapting it to your reality. The important thing is that you leave here feeling lighter, with the peace of mind that your Git flow will be better. hehehe

The central philosophy of this adapted Git Flow methodology is based on three pillars: granularity, isolation, and controlled integration. The goal is to break down large tasks, isolate changes, and introduce multiple controlled review points to ensure quality and minimize the impact of conflicts.

Step 1: Every new branch must start from main (or master)

The first and fundamental change from traditional Git Flow is that every new branch must start from main. Unlike the classic model where feature branches usually originate from develop, here, each new feature is started from the stable, released production branch. This directly addresses the problem of unwanted dependencies, where updating a feature branch from develop could inadvertently inherit bugs or incomplete work from other features. This strategy ensures that each feature is completely independent and can only be integrated when it is ready, validating the importance of isolating feature branches for superior control.

Step 2: Fractional PRs/MRs for features – Breaking down cards into smaller subtasks

Instead of a single, large Pull Request (PR) or Merge Request (MR) for an entire feature, the approach is to break down cards (representing features or epics) into smaller, atomic parts. This practice is a direct application of the best practices of keeping commits small and focused and making frequent, small commits. Smaller changes are inherently easier to review, understand, and, crucially, less likely to generate severe merge conflicts. This facilitates review and conflict resolution, and smaller commits make it easier to identify and resolve conflicts incrementally

Step 3: chore branches for subtasks and PR to feature for Code Review

Each of these smaller subtasks gets its own dedicated branch, typically named as chore. Once a chore branch is completed, a PR is opened pointing to its parent feature branch to undergo a code review. This introduces a layer of early and granular code review, ensuring that even within a single functionality, changes are meticulously verified before they are integrated. This step is in perfect alignment with the emphasis on Pull Requests (PRs) and Code Review as essential practices for evaluating changes and reducing the risk of errors.

Step 4: feature to develop only after all subtasks are reviewed

Only after all subtasks of a feature have been completed and individually reviewed (via PRs from chore branches to the feature branch) is the feature branch ready to be integrated into the develop branch (the main integration branch). This ensures that develop receives only code that has already gone through a comprehensive, multi-stage review process. This approach significantly reduces the risk of introducing incomplete, buggy, or unreviewed code into the primary development line, contributing to a more stable develop branch. This echoes the principle of ensuring that only reviewed and approved changes are added.

Step 5: Dedicated feature-dev branch for conflict resolution

This is a highly innovative and practical aspect of the workflow. In cases of conflict between the feature and develop branches, instead of the common (and often problematic) practice of pulling develop into the feature branch to resolve conflicts (which can pollute the feature branch with unwanted changes), a new dedicated branch, feature-dev, is created. This branch serves as a temporary staging area specifically for resolving conflicts between the feature and develop. Once the conflicts are resolved in feature-dev, this branch is then integrated into develop.

This strategy keeps the original feature branch untouched and focused on its core functionality, ensuring that the branch destined for main (production) remains clean and free of merge commits or conflict resolution artifacts. The concept of a dedicated branch for conflict resolution is directly validated by the idea of using conflict resolution branches instead of polluting feature branches.

Step 6: Corrections for QA Testing

When corrections are needed after QA testing, the developer should make these changes in the original feature branch. These corrections should then be pulled into the feature-dev branch. This practice keeps both branches up-to-date and ensures peace of mind regarding the release flow. If corrections for QA testing were made directly in the feature-dev branch, it would be necessary to cherry-pick these changes later to update the original feature branch, which is essential for a secure deployment and to avoid inconsistencies in the code history.

Okay, but what's next?

After that, it's just about being happy. The feature-dev branch moves to other environments that may exist in your work context, such as QA, HML, etc. But at the end of the process, the release starts from main (or master) and receives the original feature branch, clean and free of any suspicion (lol).

Why This Approach Works:
Introducing the feature-dev branch for conflict resolution is a simple but effective application of conflict resolution and a key workflow design element. This approach embodies the principle that prevention is key and that preventing Git conflicts is always better than resolving them. By isolating conflict resolution, you ensure that the feature branch remains clean and focused, mitigating the risk of problems when merging to main for production. (And let's be honest, reviewing 43 altered files at once?! No amount of coffee can get a cowboy excited).

So, have you ever had bad experiences because of a poorly done Git flow?

Have you ever made a slip-up in Git? Me? I've made several, but the important thing is to learn from your mistakes and move forward.

Tell me about your experience and if you liked this concept in the comments!

#GitFlow #VersionControl #SoftwareDevelopment #DevOps #MergeConflicts #Productivity #TechTips #GoodCodingPractices

Top comments (0)