Ask HN: Best Git workflow for small teams
What do you currently use for teams of 5-10 people?
What do you currently use for teams of 5-10 people?
- 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.)
- 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
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)
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.
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?
*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.
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.
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.
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...
git checkout master
git merge --squash some-feature-branchThe 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.
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...
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.
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 ?
- Branch off master to make your fix/feature. Prefix branch name with "fix/" or "feature/" accordingly. We occasionally use a "script/" or "migration/" prefix too.
- Do work. Use "git app -p" so commits are fairly small.
- Make GH PR into master, get code reviewed. CI test are run automatically.
- Deploy branch to staging environment and test.
- Merge with GH, deploy to production.
Notes: - master is always in sync with production, and so is always deployable.
- never commit to master.
- we don't use a develop branch.See for example https://www.thoughtworks.com/de/insights/blog/enabling-trunk... for a much more in-depth discussion.
Also, the Pro Git book: https://git-scm.com/book/en/v2
master ("production") branch:
- this is deployed
- nobody (not even GitHub Org administrators) can push; master is only changed via approved PR
dev branch:
- the base "working" branch during each 3-week sprint
- pushing is allowed iff the change is trivial or already has team consensus (e.g. changing linter rules)
- this is where we demo to our Product Owner
"story" branches:
- branched from dev
- named along the the lines of "alternate-checkout-TFF-75-story"
- base branch for any work that needs to be done for a user story
"task" branches:
- branched from the corresponding story branch
- (usually) named along the lines of "item-unavailable-TFF-92"
For all branches:
- CI must pass
- all PRs require >= 2 approvals, unless it is a trivial change then only 1
- require PRs when merging upstream (i.e. task -> story -> dev -> master)
- merge freely when going downstream (i.e. master -> dev -> story -> task)
- do not merge from story-A to story-B nor task-A to task-B
- squash commits when going task -> story and story -> dev
- do not squash when going dev -> master
Having stroy & task branches help keep the changes for each new feature in easily digestible chunks. The story branch is particularly helpful when two tasks need the same code, but the story, task-A, nor task-B branches are ready to go to dev. So we merge the shared code to the story branch, then the other task branch can merge down the required code from the story branch.
Currently, there is some contention over our verbose branch naming scheme. Previously, when 3/5 of the team was working on a different project, the branch naming scheme was "name-of-story-TSS-XX-story" for stories and "name-of-story-TSS-XX/name-of-task-TSS-XX".
There is only one long-lived branch in each of our repositories: master. All other branches in our GitHub repos exist only to perform a Pull Request/code review.
PRs must not be merged in to master until all unit tests are passing (the unit test suite is run for each new commit to a PR branch) and the change has been reviewed by another member of the team. In almost all circumstances the person who raised the PR is responsible for merging it. Once the PR has been merged in to master the GitHub branch is deleted.
Our CI server creates a new build for _every_ merge commit to master, automatically pushes this build to our integration environment, and then runs a series out automated integration tests on the system. If the build or automated tests fail then only PRs that fix the build are allowed to be merged in unless they are trivial.
Once our DiT is ready to perform a test, she promotes a build to the test environment and does her thing. Tickets that have been tested are then reviewed by a product owner and if they are happy the build gets promoted to the Live environment.
where we started 5 years ago at work we had various different kinds of 'develop' branches and process around merging between two different eternal branches. as we've scaled from a startup to a 200+ person company we've mostly simplified our github workflow and gone to single-eternal-master.
both work well I think for small teams just depends on how well you want to organize things.
1. Never commit to master
2. master is always deployable and gets built and deployed (CI/CD) automatically so its sacred
3. Everyone codes in their own feature branch (username/feature)
4. These are merged into a topic branch (origin/topic_feature) if there are more than 1 person working on a feature together
5. That topic branch is treated as a master - no force pushes, everyone rebases and merges from their personal branches
6. Topic branches are rebased with master periodically/sensibly and often times get built and deployed as beta for feature testing
7. Topic branches are merged into master
There is a code review process at each step.
Developers create merge requests for their personal feature branches into the topic branch and then merge requests for the topic branch into master for periodic code reviews.We don't force push and maintain history. Proper commit message formats are enforced to ensure that everything is standard and readable.
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.
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.
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.
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 :)
Also, what's your QA process and what is the usual quality of your builds? How R&D heavy are your new features, and how long do they typically take to develop? Are your programmers own different parts of the codebase, or is every one of them able to work on anything?
Without all this details it's impossible to know which workflow is ideal for your particular team.
What is meant by that is that what ends up in master should always be working, complete, tested code, not half-finished stuff. It doesn't mean that you need to deploy each and every commit to the public. It means that master is the canonical source of truth for the current state, disregarding tasks that are not yet complete. It means that there's a solid base for people to branch from without worrying about things that are temporarily broken.
What I normally do with iOS teams is have the CI tool produce a new internal build for every commit to master. That way everybody, including testers, have the most up to date version of the application readily available. That starts to fall apart if developers treat master as just the place to put work-in-progress stuff. It doesn't mean that we're submitting to Apple several times a day, it just means that we're sure what's in master is "done done".
I did that, but it turned out of little relevance to our QA process: new builds were happening once an hour, while QA wanted to get a version that they can run at least a full regression on, and it takes much more than that. So they ended up not using this CI builds at all, instead relying on a "release" branch version which they would get once every few days.
1- every dev works on a topic branch
2- always rebase before committing
3- have a jenkins running somewhere\
4- everything that gets merged into master gets automatically built and tested
5- everything that gets merged into -RELEASE gets automatically built, tested, packaged and deployed
6- Use issues from gitlab/github
7- Did I mention to always rebase before committing?
This isn't much and isn't exaustive, but it's a start.
Will you only have one version of the app released at a time, (websites) or multiple versions (native, mobile apps). Will you have just one team or multiple teams working on the same app at the same time?
Answer these questions first, then and only then should you seek for a workflow.
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
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!
Next to feature branches.
Jenkins for updating
Beta = production databases, but automatically copied, to avoid production errors ( lots of parameters to customize stuff)
No develop in master, just merging
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.