Best practices for app development

Loosely coupled Microservices and API Gateways

Applications that run in the cloud must be built for global reach, scalability, and high availability and security.
It should be responsive and accessible to users across the world and be able to handle high traffic volumes reliably.

Manage your app’s code and environment

Store your apps code in a code repository. Use dependency managers. Don’t store external dependencies such as external packages in your repo. Separate your configuration settings from your code.

Consider microservices

Instead of implementing a monolithic app, think about implementing or refactoring to microservices. In a monolithic application the codebase becomes large and packages have tangled dependencies. In microservices the codebase is modular and each service can be independently updated, deployed and scaled.

Perform asynchronous operations

Remote operations can have unpredictable response times and can make your app seems slow. Keep the operations in the user thread at a minimum. Perform asynchronous calls between a FE service and the BE.

Design for loose coupling

Design so that your apps are loosely-coupled at runtime. Tightly coupled components can make your app less resilient to failures, spikes in traffic and changes to services. Intermediate components such as a Message Queue can be used to implement loose coupling, perform asynchronous processing and buffer requests in case of traffic spikes.

In the context of an HTTP API payload, the consumers should bind loosely with the publishers, using only the fields they really need to perform an operation. This lets the publisher evolve the API and add fields in a backwards compatible manner.

Implement stateless components for scalability

Implement app components so that they don’t store state internally or access a shared state. This would be a common bottleneck for scalability. Design each app so that it focuses on compute tasks only. This approach enables you to use a worker pattern to add or remove addition instances for scalability. App components should start up quickly to enable efficient scaling and shut down gracefully.

Cache content

It can improve apps performance and lower network latency. Cache application data that is frequently accessed or that is computationally intensive to calculate each day. In addition to caching app data in a cache such as Memcached or Redis, you can also use a CDN (content delivery network) to cache web pages.

API gateways

Implement API gateways to make BE functionality available to consumer applications.

If you have legacy applications that cannot be refactored, consider implementing APIs as a facade or adaptive layer being able to redirect between an old legacy BE and a newer one.


Security, reliability and migration

Use federated identity management

It’s better to implement it than designing your own log-in system. To delegate user authentication to external identity providers such as Google, Facebook, Twitter or GitHub minimizes the effort for user administration.

Implement health-check endpoints

It’s important to monitor the status of your app and services to ensure that they’re always available and performing optimally. The monitoring data can be used to automatically alert operation teams as soon as the system begins to fail. Implement a health check endpoint for each service. The handler should check the health of all dependencies and infrastructure components required for the service to function properly. Evaluate which dependencies are critical enough to result in a health check failure.

A health monitoring agent such as a load balancer can periodically send requests to the health-check endpoints.

Set up logging and monitor your apps performance

Treat your logs as event streams. Logs constitute a continuous stream of events that keep occurring as long as the app is running. Don’t manage log files in your app. Instead, write to an event streams such as standard out and let the underlying structure manage it for later analysis.

Handle transient and long-lasting errors gracefully

When accessing services and resources in a distributed system, applications need to be resilient to temporary and long-lasting errors. Resources can become unavailable due to transient network errors. Implement retry logic in this case with exponential backoff and fail gracefully if the errors persist.

Do not waste CPU cycles attempting the retry over and over. Implement a circuit breaker and handle the failure.

For errors that are propagated back to the user, consider degrading the application instead of explicitly displaying the error message.

Perform high availability testing and develop disaster recovery plans

Testing

  • identify failure scenarios
  • create disaster recovery plans (people, processes, tools)

Production

  • perform canary testing and blue / green deployments
  • validate your disaster recovery plan

Implement CI / CD

Automation helps you increase release velocity and reliability. With a robust pipeline you can test and roll out changes incrementally. This approach enables you to lower the risk of regressions, debug issues quickly and roll back easily to the last version.

In a CD system, a deployment system such as Spinnaker automatically triggers the deployment of builds to test environments. You can automatically execute integration, security, and performance tests and then deploy builds to your PROD environment. It’s important to consider security throughout the whole process. With a SecDevOps approach you can automate security checks.

Use the strangler pattern to re-architect applications

Strangler pattern: Incrementally replace components of the old application with new services. Consider it when re-architecting and migrating large applications.

In the early phases you might replace smaller components of the legacy application with newer app components or services. You can incrementally replace more features of the original app. A strangler facade can receive requests and direct them to the old app or the new services. As your implementation evolves, the legacy application is strangled by new services and is no longer required.ss