All is not lost, though. There are still things you can do to future proof your software, and decoupling components may be one of the most powerful. But decoupling code is often not enough, you need to decouple ideas.
Imagine you are developing a .NET website in C# with dynamic data coming from multiple different sources. One of your sources is a Content Management System (CMS) where authors and editors can update some of the web pages in production without any developer interaction. The CMS system works by providing the authors an editors with an interface friendly to them. It stores their work in database tables, which your .NET application can access.
In this particular application the CMS system has two types of pages "Generic Pages" and "Articles". The Articles database table has columns like Content and Title which contain HTML data and NumberOfWords which is an integer. The GenericPages database table has columns: Body, DisplayTitle, and SidebarContent, which also contain HTML data.
An obvious approach to solving this problem is to create classes GenericPage and Article which know how to read from the database and have accessors for the various fields. You probably also want a CMSHandler class which has methods for taking a unique identifier (e.g. relative path) and returning the appropriate GenericPage or Article. Your website code access these GenericPages and Articles via the CMSHandler.
These CMS classes that you wrote seem like they should be decoupled from the rest of the application. It doesn't seem farfetched that the CMS system will change. Maybe the company providing it will be bought by Microsoft. Maybe you'll decide to write an in-house system. Who knows?
So with this thought in mind you create interfaces IGenericPage, IArticle, and ICMSHandler. Now the rest of your code base is coupled to the interface, rather than the implementation. i.e. your website code access an IGenericPage or an IArticle via an ICMSHandler.
Success! ... Right? ... Maybe not.
Fast forward two years. Your CMS provider is bought by Microsoft, and you decide to go with a cheaper competitor. However, the new system is a little bit different. Besides GenericPages and Articles, it also has FAQPages. And the Articles table in the database doesn't have a NumberOfWord columns.
While you did decouple your code base from the CMS classes, you didn't actually decouple your code from the CMS system and all its assumptions. Now that you have a new system, you realize it doesn't work the same way as the old system. Your code base has to change anyway to handle the new system.
Decouple the Idea
So what's the solution? Decouple your code from the assumption in the system you are using, not just the classes. Do you really need to handle GenericPages and Articles differently? Or are you just going to show the pages the same, but not show sidebars for the Articles? Do you really need a word count? Rather than looking at the features that are provided, consider the features you really need. Write your interface classes to provide those features. This may mean the interface hides some of the functionality that could be provided by the underlying mechanism. It also means that you may have to add some functionality that wasn't there. (for example, you decide you really do need a word count, even if it isn't provided by the CMS system).
In this example it means that your website accesses an ICMSPage via an ICMSHandler. The fact that there are different types of pages like GenericPages or Articles is hidden from your website, since the website doesn't need to know this.
If you have successfully decoupled the assumptions of your code base from the assumptions of the CMS system, then you shouldn't have trouble replacing the CMS system. Your interface should represent the minimal set of assumptions that your application makes. Any CMS system that satisfies these assumptions should be easy to drop in. Or at least much easier than if your decoupling mechanism still allowed all the assumptions of the CMS system to leak through to the website itself.