To elaborate a bit: That's usually called "Trunk based development", and it works pretty well in small teams (at least that's where I have experience with it), and if you have automated test, and infrastructure that runs them automatically.
This is a great page that shows a few different approaches. I like the GitHub flow but the GitLab one isn't too different and both would be appropriate for a small team like OP's :).
That's an implementation detail. Most people think of git history as more of a stream, forking and merging while moving forward. In this context, the arrows pointing in the direction of chronology is perfectly sensible.
> 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.
For 5-10 people it depends on what everyone is doing. I typically push some code out in the wild (master) for stuff that wont change and do releases or branching for incremental updates and patches. Git Branching + Git Releases should help you out mostly also bonus if you are using github throw in some good 'ol kanaban action using projects.
I had a lot of success with a master branch, all features built in feature branches.
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.
I would strongly recommend avoiding a rebase-based strategy. When developers at my company run into git issues, they are almost always the result of a bad or confusing rebase.
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.
Can you go more in depth with this process on how rebasing things makes it easier to track down bugs / breaking changes? Usually you can track down which feature branch is the one that cause a regression in your code base, and you can also look at specific commits to find out where problems happen. I can't tell how a rebasing flow would make this any easier?
It sounds like you've had people rebasing some public commit which has then been branched from. That's asking for trouble.
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.
GitHub flow is the ideal way to go IMO. It is simple, easy to understand, and widely used.
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.
There is the philosophical discussion, if you should rebase or not. If your team comes from CVS there is probably no bias yet. Pick for yourself if you prefer a clean or an accurate history.
Master, dev, feature and release. Mostly always in fact.
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
We've been using the Github flow (or some variation of it) in teams of 2-10 people for a few years now. We work on feature branches, merge after code review using the github web UI. Here's a few things that help us:
- 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.
+1 for master always deployable. In teams I've led, I've made it a rule to never commit to master, and sparingly commit to develop.
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.)
To prevent confusion I think it is better to merge feature branches into master and not have a development branch. Master should always be deployable but if you're not practising continuous deployment you can merge into the production branch to do so. This is further detailed in GitLab Flow http://doc.gitlab.com/ee/workflow/gitlab_flow.html
> Enable branch protection for master, so that only pull request that are green on CI and have been reviewed can be merged.
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.
Since recently, yes... But for some reason they decided to make it part of "BitBucket Premium"[1] which is garbage. These are absolutely essential features, and their pricing overhaul hides this behind $3/month/per user. And it also seems that the unlimited users for $200/mo plan is also gone now. So their pricing overhaul has made things more expensive for anyone over 100 users, and more than doubled the price if you want these essential features (merge checks, I understand charging extra for cloud mirroring).
Yeah we have the same approach, but with one difference. Everyone has their own fork of the repo (The reason for this is we had a few avoidable conflict issues).
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)
I generally like GitHub Flow (https://guides.github.com/introduction/flow/) because it gives you isolation for individuals, but with only one ceremonial branch (ie. master). It might not be sufficient if you have formal release and QA cycles where you need to batch a bunch of changes to push to a staging environment, but all else being equal I prefer to optimize for branching, merging and deploying to production quickly. And with current cloud and/or containerized setups, it becomes possible to have on-demand staging environments (or even using ngrok with dev workstations!), but YMMV.
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.
We use what is basically the "GitHub" flow... forks and PRs on an instance of GitHub enterprise.
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.
Paul Hammant has some interesting blogs about the topic and he has been talking and promoting Trunk Based Development for a while. We use a variation of it at work, though we have several teams of ~5 working on the same repository.
If most know CVS already just use it. The burden of switching a tool and learning a new one is not worth it if the old one is good enough. I'd just set up a CC list for patch reviews, put a TODO file in the repo and go on with whatever VCS my team is used to using.
Everyone says what Git branch workflow should you use, but honestly, it can be complicated especially for junior developers.
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!
Just make sure to rebase every time you start work to avoid merge conflicts, but yes, this is the single most effective git workflow for small and probably larger teams too! I'm in a small team where the lead dev comes from a top SV company and he introduced this workflow and it works great.
We use to do this until someone added a simple bug fix to master and someone added a new feature that wasn't ready to go out yet to master and then master got pulled to update the website and the users were wondering what the hell these new features were that were untested and broken.
We use develop branch as a pre-master. All feature branches get pushed to origin and pull requests created to merge feature branches to develop from there. Once a sprint is finished we merge to master and deploy to staging. Staging gets promoted to production using pipelines. As we are in early stages the develop branch is good but I can see us dropping it and using only master once we start doing daily releases.
Quarterly website release cycle with 3-5 developers. usually one liner bug fixes are the only thing pushed to master without a branch as in master -> master. Everything else is a branch / independent feature each developer is working on and we pull them all into a staging copy of master to pretest Which makes it easy to just delete the staging if we want to repull a feature or two and scrap what other features we pulled, once staging is well tested we merge with master on bitbucket as the new release.
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 :)
Like others are saying here, the branch named "master" is always deployable.
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.
Continuous integration AKA trunk based development is the workflow you want to have. Ensure you meet the prerequisites first though (reliable automated tests, solid code reviews, feature flags, etc).
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.
But a policy of squashing prevents two people from working on the same feature (you shouldn't squash what you've already pushed as this changes history).
*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?
Depends on what is "public" history. Is a branch where you working on alone public, or does it become public after merging in back to master.
Squashing a branch before merging back depends a lot of the discipline of the commiter. So for a branch with commits like "add FeatureX", "arg forgot this edge case", "ups another fix", "finally it works" it makes a lot of sense to squash it. While for "add FeatureX", "fix some linter errors during development" it makes then to leave the history as it is.
> *In most cases, I think we should consider invalidating secret keys instead of trying to delete it and hope nobody saw it.
Absolutely. AWS secret keys accidentally pushed to GitHub are abused within a few minutes. There's essentially no window where published keys remain safe.
Code review is read only. Pair programming is at the same machine. Apart from that the features should be ultra-thin and delivered vertically end-to-end by a single programmer. That is the optimal model.
> 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.
Well given that we're talking specifically about reverting the merge when something goes awry, firstly you want that history preserved so that you can re-start work on it, potentially without pulling in all of the changes in one go; and secondly so that you can determine the precise point at which the problem happened.
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?
- 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.
At my previous team (5-6 people worked simultaneously) we used CI & CD and we relied on feature flags, so we could merge our branches more often and make smaller pull requests on Github. Each part of the feature had its own branch with a clear goal and tests written for it (we never pushed to master, every time you start working on something, you create a branch prefixed with your initials, make a PR, merge after review and then do it all over again). Of course, master branch was always deployable.
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 :)
Your master branch really should be your master branch. A branch that represents the most up to date working version you have, that everybody works in relation to.
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.
I think that a lot of people here make assumptions about the project that are not necessarily true. For example "everyone can deploy from master at anytime" is a great rule for a website that can actually be deployed by one click of a button, but a very bad practice for an iOS app, each new version of which can, theoretically, spend up to two weeks in review.
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.
> For example "everyone can deploy from master at anytime" is a great rule for a website that can actually be deployed by one click of a button, but a very bad practice for an iOS app, each new version of which can, theoretically, spend up to two weeks in review.
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".
> 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.
My team comprises 4-5 Software Engineers and 1 Developer-in-Test. We develop three (related) websites + a bunch of back-end services.
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.
I'm going to say something not popular but I miss SVN when working with teams. Git is great for distributed open source work but it sucks at being centralized/mono repo . SVN made it easy for instance to lock some parts of a mono repo or to even manage who can access to what. I don't get tall that for free with Git.
For my senior design team of 5 developers using Scrum and JIRA:
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".
You need to figure out your release cycle first, before creating/picking a workflow.
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.
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.
What do you currently use for teams of 5-10 people?