I have seen many systems in which application code is inextricably tied to the specific implementation of a core service API. Persistence APIs tend to be the most common culprits. This sort of sloppiness surely weakens a system over time. More often than not it guarantees the obsolescence of the system; the complexity and cost of rectifying a bad technology decision or moving away from an unsupported technology can be overwhelming in such circumstances. This nightmare is all too common.
The good news is that it is actually quite simple to avoid this fate. There are many ways to prevent application code from relying explicitly on the details of a service implementation. The approach presented here has the twin advantages of being reliable and not too fussy.
There are three components of the solution: 1) Physically separate the implementation code from the core application code; 2) arrange the project's build script to compile the core application code first, thus proving that no unwanted entanglements are lurking; and 3) wire in the specific implementation using a modern IOC container. (When I have the choice, I use Spring.)
To illustrate each of the three components in a little more detail, we'll use a simple example application. (The application is available for download. Visit this article.) Within the application is a persistence interface specified in DataManager.java. The application relies on this high-level interface to extract data from a datastore of some sort. The interface is implemented by a class cleverly named DataManagerImpl.java, and the implementation communicates with Excel to provide data to the application. Clearly we don't want any of the application code to refer to any supporting code within the implementation; no one in their right mind would want to commit to Excel as a datastore, and we want to be able to switch to, say, a standard RDBMS without touching any of the application code. The example is a bit extreme, but imagine an application that committed in some fundamental way to a persistence technology like EJB 1.0. If you've been around for a while, I'm sure you've encountered at least one such system.
Separating the implementation from the application code obviously makes everything easier, and I usually handle it as shown below. All of the application code, including the data-access interface, is kept in the src folder. The implementation of the data-access interface is stored under the plugins/excel folder — completely separated from the src folder. (It hardly warrants mentioning, but all classes supporting the implementation would be kept in the plugins/excel source tree.)

Next, when the project is built, compile the src tree first. Successfully running ant clean build.src establishes that there is no dependancy upon code living under plugins.

The last step is to simply declare which specific implementation of the DataManager interface should be used in the application. Here's a snippet from the example application's Spring configuration:

With this approach, experimenting with new technologies is relatively simple and safe: just implement the service interface and change the application configuration. There will never be any reason to touch application code if all one needs to do is switch the implementation technology. One could even carve out pieces of an interface and use different technologies for each without much trouble. Following these almost trivial steps can save a lot of time and money over the life of an application.