Application changes should be forward and backward compatible


Last Updated on Jan 16, 2021

Product success is shaped by its ability to evolve. The easier it is to change the product, the faster it can develop and keep pace with user and business demands.

Evolution is easy if one can restrict application changes to a specific part of the system, and any side-effects are limited to that section. When we change one aspect of the system, the rest of the system should remain unaffected and continue to work.

To put it in another way, the transition from one version to the next should be smooth - the changes introduced in the newer version of the system should be compatible with the older version.

Compatibility applies to code as well as data.

Changes in the system should be forward-compatible, meaning that the system should gracefully process input designed for later versions, ignoring new parts that it does not understand.

They should also be backward compatible, meaning that more recent versions should work with legacy data and services. Most software changes account for existing data and processes, so backward compatibility is a norm nowadays.

Only by ensuring this compatibility will systems components be upgradable in isolation.

Data endures forever.

The purpose of storing data in databases is to use it in some form in the future. In that sense, storing data in databases is like sending a message to oneself in the future. Application code may completely change during a product's lifecycle, but data created five years ago will still be in use along with data added yesterday.

Data compatibility is ensuring congruity between processes that write and read data, often at different points in time. Backward compatibility is necessary because newer code should be able to read older data.

Forward compatibility is necessary because there may be times when older code may need to read data created by the newer versions. Immediately after deployment, for example, both old and new versions of the code may simultaneously access the database.

Services need to be maintainable in isolation.

This kind of compatibility is also crucial between services that make up the product, for example, in a microservices architecture.

The point of decomposing a large application by functionality into smaller services is to make the application easier to change. Each service should be evolvable independently.

If changes made services incompatible with each other, newer versions of services might need to be deployed simultaneously, nullifying the architecture's benefits. Then one still has a big ball of mud on hand, now in microservices instead of a monolithic app.

Services should be able to gracefully handle calls from other processes that interacted earlier with the older version. When a service is upgraded, the rest of the system can behave as if the service has remained unchanged.

Conversely, a service should process new attributes sent as part of the response to a call to another service and ignore elements that it does not understand.

In general, changes that are forward and backward compatible increase a system's stability and maintainability.


© 2022 Ambitious Systems. All Rights Reserved.