Symfony 2.8 and 3.0, released today, both at once, on purpose. The pairing is the whole message. 2.8 is the last of the 2.x line and an LTS. 3.0 is roughly 2.8 with the deprecated code deleted. Same features, cleaner body.
This turns a scary major upgrade into two steps.
Step one, move to 2.8. Minor upgrade, low risk. Then make the deprecation log empty. The phpunit bridge prints every deprecated call your code and tests touch. Burn the list down during normal sprints, item by item, each fix small and releasable on its own.
Step two, when the log is silent, change the constraint to 3.0. If step one was honest, step two is an afternoon.
Compare with the old style of major migration: one heroic branch that lives for two months, diverges from everything, and merges as a single terrifying diff. I have owned such a branch. I do not want another.
One discipline makes or breaks this. Do not refactor business code in the same commits. During migration you see ugly code all day, and the temptation to fix it while you are here is strong. Resist. A migration commit should answer one question in review: does this change behavior? No. A refactoring commit answers a different question. Mix them and the reviewer can answer neither, and when something breaks in production you cannot tell which intention broke it.
The framework tells you the future years in advance and lets you walk there in small steps. All you have to do is read the warnings. In most projects I have seen, that is the hard part.