Recently, I’ve been reading a new book called Building Microservices: Designing Fine-Grained Systems, by Sam Newman. It’s a good book so far and this is my second article pertaining to the book. One of the things that the author discusses on page 30 is the importance of loose coupling between microservices.
When services are loosely coupled, a change to one service should not require a change to another. The whole point of a microservice is being able to make a change to one service and deploy it, without needing to change any other part of the system. This is really quite important.
IMO, the author’s statement is perfectly valid. But, in this high level book, the author doesn’t get into practical code level recommendations. As such, I thought that I would discuss some practical tactics for achieving truly independent deployments for the Java RESTful JSON microservice world.
- Client-Side Independence: Client side independence happens when you’re working on a client that calls the Java service and you can add new data to the service’s JSON request payload without actually changing anything on the server side beforehand. This is useful, especially when the two codebases involved are owned by two different teams. Perhaps the server side coders aren’t ready with their implementation yet. In order to achieve this type of independence, you really want to ensure that the JSON POJOs being deserialized on the server side make use of Jackson’s
@JsonIgnoreProperties
annotation (or the equivalent of whatever non-Jackson JSON library you are using). - Server-Side Independence: Server-Side independence is the complement of client-side independence. It happens when you’re working on a service that gets called by a client and you can add new functionality without worrying about the client causing exceptions by not sending the most up-to-date data. Like client-side independence, this is especially useful when the two codebases involved are owned by two different teams. In order to achieve this degree of freedom and loose coupling, you need to be able to gracefully handle
null
values in your requests. The client doesn’t send the new field? No problem, just ignore and move on. - Don’t Change Existing Inputs, Just Add New Ones: This recommendation is a corollary to #1 and #2. One of the cornerstones of achieving loose coupling is to ensure that you never change the meaning or type of existing inputs. Rather, you only add new inputs. Want to change the meaning of an existing variable in the JSON request? Just deprecate it and create a new variable. Alternatively, create a new version of your API, eventually ignoring the old variable completely and supporting both in the interim. Doing so doesn’t force your clients to change all at once. In fact, Unicode uses this principle to great effect. The Unicode consortium has guaranteed that none of the characters for any given number will change – ever. This policy is necessary because it means that you can always have confidence that when you code an
a
, that it will always mean ana
, and not change meaning to ab
when Trump decides to establish Minitrue. Similarly, your clients can have confidence that you will never change the meaning of their data they send to you unless they call a new versioned endpoint. - Flags to Turn On and Off Functionality: Lastly, as a fallback, you should consider adding a configuration flag that can enable and disable the new functionality. Ideally, with proper testing you shouldn’t need this. But, you should consider making use of such flags because they are fallbacks that can be readily used should you find an issue with your new functionality. Rollbacks are useful, but suppose you want to disable your functionality after another change has already been rolled out, or pushed to prod at the same time your own change went out. Unforeseeable issues make functionality flags a sensible tactic for achieving independent deployments between services, augmenting your systems’ overall robustness.
I hope that you, like me, can make use of these tactics for achieving independent deployments between services. Happy coding.