After reading this post, we’ve decided to adopt the approach suggested by Steve Losh.
Basically, the tip of the default branch is the last release of code. A tag will be created each time a release is done.
When development begins on a new release, a branch is created. Once dev is done on a release, it is rolled into default.
While development is taking place, changes to a previous release can be merged into the upcoming release. Theoretically, changes on the previous release will be bug fixes, so will want on next release as well.