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:

CriteriaGitHub flowGitLab Flowgit-flow
Main branchmainmainmaster and develop
Feature developmentBranch off from and merge back into mainBranch off from and merge back into mainBranch off from and merge back into develop
Deployments and releasesContinuous delivery from mainSee GitLab Flow deployments / releases belowBranch off from develop, merge back into develop and master. Release from master and tag the version.
HotfixesTreated the same as feature developmentDevelop on a feature branch and cherry-pick into the environment / release branchBranch off from master, merge back into develop and master
Environment branchesNoneSeparate for each environmentNone
ComplexityLowerModerate (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 into staging, staging into pre-prod, pre-prod into prod).

  • 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!