Why Git workflow is a pain for every Go developer?
\nHave you ever spent half a day figuring out why the build broke in the master branch, even though everything worked yesterday? Or got confused with Go module versions when two developers were editing the same package simultaneously? Sound familiar? Welcome to the club.
\nGo is a simple and elegant language. But without the right Git workflow for Go developers, even the cleanest code turns into spaghetti. In this article, we'll break down not abstract theories, but real scenarios: how to work with go modules, how not to break CI/CD, and how to make code review fast and useful. Buckle up, it's going to be practical.
The Basics: Git Flow and its adaptation for Go
\nClassic Git Flow (branches main, develop, feature/*, release/*, hotfix/*) works great for large projects. But in the Go world, there are nuances.
Why Trunk-Based Development often wins?
\nThe Go community loves simplicity. Trunk-Based Development (working in one main branch with short-lived feature branches) perfectly aligns with Go's philosophy. Fewer branches — less headache with merges. For small teams (up to 5 people), this is often the best choice.
\nHowever, if you have a two-week release cycle and several parallel features — Git Flow remains king. Just remember: each branch should live no longer than 2-3 days. Otherwise, conflicts are inevitable.
\n\n# Example of creating a feature branch for a new middleware\ngit checkout -b feature/add-request-logger\n# Work, commit, push\ngit push origin feature/add-request-logger\n# After code review — squash and merge into main\n\n\nGo Modules and Git: How not to go crazy
\nThe most common problem in golang git workflow is dependencies. Go Modules (starting from Go 1.11) solved many problems but introduced new ones.
\n\nProper work with tags
\nYour module is a public API. If you change it, change the version. Semantic versioning (semver) is not just trendy, it's mandatory.
\n- \n
- v0.x.x — unstable version, breaking compatibility is allowed. \n
- v1.x.x — stable, backward compatibility is mandatory. \n
- v2+ — major changes, you must change the module path. \n
Example of correct tagging:
\ngit tag v1.2.3\ngit push origin v1.2.3\n\nAnd now an example of what NOT to do:
\n# Bad: tag without syncing with go.mod\ngit tag v1.0.0\n# But go.mod still has version v0.5.0\n\nAlways update go.mod first, then set the tag. Otherwise, go get will go crazy.
How to update dependencies in a team
\nLet's say you updated the library github.com/gin-gonic/gin. Instead of pushing a huge diff, follow the rule: one commit — one change.
# Update one dependency\ngo get github.com/gin-gonic/gin@v1.9.1\ngo mod tidy\ngit add go.mod go.sum\ngit commit -m "chore: update gin to v1.9.1"\ngit push\n\nThis simplifies code review: your colleague sees exactly what changed, not the "magic" in go.sum.
Code Review for Go: Git workflow guarding quality
\nCode review is not about "find bugs". It's about knowledge transfer and maintaining style. But if your git workflow for golang projects is not set up, review turns into hell.
\n\nHow to make review fast
\n- \n
- Small PRs: 200-300 lines of code — maximum. More — break it down. \n
- Automatic linting:
golangci-lintshould work before the review, not after. Set up a pre-commit hook or CI. \n - Consistent style: use
gofmt(yes, it's mandatory). No arguments