Last week, in The Power of REST – Part 1, I challenged someone’s proposal that their client-side query language could supplant the power of REST. It seemed to attack strawman arguments about REST. In this article, I wanted to delve a little more into what REST does and why it does it.
Basis of REST
REST was created to take the concepts that made the web successful, into API development. The success of the web, which some people don’t realize, can be summarized like this: “if the web page is updated, does the browser need an update?”
When we use RCP-oriented toolkits with high specificity, one change can trigger a forced update to all parties. The clinical term for this is “brittle“. And people hate brittleness. When updates are being made, resources are no longer available.
Let’s take a quick peek at the banking industry. Despite what you think, the banking industry isn’t built up on transactions and ACID (Atomicity/Consistency/Isolation/Durability). Nope. It’s built on BASE (Basic Availability/Soft-state/Eventual consistency). An ATM machine can be disconnected from the home office, yet it will dispense cash. You can go over your limit, and still get money. But when things are made consistent, it’s you that will be paying the cost of overdrawing, not the bank.
When it comes to e-commerce, downtimes of hours/minutes/seconds can translate into millions or billions of lost dollars. (Hello, Amazon!)
Updating ALL the clients because you want to split your API’s “name” field into “firstName” and “lastName” will be nixed by management until you A) show that it doesn’t impact business or B) prove the downtime to upgrade won’t cause any loss of revenue.
Evolving an API
To evolve an API, we need to reduce breakages. We need to be able to make changes to the API that WILL NOT IMPACT existing clients. Changes that allow existing clients to keep right on humming as if nothing has happened.
Eventually, they can catch up and take advantage of the new features. But only when they’re good and ready. And SOAP and CORBA were not built for this. Their definition languages (WSDL and IDL) don’t know how to be “flexible”.
But REST can. How? Let’s start with that example mentioned earlier, a resource that serves up a name. Perhaps a small piece of some e-commerce platform. You design this:
When an instance of this domain object is turned into a hypermedia-based JSON structure (through a controller we can imagine), it looks like this:
This nice bit of JSON flies around the system. You build powerful clients leveraging its vast data. Customer growth is exponential.
But suddenly, we’re victims of our own success. Your initial take on a customer representation was kind of scratched together. And now your manager darkens your door, saying, “We need first and last name. Can you do that?”
You nod and get cracking. Just need to replace name with firstName and lastName, and update all the clients. Except what you just said will incur downtime. No, you need something a little smoother. Something that can roll out and not impact the existing clients. Instead of “versioning” your API, why not ADD TO your existing resource?
Ta-dah! Your updated domain object moves from having a single name to a first and last name, as requested. But that is not all. It also can generate that old name field, using the new data. And it can parse an incoming “name” field, turning it into your new ones.
What does this look like in JSON?
You are now sporting both the new fields AND the original one. Old clients, if they follow the conventions of REST, will simply ignore the new stuff and use the old stuff. If they need to POST an update, they know where to send it and can send just the fields they know. Your code can handle this!
Of course, you’ll have to migrate your data store before rolling this out. But again, you can maintain availability with just a LITTLE extra effort.
Honor the Robustness Principle
Be conservative in what you do, be liberal in what you accept from others.
This statement is known as the Robustness Principle. It’s what we accomplish using @JsonIgnoreProperties(ignoreUnknown = true). That annotation tells Jackson to NOT blow up when we receive an unrecognized property. Old servers can accept the new format, until they have a chance to catch up. And old clients can also talk to new servers thanks to our customized setName(wholeName) method.
By carrying around this extra sliver of information (two copies of a person’s name instead of one), we can save millions of dollars when used at scale.
And this is a core piece of REST. By avoiding antiquated concepts like versioned APIs (a requirement for CORBA and SOAP), and instead making our REST resources backwards compatible, we not only reduce downtime, we can make maintenance easier on ourselves.
If you enjoyed this, stay tuned for next week’s post, The Power of REST – Part 3, where we will dig into hypermedia.