If you’ve been developing software for more than five years, you’ve probably seen this cycle before: New architecture emerges. Developers abandon previous best practices. Developers eventually realize that some of those “old-school” practices apply to the new architectural approach.

Microservices are no exception.

Some developers see microservices as a way to throw out established service-oriented architecture (SOA) approaches. However, in doing so, they are bringing back some of the complexity and redundancy that plagued us 15 to 20 years ago, which we have worked so hard to remove.

(Related: Microservices force companies to innovate)

Here are four particularly surefire ways to set your microservices up for failure. And for those who would like to avoid the headaches of the past, included are some modifications for reaping the benefits of microservices while keeping your software architecture agile, reliable, streamlined and high performance.

Skip deploying a central hub. The idea of adding a mediation gateway or enterprise service bus-like functionality into microservices themselves may be appealing, but it’s a certain strategy for adding layers of complexity and redundancy to your architecture. After all, most organizations will need microservices to connect both internally with their existing systems and externally with those of their partners. What could be more fun than having to modify a microservice every time one of those systems changes? Then there’s the added bonus of compromising security when services get pushed via APIs to the edge of the network for external access.

The smarter way to think of a microservices architecture is as a layered system in which the microservices interact with a mediation engine or ESB-like central hub to handle mediation, transformation, and other integration functions. There are several benefits to this approach: Changes to microservices will be minimal if most of them can occur in the mediation layer, and your architecture will be more agile if a change can occur once in the mediation layer and then be applied to all applicable microservices. Using an enterprise service bus (ESB) or similar mediation engine provides the necessary connectivity to incorporate legacy data and services into microservices, as well as for conducting a gradual migration. Equally important, a mediation layer allows the organization to maintain its own standards while partners in the ecosystem maintain theirs.

Ignore your dependencies. Not documenting what a microservice requires and depends on is the perfect way to complicate your composition scenarios and create chaos if a microservice crashes.

On the other hand, dependency management not only streamlines service composition, it also helps to wire microservices into a service broker or hub. This can be accomplished by having a service registry catalog everything to provide a holistic view and then have microservices communicate using a microservice architecture broker—essentially applying basic SOA concepts.

If there are dependencies that need to be managed, the registry-and-broker approach provides a mechanism for analyzing how other microservices will be affected. For instance, it can help to understand what will happen to dependent microservices if a particular microservice crashes, providing developers with the insights to build in recovery procedures. Similarly, this approach offers a way to address the versioning of microservices. Moreover, APIs for microservices with dependencies can be cataloged in the service broker to help ensure that they also continue to perform as needed.

APIs go hand-in-hand with microservices, so here is something else to consider.

Gloss over your APIs when configuring apps. Doing so will allow you to recreate the experience of poorly defined, monolithic services with zero communication. However, if you want to avoid going full retro, make sure your microservices architecture includes the creation of REST APIs that let you tailor application logic and resource models.

Stick to the fundamentals by implementing the microservice first and then exposing it via an API, which serves as the interface to other systems and services. The API, not the microservice, should be consumable. Typically APIs will be built using a REST approach, the popular, developer-friendly architecture for apps. However, be prepared to have APIs support other standards, such as the OASIS Message Queuing Telemetry Transport protocol, which offers lightweight messaging protocol widely adopted for connecting IoT devices, sensors and gateways.

Embed failover scenarios into your microservices code. Last but hardly least, isn’t assured uptime worth the added development and management complexity? Honestly, it’s not. Software architects and developers should only have to worry about the microservice itself, and not how scalability, availability and performance will be managed. Those should be managed by an underlying platform that also handles error detection, handling and prediction.

The advantage of a platform that supports containers is, when it detects a situation, it can quickly spin up another runtime instance of the microservice. Additionally, an analytical layer can capture data on the system layer in order to provide information at the container level to make decisions. In doing so, real-time and predictive notifications can enable containers and container-management systems to take the necessary actions before a failure occurs.

Microservices provide an effective way to break applications into smaller modules. But the best microservices are not islands. Instead, take advantage of common underlying functionality for mediation, governance, API management, and runtime reliability and performance. Equally important, they allow developers and software architects to focus on a microservice’s functionality instead of wasting cycles on how to keep it up to date and running.