Daniel Allen Deutsch

Web Developer

Blog

On Breaking Changes, and Getting Older

April 4, 2016
What is a Breaking Change?

Let's build a contrived example to illustrate breaking changes.

We are managing a JSON API that has a user endpoint. This is a valid call to the API: http://danielallendeutsch.com/api/users/1.json. It will return this response:

{
  "email": "danielallendeutsch@gmail.com",
  "name": "Daniel Deutsch",
  "profile": "danielallendeutsch.com"
}

All is well in the world. Until, for whatever reason, we need to update our database schema. Instead of storing email, name, and profile, we need to store email, first_name, last_name and profile 1.

Okay! We update the database schema—and we are even clever enough to migrate all of the existing users2. But how do we handle our API? There are 3 options:

1. Backward Compatible: have the API return first_name, last_name, and name.

2. Versioning: create two APIs, one that supports the prior schema and one that supports the new schema.

3. Breaking Change: have the API return just first_name and last_name.

What Are the Implications?
Backward Compatible

There is nothing the speaks against delivering the new and the old at the same time. Because the names do not conflict, it is possible to serve everything. Sure, name no longer exists in the database. But I can write a function that joins first_name and last_name with a space.

Here is the new response:
{
  "email": "danielallendeutsch@gmail.com",
  "name": "Daniel Deutsch",
  "first_name": "Daniel",
  "last_name": "Deutsch",
  "profile": "danielallendeutsch.com"
}

Code that depends on the is API can access user['name'] and/or user['first_name']. In real life, you could remove name from the documentation and encourage the use of first_name and last_name. But any old code that depends on name would not break.

Pros: for people consuming the API, everything just works. Everything existing is still good, plus there are new features.

Cons: as the developer, you need to write more code. You are responsible for writing a function that wrangles the new schema into something it is not. Also—you are carrying around legacy code.

Versioning

It is possible to create two versions of the API. http://danielallendeutsch.com/api/v1/users/1.json and http://danielallendeutsch.com/api/v2/users/1.json are both valid endpoints.

This is what they return, v1 and v2 respectively:

{
  "email": "danielallendeutsch@gmail.com",
  "name": "Daniel Deutsch",
  "profile": "danielallendeutsch.com"
}
{
  "email": "danielallendeutsch@gmail.com",
  "first_name": "Daniel",
  "last_name": "Deutsch",
  "profile": "danielallendeutsch.com"
}

In this situation, existing code that depends on your API is already using v1. There are no changes to make for them. New things that are created that use this API will be built against v2.

The pros and cons are similar to above. Except consumers of the API can no longer mix-and-match features. Pros: it still works. Cons: maintaining legacy code.

Breaking Change

What if first_name and last_name is just better than name? And what if no one depends on the API except for you. Then you can say fuck it, and return the new stuff:

{
  "email": "danielallendeutsch@gmail.com",
  "first_name": "Daniel",
  "last_name": "Deutsch",
  "profile": "danielallendeutsch.com"
}

This will break anything that expects name to be returned. But it allows you to shed your legacy code. It is not fun to maintain old code for 5+ years.

You can get away with this if you can also change everything that depends on the API. Or if you don't care if you break those things.

Why Do We Care?

I think when we're younger, a true breaking change seems impossible. With every decision that is made, we only add to the options available. Everything can be fixed, and mistakes are reversible.

But as we get older, the stakes get higher. It becomes clear that breaking changes are possible—sometimes we break dependencies and there is no going back.

Worst of all, we learn that the choices we make have the real possibility of being bad/wrong. We tell each other that "everything happens for a reason" and "it wasn't meant to be" and "30 is the new 40". I think we say these things because if we didn't, we'd be paralysed by fear, indecision, and regret.

But here is what is true: 30 != 40, some decisions we make in life will make us less happy, worse off, than if we'd chosen differently.

I listened to a beautiful story recently. In it, a woman decides to give up her son for adoption. This is not backward compatible. She made what she thought was the best decision at the time, with the information she had. It is impossible for anyone to say, but in the simplest sense—it may have been the wrong decision.

There are some things in life that cannot be hurried3. Time and distance often afford perspective that would have been nice to have sooner.

So what is the right thing to do when we realize we made a mistake and would prefer to go back? Do we fight to revert the breaking change? Or do we learn to live with our new reality?

I have no answers. I do not know how to optimize for happiness.

Recently, I experienced a significant breaking change. I gave up on legacy. It was a mistake. I want my prior schema back. I love her and I miss her. She is my partner. I think my life will be worse.

With each passing year, the stakes get higher; the cost of indecision and reverting increases aggressively. I am nervous.

So what did we learn? 1. Use deprecation warnings. Before implementing a breaking change, do everything possible to make sure it's the right decision. 2. Life is tough, and moves only in one direction. Do whatever it takes to minimize mistakes.


1 The marketing team has determined that emails with "Hi Jane" have higher conversion rates then those with "Hi Jane Doe". I believe this is a reasonable use case.
2 This ignores the fact that this is a very difficult problem. In addition to the obvious edge cases of "Sue Ann" and "O'Connor", internationalization opens an even bigger can of worms.
3 A joke: a project manager says to his pregnant wife, "How long until the baby is here?" The wife says, "9 months. That's how long it takes." The husband says, "That's too long. What additional resources can I provide to cut the time in half?"

Have anything to say? Questions or feedback? Tweet at me @cmmn_nighthawk!