May 10, 2022

Announcing `stellate check`: Prevent Breaking GraphQL Changes

Blog post's hero image

If the internet were truly a superhighway, one of the billboards would look like this:

EVOLVE-13 Keynote Roy Fielding 2022-04-26 at 2.33.09 PM (1)
EVOLVE-13 Keynote Roy Fielding 2022-04-26 at 2.33.09 PM (1)

Source: Keynote at the 2013 Adobe Evolve conference by none other than Roy Fielding, the originator of the REST architecture.

GraphQL was explicitly designed with this in mind. As the GraphQL best practices list says:

“GraphQL takes a strong opinion on avoiding versioning by providing the tools for the continuous evolution of a GraphQL schema.”

If you don’t have versions, how should you handle the breaking changes you’ll inevitably have to make to your API? The workflow that GraphQL recommends is:

  1. Introduce new type/field with breaking changes. Since clients explicitly specify what they request, no existing client will be fetching the new type/field.

  2. Mark the old type/field as deprecated

  3. Wait for all clients to update and fetch the new type/field instead of the old one

  4. Once none of your users are requesting the old type/field anymore, remove it from the schema

However, people have told us that they’ve accidentally broken their customers by removing a field they incorrectly thought was not in use anymore. For example, with mobile apps you have no control over when your update to the clients goes live.

Well, guess what, internet? We just built a tool to make sure this doesn’t happen to you automagically. ✨ Introducing stellate check:

$ stellate check
[error] Return type on field "Query.todos" changed, we've seen 483,759 requests with this type in the last 7d
-- todos: Todo!
++ todos: Todo
[error] Field on type Query removed, we've seen 583,571 requests with this field in the last 7d
-- todos: Todo!
[error] Input argument on field "Query.todos" removed, we've seen 5,824,434 requests with this input argument in the last 7d
-- todos(id: ID!): Todo!
[error] Input argument(s) on field "Query.todos" changed, we've seen 28,563,019 requests with this input argument in the last 7d
-- foo: String
++ foo: String!
[note] Type "Pineapple" removed, we've not seen a request with this type in the last 7d
-- type Pineapple {
-- id: ID!
-- }

Based on the data our GraphQL Analytics collect, stellate check verifies that any breaking change you make to your API will not affect any of your users by checking if any removed fields, types or arguments have been used recently.

Simply run this command in your CI/CD workflow and it will block breaking changes from being deployed that affect any of your users. Magic! ✨

sigh of relief

Let us tell you how we got here. This may sound familiar: It all started with a hackathon. (It’s true.) We were at a company-wide offsite & mapped out the problems our customers told us they face:

GraphQL Problem Space Mapping Exercise with the whole team
GraphQL Problem Space Mapping Exercise with the whole team

Our team found a quick win we could build with the data we already had:

The hackathon team hard at work building stellate check
The hackathon team hard at work building stellate check

We’re shipping a first version today. Run the command in your CI, prevent any breaking changes from impacting your users and let us know how it goes!