Good practices for handling dependency changes are the same as good practices for application design. You want to layer your architecture and minimize widespread coupling of your code on each dependency in order to keep changes isolated, so that upgrading a dependency doesn't break every part of your application. Code to interfaces and keep business logic separate from infrastructure code.
For minor upgrades (upgrading point releases of a dependency), it helps if you have a comprehensive set of unit tests to detect failures due to API changes. This is one big reason why it sometimes helps to write trivial tests that seem on the surface to always work. An example of this is writing a simple JDBC unit test to perform a simple query. This seems like a waste of effort until you catch a runtime problem with the JDBC driver after a database upgrade (it's happened to me).
For larger changes (like upgrading between incompatible versions of a framework like Spring), it helps if you have some automated functional or integration tests, or at least have a functional specification that your QA people can run through to verify high level functionality. Unit tests will likely no longer be relevant if the framework API you are upgrading is different enough to require broad code changes.
The actual tactical part of managing a migration from one version of a dependency to another incompatible one really depends on what you are doing. A mature library will provide some kind of migration path and hopefully won't require you to rewrite everything. It is a good idea to separate the code changes related to a framework upgrade from the changes related to implementing a new feature. This way if something breaks you will know it has to do with the framework upgrade and not something you broke while implementing a new feature.
Part of what makes this so difficult is that at runtime you can only have one version of a particular dependency in your JVM, so you have to update all of the code at once. OSGi addresses this particular problem by allowing different OSGi bundles running in the same to depend on different dependency versions, so you could depend on different dependency versions at runtime. This is how Eclipse manages dependencies for plugins without breaking other plugins.