When writing software you will end up using frameworks and libraries to make your life easier. Even if you don’t use any frameworks or libraries you still have the language itself that can evolve over time. A nice example of this is the recent release of Java 9 which did introduce some changes that might break your software.
All of these dependencies could become a hell if you are too tight coupled to them, as they restrict you from upgrading or changing when required. It is essential that you take this into account when writing software but as always it is easier said than done.
It makes sense that before you agree upon selecting a framework or library that you do some assessment on how well it fits your needs, how easy it is to use and how much it costs. While it is nearly impossible to predict how your needs will change in the future, it is good practice to try to predict it based on the vision of your software.
After making a decision not everything is lost, on the contrary. Depending on how big and how many features the framework or library provides you may want to invest a lot effort in making sure your code is as loosely coupled as possible. However, it may be overkill to do this for very small and simple libraries or frameworks, especially if they are very unlikely to ever need to be updated or replaced.
Another thing to evaluate is how easy it would be to keep existing code running with the old version, but start writing new code using the new one. While this may pose a strain on your maintenance cost, it can provide a transition period where otherwise you would have to choose between keep on using the old version or completely change all of the existing code to use the new one.
A concrete example of this, one that many people don’t realise is the usage of a test framework such as jUnit. While there are other frameworks, the choose of framework will greatly depend on personal preference, because of this it is unlikely that you will switch to a different framework. Upgrading to a newer version is however much more likely. A test framework is also used often in many different places. So do we want to loosely couple our code from jUnit to allow for changes in the future? So far a new version of jUnit was always possible to work besides an older version making the before mentioned practice possible. Writing a layer in between your code to hide the usage of jUnit may be very costly, it may greatly decrease the usability of the library and may even obfuscate your code, especially for new people who are used to jUnit but not to the special written layer.
The case of jUnit may be a special one, but it indicates that such libraries and frameworks exist. Generally you want to have such a tight coupling and introduce an intermediate layer just to abstract this away. Often you also do this to improve the readability of the code, as it allows you to create a more specific interface which matches your domain more nicely.
In the end the situation we want to prevent is that you are stuck with a certain framework or library that is hard to use, is outdated or does not satisfy your needs anymore but you have to keep using it because you can’t switch to a different one because of the big pile of legacy code that uses it. In such a case it is easy to just keep on doing things they way they have been done in the past, but it might be better to swallow the bitter pill and finally clean it up and prepare for the future. This will be a great cost, but normally just postponing the work won’t make it go away, on the contrary it will only make it harder to do as more and more boilerplate was written to match the requirements with the provided features.