Key Differences Between Backward and Forward Compatibility in Data Handling

Key Differences Between Backward and Forward Compatibility in Data Handling

In this blog post, I will explore the definitions of backward and forward compatibility, particularly from the perspective of data handling. Understanding these concepts is crucial for maintaining and evolving software systems without disrupting existing functionalities.

Backward compatibility: The newer version of code can handle data produced by older code.

Forward compatibility: The older version of code can handle data produced by the newer version of the code.

1) Backward compatibility demonstrated by practical example:

Let’s explore this idea through an example written in Java, where we store information about users of a website. Initially, only the name and the associated email address of each user are stored. Our data model:

Note: annotations provided by Lombok library are used to reduce boilerplate code, like getters, setters, etc.

However, over time, the business requirements prompt us to store more information about the users. For example, it would be beneficial to have a phone number stored for account recovery purposes. The updated user object now looks like the following:

Now let’s assume that a new release is running in production where every user object is expected to be successfully deserialized into the UpdatedUser class. As expected, we will have a significant amount of user data where users have not yet provided their phone numbers. The ObjectMapper of Jackson sets all those variables to their default values if they are primitives, or null if they happen to be objects, when the JSON string does not contain such data points which are expected according to the data model.

Content of the user.json file:{
"name": "Alice",
"email":"alice@gmail.com"
}

And this is what’s printed in the terminal:

Of course, if we had updated user information stored in our JSON file, then that would be reflected in the phoneNumber field:

Content of the user.json file:{
"name": "Alice",
"email": "alice@gmail.com",
"phoneNumber":"+4498765432"
}

And this is what’s printed in the terminal when we deserialize this file into our object:

2) Forward compatibility demonstrated by practical example:

In this scenario, we would like our older version of the code (or current code in production) to be able to deserialize updated user data (produced by an upcoming version of the code/ release) to our current data model. Although there are a couple of ways to do this, let’s explore implementing this by enabling our object mapper to handle this scenario.

But let’s see how the object mapper fails to deserialise our JSON before adding line 15 to our code: Exception in thread"main"com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "phoneNumber" (class com.example.User), not marked as ignorable (2 known properties: "name", "email"])

Here Jackson is complaining that it encountered an unknown property that is not specified in our current user data model. After having updated our object mapper with the line highlighted above, mapping the JSON string to the User object is successful:

To recap, the short examples above demonstrated the ideas of backward vs forward compatibility. I hope you found it somewhat useful and please share any constructive feedback you might have with me.