Git Workflows, Principally
Are modern Git workflows actually competing? What is the purpose of each? This writing is to help you decide for yourself. Fewer words, more substance.
The Context
I always wondered why Git workflows seemed so opposite to one another and so awkward to my personal rationale when reading about them in numerous public articles.
It occurred to me that the articles were just SEO pieces. They merely presented a set of words in context with no actual meaning or purpose for software development professionals.
One day, I set out to formulate my own understanding of the subject, and here it is: all three Git workflows (Git Flow, GitHub Flow and trunk-based development) cannot be used interchangeably at will. The digital product's life stage 1 dictates the code functional weight we incorporate to a mainline. And that, in turn, dictates which Git workflow to use. The workflows may change as the product life stage dictates.
Code Functional Weight Dictates Workflow
The code functional weight, not a Git workflow, drives the set of granular Git workflow techniques.
There are basically 3 types of code functional weights: functionally heavy, moderate and light. At different product life cycle stages we may see first one before launch, and two others after launch in active development or maturity / support stages.
Moreover, some products may contain the different functional weight code changes at the same time after launch in different parts of the product:
- heavy: big substance features not feasible to be launched in parts;
- middle: lightweight substantial features;
- light: fixes, patches, small improvements or changes;
For functionally-heavy we may use Git Flow. For functionally-low we may use GitHub Flow or trunk-based development.
Align Workflows with Code Functional Weight
How Workflows align with the code functional weight:
- Small, Incremental Work (Bug Fixes, Minor Updates, Patches):
Starting Point: Trunk-Based Development or GitHub Flow.
- Trunk-Based: For quick, small updates or bug fixes that are continuously integrated into the main branch with minimal overhead.
- GitHub Flow: Suitable for lightweight features or small fixes, as long as they can be merged quickly into the main branch.
- Medium to Larger Features (Modular Features, Enhancements, Non-Disruptive Changes):
Starting Point: GitHub Flow or Git Flow
- GitHub Flow or Git Flow: For features that can be completed in a shorter cycle, reviewed, and merged into mainline without disrupting production.
- Trunk-Based: Later in product life cycle when multiple small increments can be continuously integrated into the main branch (with feature flags if necessary; beware of feature flags imposed issues).
- Large, Complex Features (Multiple Components, Long Development Cycles, Release Planning):
Starting Point: GitHub Flow or Git Flow
- Git Flow: Perfect for larger, more complex features where you need a more structured approach with separate branches for development (
develop
), release preparation (release
), and hotfixes. These large features can be worked on over a longer period and require more careful planning and testing.
Principally Better
But we can make things principally better.
We could combine any the techniques from each of Git workflows into a specific flow that meets our requirements at any given time and for any product state. All we need is to know and learn to apply all of the techniques.
The List of Techniques
Technique/Approach | Benefit | Reason | Used In |
---|---|---|---|
Long-lived Feature Branches | Isolates new development work from mainline | Allows developers to work on features without affecting the codebase in mainline | Git Flow, GitHub Flow |
Permanent Develop Branch | Always releasable code. Integrates the finished functionality. Reflects the finished development history. | Accumulates the code ready for releases. | Git Flow |
Release Branches | Facilitates final testing and bug fixes before release | Helps isolate release preparation work from ongoing feature development | Git Flow |
Hotfix Branches | Allows urgent fixes to be applied to `main` without affecting `develop` | Supports rapid resolution of production issues while maintaining the integrity of `develop` | Git Flow |
Master Branch | Only production code. Reflects the latest production state and previous production states with version tags. | Guarantees that mainline is always stable and ready for release | Git Flow |
Mainline Branch | Always releasable code. Integrates the finished functionality. Reflects the past production states and is ready to be released. | Guarantees mainline is always stable and ready for release | GitHub Flow, Trunk-Based Development |
Trunk (mainline) Branch | Always kept in a deployable state. Accepts commits continuously. | Enforces small commits and very short-lived feature. Applicable for small functionality changes. | Trunk-Based Development |
Merge Strategy (Feature → Develop, Release → Main and Develop) | Keeps mainline stable and ensures thorough integration testing | Ensures features are tested and validated before they are merged into mainline. | Git Flow |
Pull Requests (PRs) for Review | Enables peer review and collaboration before merging | Ensures code quality through review, preventing defects from entering mainline. | GitHub Flow |
Continuous Integration (CI) | Automatically tests code upon commits or PR creation and before merging | Prevents defects from being merged into mainline by ensuring that code passes tests before integration | Git Flow, GitHub Flow, Trunk-Based Development |
Frequent Merges | Keeps the codebase up to date and reduces integration problems | Minimizes merge conflicts and promotes a smooth, continuous flow of changes | Git Flow, GitHub Flow, Trunk-Based Development |
Feature Flags | Enables incomplete or experimental features to be merged without affecting users | Allows larger features to be developed incrementally while maintaining a deployable state. NB: Imposes more issues than solves. For larger features use Git Flow techniques. | Trunk-Based Development |
No Long-Lived Feature Branches | Reduces the risk of divergence between branches | Ensures that changes are integrated frequently and avoids prolonged isolation of features. Fits small functionality. | Trunk-Based Development |
List of Practices and Their Consequences
Practice | Consequences |
---|---|
Frequent Integration |
|
Feature Branching |
|
Pull Requests / Code Reviews |
|
Continuous Integration (CI) |
|
Feature Flags |
|
Releasing Regularly |
|
Stable Mainline |
|
Clear Version Control |
|
Threats and Damages Counteracted
List of threats and damages that are usually counteracted by good Git workflow practices irrespectively of the concrete Git workflow.
- Merge Conflicts: A signal of infrequent integration of long-lived feature branches, leading to inconsistent codebases.
- Long Release Cycles: A signal to revise requirements for more granular feature definitions to establish the release plan, delivering valuable product functionality as close to continuous as customer value granularity suggests.
- Infrequent Integrations: A signal of stagnant development process.
- Unpredictable Deployments: A signal to introduce continuous automated and manual quality enforcement (lint, A/TDD, E2E, code review, acceptance testing).
- Lack of Transparency: A signal of infrequent integrations, establish continuous integration process (in any Git workflow).
Conclusion:
The key takeaway is that Git workflows should be driven by the code functional weight. By recognizing the size and complexity of the task at hand, the project can choose the right workflow that supports that functional weight, leading to comfortably error-free development processes.
The second, implied takeaway, is that a concrete product's Git workflow is not that one of the tree monolithic approaches. The concrete Git workflow contains 3-4 dozens of granular tools 1, 2, being purposefully combined together by a software engineer, serving the concrete goals at the always changing digital product life point in time.
Literature
To be able to establish the specifically appropriate Git workflows, study these essential sources:
Footnotes
-
The following product life stages generally impact the code functional weigh: early prototyping, massive foundational development, market launch, growth, major product adaptations, maturity (small fixes). An engineering team structure, commonly referred as impacting a Git workflow, is actually the consequence of the product life stage as well. ↩