Our release process at ActBlue: git + github + jenkins + capistrano + peer code review => _almost_ continuous deployment.
Usually one person will work on a feature or a bug fix. Maybe two, and they'll collaborate using a branch via github. Maybe there'll be a cycle of review by the political team using a staging server we call 'playpen'. There will be requirements discovery, testcases, and code written. Our jenkins ci server is available to assist with testing. But that's not what we're talking about - what happens when it's declared "ready".
Once the developer is satisfied that a feature is ready to go, he'll push a new branch up to github (they're about free, right?) and submit a pull request.
Now this developer has to convince another developer that it's all good. (It's considered bad form to push the merge button on your own code.) Bribery isn't altogether out of the question, but more often this involves a lightweight code review, maybe talking through design and style points. We hope through this process that the reviewed feature and code and tests gets a second set of eyeballs, that'll maybe shake out flaws in design, or efficiency problems, or inadequate testing. Another benefit is that the reviewer is forced to look at some level at some code he might not otherwise, encouraging broader understanding of more parts of the application.
When the reviewer is satisfied, he pushes the merge button on the pull request in github. (And really should delete the branch ... this is a clunky part of the process... are you listening github?)
Merging to master communicates intention to ship the code or feature. Some teams are fully automated from there on, but we're a little more conservative (in the good sense). Some features feel safer to deploy during a quiet period, and some periods we like (for business reasons) to not deploy anything that isn't critical.
In very rare cases we have a database migration so radical that we need to hang a "down for maintenance" sign on the front door while we're doing it. This is pretty rare - we try to build migrations that can be run zero downtime, where we're confident that old code will be cool with new schema, for the minute or so it'll take the new code to spin up.
In general, however, an arrival of a new jenkins passed tag means a deploy is going out. Our capistrano setup takes the tag (it'll be something like 'jenkins_passed_123') and deploys that one tag. This is safety feature - we don't just deploy "master" in case something new is merged in that jenkins hasn't yet been able to approve.
After deploying, capistrano:
- sends email around to the whole team with release notes composed from the commit messages between the last production deploy and this one
- scans the commit messages for indications a bug was fixed, and marks those tickets as closed
- puts a git tag in the github repo marking the deploy
There are a couple of rough edges, maybe we'll fix them one day. When approving a pull request, we feel the branch should just go away. When a pull request is issued, it'd be nice if the tickets referenced in commits in that pull request were tagged as "in review", so stakeholders can see progress. And there's still a bit of typing and VPN wrangling involved in the deploy itself.
Still, the process we've got now feels like a great balance, with just the right amount of code review, and automated testing giving us the confidence to ship sometimes several times a day.