A view of the Earth, as seen from space at night, with cities lit up by electricity

Using GraphQL to ship features before they’re done

Joe Staller
Jul 15 · 4 min read

Heads up: I’m going to assume familiarity with common GraphQL terms on your part, like schemas and resolvers. If you need a refresher, I recommend checking out GraphQL.org

GraphQL represents a fundamental shift in how we developers can deliver features to end users faster than ever before. Having a decoupled schema, separate from both your client(s) and server(s) gives you a ton of flexibility when it comes to iterating on a contract for a feature. One useful technique that GraphQL enables is the zero-downtime migration, which I’ll attempt to illustrate with Data.

No, not data, Data, from Star Trek: The Next Generation.

Let’s imagine a schema we have for characters of the popular Sci-Fi show, which may look something like the following:

Which would allow us to query for characters like:

Which would yield:

Now, you’re probably wondering about that isSentient field. The episode “The Measure Of A Man” centres on if Data should be considered sentient. The ensuing episode revolves around figuring out if Data is sentient according to the definition given by Maddox:

  • Intelligence: the ability to learn, understand, and cope with new situations
  • Self-awareness: being conscious of one’s existence and actions or aware of one’s self and one’s ego
  • Consciousness

Our schema cannot support this new, complex definition of sentience! Our current schema exposes it as a Boolean, but (as Picard argues), Data fits some, but perhaps not all, of the conditions to be considered sentient.

I’m going to attempt to use this example to illustrate how we can take a schema-first approach to enable a zero-downtime migration from our current schema to something more complex.

For this example, I want you to imagine that we want to build out a more robust system that can determine if a character is sentient. Our existing system returns true/false, but we need to change it to return how a given character fits into any of Maddox’s given criteria for sentience. Building this system out fully may take a number of weeks, with the client having to wait until the server team is finished before they begin their implementation.

Leveraging the schema-first development style and some resolver magic, I want to illustrate how our teams could work on the client and server for this feature simultaneously, allowing either side to ship code to our users at a rate that works for them, without getting stuck on a dependency to another team. In this way, our client could be finished, running out in production, before the server team is “done” implementing the feature.

First, we’ll need to define the schema for our new rules we will use to judge sentience:

Okay, I’ve extended our Character type to have a new, more complex attribute defining their sentience, called the same, beyond a simple isSentient flag. sentience returns an array of SentienceCondition, which will (at the time of Data’s trial) be “Intelligence”, “Self-awareness”, and “Consciousness”. Our implementation can be hard-coded to return these three conditions, mapping isSentient to the satisfies attribute in our new schema, to maintain feature parity. A Javascript implementation of Character->sentience might look like the following:

Let’s update our operation that fetches Commander Riker’s data so we’re still on the same page:

Which now yields:

We can see our three SentienceConditions all have satisfies = true, as this is now an abstraction over the old isSentient flag.

This is huge! You can see that writing this thin abstraction over the current isSentient flag would not be much work server-side. However, this would allow any of our clients to start using our new sentience feature right away. If we have a schema registry set up with Apollo (for instance), client developers can use the Apollo GraphQL VSCode extension to update their operations/unit tests/prop types/Typescript types, all without leaving Visual Studio Code.

With a minor modification to our schema, and a quick hard-coded implementation based on our previous work, our clients can start consuming data in the new shape we want before we write a single line of code for our new feature! This helps us get around the waterfall-like nature of typical client/server development, where the server must be 100% ready before the client starts consuming from it.

Following the schema-first approach, we can unburden teams from heavy reliance on one another through an agreed-upon contract: our GraphQL schema. Once the schema is “live”, both teams can be reasonably confident it will be stable and can start coding towards it. In a zero-downtime migration scenario, we can write a thin abstraction over our old implementation, have clients implement the new schema, and remove the dependency that often exists between the two sides.

Oh, right, Data! Let’s see what the response for him looks like:

Yields… well, you’ll have to watch the episode!

Additional Resources

If you’re interested in joining our team, check out our open positions here!

Prodigy Engineering

Prodigy Education connects students, parents, teachers and…