Taking off from what others have said, we have a rather rigid structure of progression from alpha, to beta, to production. The alpha code is whatever the head of the trunk is, and is kept stable for the most part, but not always. When we are ready to release, we create a "release branch" that effectively freezes that code, and only bug fixes are applied to it. (These are ported back into the trunk). Also, tags are periodically made as release candidates, and these are the beta versions. Once the code moves to production, the release branch is kept open for support, security, and bug-fixing, and minor versions are tagged and release off of this.
Once a particular version is no longer supported, we close the branch. This allows us to have a clear distinction of what bugs were fixed for what releases, and then they get moved into the trunk.
Major, long-term, or massive changes that will break the system for long periods of time are also given their own branch, but these are much shorter-lived, and don't have the word "release" in them.