Ask HN: Best Git workflow for small teams
I have been building up a small team of programmers that are coming from CVS. I am looking for some ideas on ideal workflows.
What do you currently use for teams of 5-10 people?
What do you currently use for teams of 5-10 people?
88 comments
[ 5.6 ms ] story [ 89.6 ms ] threadSee for example https://www.thoughtworks.com/de/insights/blog/enabling-trunk... for a much more in-depth discussion.
We just keep it simple like:
- No one pushes to master.
- For every feature (or update on a feature), create new branch.
- Send pull request for reviewing (one of us is the reviewer).
- Merge pull request into master.
---
You could try git workflow, but it's just too complicated for small team (and for large teams too?).
git flow seems overkill, unless you regularly need to patch older versions. For software where you control which version(s) are deployed, you can usually get away with (much) simpler workflows.
There isn't one size fits all, different teams develop at different rates because of ability, requirements, deadlines, culture.
Just keep it simple stupid! Why have a separate branch for tags? Or hot fixes even ?
both work well I think for small teams just depends on how well you want to organize things.
Feature branches were rebased then merged with a --no-ff. This creates a merge commit on master which includes the ticket number and short description.
The rebase is done by the feature branch developer who is usually best qualified to do so.
The advantage is that changes are isolated from each other, so nothing is spread before it's reviewed. The rebase makes the history easy to follow because it becomes linear and you can always easily see exactly what was changed in the branch. You still retain the full history of the feature branches.
Disadvantages: it doesn't scale to a massive team because you need to linearize the merges back to master. So it works for upto around 20 people.
I'd recommend against Git Flow because their method of merging makes it very unclear exactly what was changed in a branch. I actually prefer no feature branches to using Git Flow.
As the above post mentions I like to use feature flags to make this possible.
My team's process is simple and easy for anyone to follow: all feature work is done on feature branches. Branches should be kept small and atomic. When they've passed code review, squash and merge into Master.
If you need to pull changes from master, always merge into your feature branch, never rebase.
Of course, this does require someone to be more proficient with Git beyond the simple add, commit, fetch, push model, which has its own drawbacks.
Also, at least in Git, squash is just one use case of interactive rebase, so I'm not quite sure what you're arguing against...
Rebasing is wonderful, it makes Git a joy to use. But you have to understand the process and, most importantly, train your team so that they also understand it.
As I've said, I've had great experiences working like this; but it took a lot of effort and support of my senior developers to get the team up to speed.
In the end, however, it is going to come down to the specifics of your app and environment. What sort of CI do you have set up? Do you have anything that controls when or how you are able to release code? These types of things are what generally push teams to other models.
An admin merges a release back into Master and back through to dev after a milestone build, demo or production release.
Keep the same Workflow with other teams in your organisation if it exists - unless it is fatally flawed - so that team members moving around have the same experience. Consistency and all that
- Make a rule that anything that's in master can be deployed by anyone at any time. This will enforce discipline for code review, but also for introducing changes that f.e. require migrations. You'll use feature flags more, split db schema changes into several deployments, make code work with both schema version, etc. All good practices that you'll need anyway when you reach larger scale and run into issues with different versions of code running concurrently during deployments.
- If you're using Github, check out the recently added options for controlling merging. Enable branch protection for master, so that only pull request that are green on CI and have been reviewed can be merged. Enable different dropdown options for the merge button (eg rebase and merge), these are useful for small changes.
- It takes some time to get used to those constraints. Make sure that everyone is on board, and do a regular review of the workflow just as you would review your code. It takes some time to get used to, but I think it's worth it in the long run.
Features are developed in branches and merged into develop. Once we're happy with things, we tag & merge into master. (Junior dev so my approach may not be the best.)
Is that functionality available within Bitbucket + an external CI somehow?
Our workflow involves branching off of master at all time. Staging server can be wiped at any time, we have multiple staging servers. Production code can only rebase with master (or release branch); depending on the client we also have a release server as well which rebases with master and then into production.
Works well so far with team up to 5 developers....haven't tested it out further but wouldn't anticipate too many issues.
[1]: https://bitbucket.org/product/pricing?tab=host-in-the-cloud
But yes, master should always be deployable, and can only be merged once all tests have past (we use Jenkins).. And you are never, ever allowed to merge your own code (no matter how small it is)
- https://westurner.org/tools/#hubflow
- Src: https://github.com/datasift/gitflow
- Docs: https://datasift.github.io/gitflow/
-- The git branch diagrams (originally from GitFlow) are extremely helpful: https://datasift.github.io/gitflow/IntroducingGitFlow.html
The main cherry I like to put on top is to always rebase before merging then merge with --no-ff (which is the default with GitHub PR merge button). This gives linear history for purposes of git bisect but preserves the topic branch structure so you don't have to read the tea leaves of the individual commits to piece together the overall development timeline. That one little piece goes a long way to addressing the concern that "rebasing destroys history" (which I don't agree with anyway, but that's the topic of another post).
This might not be worth it for certain types of contributors who are not programmers by trade, but for career programmers I believe mastering git and rebase in all its flavors (-i, --onto, pull --rebase, etc) pays big dividends that your team will appreciate more as the years go by. For git beginners maybe you start without rebasing, then learn the ins and outs of rebasing with local branches only before pushing, before finally going the whole hog and rebasing topic branches with force push before merging.
One last idea I like is Zach Holman's recommendation of deploying topic branches directly and then merging if there are no issues (https://zachholman.com/posts/deploying-software) in order to have easier rollbacks. Then you have 100% guarantee master is always stable.
This is nice because in addition to the isolation it provides when everyone is working on their own fork it's easy to code review changes BEFORE they land in the "blessed" repository.
It's also nice because junior developers aren't pushing directly to the blessed repo either.
This model is really nice for maintenance and small feature development.
For major new development (new version, major feature that multiple people will work on) we tend to revert back to the behavior of committing directly to the blessed repo.
http://paulhammant.com/2013/04/05/what-is-trunk-based-develo...
http://paulhammant.com/2014/09/29/shades-of-trunk-based-deve...
http://paulhammant.com/2013/12/04/what_is_your_branching_mod...
I'm putting together a course on GitLab CI. It's powerful stuff!
Is there a contact address for basic non-technical feedback?
You can get in touch with GitLab via https://about.gitlab.com/contact/
I _am_ developing a tutorial on GitLab CI which I'll post on Show HN once done and I'll mention it in this thread.
https://www.kernel.org/pub/software/scm/git/docs/gitworkflow...
Just commit everything to master and that's all! Sound stupid but worked best for us. I have seen a big software company using this, I learned this from them and never looked back!
The biggest change for us when we starte was do not push features for the 3 month release into master so at any moment or bug fix master is always ready to go live anywhere without some dumb new feature we don't want. Keep master clean dammit :)
Feature branches are named after a ticket. Pull requests are across branches in the same repo, not across repos. (It's marginally easier to collaborate on feature branches if they're all in the same repo.)
We don't (yet) do full continuous deployment, so many feature branches get grouped together before a release. Our strategy is to test* each feature independently and then merge all the tickets together to a branch named "staging". The staging branch then gets run against some extra regression/acceptance tests and whatever manual tests we may want. If they pass we merge to master, tag, and deploy.
Seems fine so far. Specifics about our tests and deployment process drive the Git workflow more than anything else.
*Run the test suite, test manually, whatever.
To be precise, small feature branches are perfectly fine. They are meant to be reviewed and squashed into master ("trunk") in a few hours to a few days maximum.
Squashing instead of merging has a big advantage of being able to quickly revert the changes if things go awry despite all best practices in place.
PS Avoid gitflow and similar workflows as they introduce leaps and thus risk. As everything in software engineering, you want your workflow to be as incremental as possible.
*In most cases, I think we should consider invalidating secret keys instead of trying to delete it and hope nobody saw it.
Are there secret keys in your app that you can't invalidate without a lot of pain? Maybe it is time to change some things in your app.
Maybe if someone accidentally commits secret-evil-plan.txt you might want to rewrite history? I can't think of any other scenarios... I mean even if someone commits a secret key, you're probably better off revoking it and leaving the secret key there. Or should you delete that file anyway to prevent people trying to brute force your old revoked private key?
Absolutely. AWS secret keys accidentally pushed to GitHub are abused within a few minutes. There's essentially no window where published keys remain safe.
Merge with --no-ff. You can revert easily, but you don't throw away the feature branch history.
Once a feature is merged, how is the history of a feature branch helpful?
Since I do not see/understand the benefits of retaining feature branch history, my preference is to squash commit and merge. Perhaps, this is a result of my habits that I carry from the ClearCase days, but would love to hear the downsides of this approach.
Consider the difference between:
* Well we need to revert this because it's causing problems, but now we've got one massive chunk of several day's work to pick through.
…vs:
* Well we need to revert this, but we can see that the problem was specifically commit xzy, so let's yank that, do it a different way, and merge the result.
Aside from that, it can be very useful to see the individual steps in implementing a feature for a variety of reasons. Given that a --no-ff merge achieves everything you are aiming for with the squash, why wouldn't you pick the more flexible approach?
Right now, I work on a smaller project, we use similar workflow - we haven't implemented feature flags yet, but I suppose we will in the future (the project hasn't been released yet actually). I personally don't like developing an entire feature in a separate branch and then merging when it's all over, because code review becomes tedious (and ineffective) and the risk of merge conflicts increases. I would suggest you invest time in writing tests, so you can become better at it (if you aren't already) and it becomes a natural part of your development. Same goes for feature flags :) I was a junior when I started working like this (I still am, kind of) and it meant the world for me :)
Only working, tested code should make it into master. Code reviews and automated tests are great tools for this, but ultimately, it's up to the individual developers to not be sloppy.
Avoid long-lived branches and big merges. If you've got big features that take a while to complete, break them into smaller things you can deploy sooner or use feature toggles.
Don't leave it too long between deployments. Small, frequent updates are far easier to manage and far less risky. A dozen deployments a day are better than a dozen a year.
Everybody should stay up to date with master. Rebase on top of master before merging instead of having awkward merge commits that only obfuscate history.
If you have people working on features that take multiple commits to implement, use feature branches. These should be rebased onto master and cleaned up before merging into master.
If you combine all of the above, you're heading down a path similar to GitLab Flow or GitHub Flow, so strongly consider using one of those instead of coming up with your own from scratch. Don't use Git Flow. It overcomplicates things and offers zero benefits over the alternatives. As far as I can see, it's only popular because a lot of inexperienced Git users were feeling like they were adrift without a workflow, and a blog post with a snappy title came along at just the right time to catch their attention.
If your team don't have good VCS habits, you need to do something about this. Small, frequent commits that do one thing and one thing only, good log messages, push and pull frequently, etc. If you can't describe the change briefly, you are doing too much in one commit. There's lots of best practices articles for Git that have been written – read them. Also read Pro Git, which is free on the Git website.