Optimizing your Git workflow
Exploring GitHub flow, GitLab Flow, and how to leverage feature flags for seamless deployments
When working with a team on a software project, choosing the right Git workflow is essential. Traditional git-flow has been popular, but it's not always the best fit (it was conceived in 2010, and not with web apps in mind).
In this article, we’ll explore GitHub flow and GitLab Flow, and how combining them with feature flags can optimize your development process.
What is GitHub flow?
GitHub flow is a simplified Git workflow that emphasizes collaboration and deployment. Unlike git-flow, GitHub flow doesn’t use a develop
branch. Instead, it encourages creating a new branch off from the main
branch for each feature or task. Developers commit changes and open a pull request early, keeping everyone informed. Merging feature branches into the main
branch happens more frequently.
This approach has the following benefits:
Reduces complex merge conflicts.
The
main
branch codebase doesn’t diverge.Facilitates continuous deployment.
How does trunk-based development fit in?
You might wonder how GitHub flow compares to Trunk Based Development. They’re almost similar! Both workflows advocate frequent merging into the main
branch.
The key difference lies in branch lifetimes and the approach to commits. In trunk-based development, each committer is encouraged to stream small, well-tested commits directly into the trunk
or master
branch. When scaled, trunk-based development employs short-lived feature branches that typically last no more than a couple of days, and flow through a pull-request style code-review and build automation process.
On the other hand, in GitHub Flow, branch lifetimes are more flexible and can be longer, depending on the feature.
GitLab Flow = GitHub flow + deployments, environments, releases, and integrations with issues
GitLab Flow combines principles from GitHub flow and git-flow.
Like GitHub flow, GitLab Flow emphasizes a deployable main
branch, and feature branches. But it also introduces environment branches for different deployment stages or version branches for software releases to the outside world.
Like git-flow, GitLab Flow has guidelines for implementing hotfixes, but GitLab Flow simplifies the branching and merging rules associated with hotfixes.
Comparing the Git workflows
The following table provides a side-by-side comparison of GitHub flow, GitLab Flow, and git-flow:
Criteria | GitHub flow | GitLab Flow | git-flow |
Main branch | main | main | master and develop |
Feature development | Branch off from and merge back into main | Branch off from and merge back into main | Branch off from and merge back into develop |
Deployments and releases | Continuous delivery from main | See GitLab Flow deployments / releases below | Branch off from develop , merge back into develop and master . Release from master and tag the version. |
Hotfixes | Treated the same as feature development | Develop on a feature branch and cherry-pick into the environment / release branch | Branch off from master , merge back into develop and master |
Environment branches | None | Separate for each environment | None |
Complexity | Lower | Moderate (more flexible than git-flow) | Higher (prone to human error due to the various branch-off-from / merge-back-into rules) |
GitLab Flow deployments / releases
If you deploy software to a server, create environment branches and merge downstream (from
main
intostaging
,staging
intopre-prod
,pre-prod
intoprod
).If you release software to the outside world, create a version branch off from
main
for each release.
Taking control with feature flags
So, how do we prevent incomplete code from executing when frequently merging into main
? Feature flags are the answer.
What are feature flags?
Developers use feature flags to deploy code without activating it. They can turn the feature on later, without modifying the codebase. Essentially, a feature flag is like an on/off switch for features.
This is especially useful in GitHub flow. Wrap an incomplete feature in a feature flag. Merge it into main
without worries. The code is shipped but stays inactive. When the feature is complete, toggle the flag to "on".
Feature flags also support more complex scenarios, like canary releases. In canary releases, a new feature is exposed only to a small subset of users initially.
But be cautious! Feature flags can clutter the codebase if not managed properly. It’s important to remove them once the feature is stable and rolled out to all users.
Read more on feature flags in Martin Fowler's article.
Preprocessor directives: An alternative
An alternative to feature flags is preprocessor directives. These control which portions of code are included during compile time. They’re traditionally used in languages like C, C++, and C#.
Here’s an example in C#:
#if NEW_FEATURE
// New feature code
#else
// Old logic
#endif
This allows you to include incomplete features in the main
branch without affecting the deployable state.
But there are limitations:
Requires recompilation to enable or disable features.
Lacks granular control that feature flags offer.
Use them wisely.
Reducing technical debt: Clean up feature flags and preprocessor directives
When not maintained, feature flags and preprocessor directives can lead to technical debt.
Feature flags: They create two paths in your code: one with the feature and one without. This can make the codebase complex. When a feature is stable, remove the flag and any related code paths.
Preprocessor directives: Like feature flags, these directives create multiple versions of a codebase. Once a feature is complete, remove the directive and always include the feature code in the build.
Cleaning up is essential to avoid complexity and maintain a clean codebase.
Conclusion
Choosing the right Git workflow is important. By combining GitHub flow or GitLab Flow with feature flags, you can optimize your development process, reduce merge conflicts, and deploy smoothly.
Remember, the best Git workflow is the one that fits your team and project. Keep learning, adapting, and improving.
Happy coding!