-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make non-null the default #63
Comments
This was the subject of considerable internal debate prior to the release of the spec, so it's definitely a discussion worth having, thanks for raising it! A few things came up when we were considering this that convinced us nullability by default was a reasonable call. Because the type system on a GraphQL server is application-specific, not feature-specific, it can be difficult to provide universally valid default values. For example, what's the right behavior if someone's profile picture fails to load? In some features, it might be to replace it with a default silhouette; in others it might be to omit the person from the list. The correct behavior in the presence of an error is dependent not on the field, but on the feature, and only the client knows about the feature. By making the field be nullable and providing an error indicating that we nulled this field out because of the error, the feature can handle it as it sees fit. Especially as queries and type systems grow, the probability that something will go wrong in the backend increases, and we need some way of handling that and expressing that in the query. The entirety of news feed for Facebook is powered by one GraphQL query, and there are a number of places where a backend system might (for any number of reasons) not be able to provide a result for a field. In a system where things are non-null by default, this could mean that a backend failure at a leaf node deep in the tree causes the entire query to fail. To be clear, though: while nullable fields is a feature of GraphQL (and one that we think is valuable), it's absolutely not a requirement of it. Non-null fields and nullable fields are both first class citizens of GraphQL. We had to choose one to be the default, though, and based on the above, we chose nullable. |
Just wanted to add my 2 cents. This topic also concerned me quite a bit , so I glad that @matthewcheok started this discussion. I definitely can see the advantages of nullable as a default. Backwards-compatibility and error-handling are good and valid arguments in favor of it. Still I feel that in many cases not-null default can be advantageous. In addition to @matthewcheok arguments (which I agree with), I also would like to point out these two aspects of it:
|
In addition to @dschafer's points, another fairly significant reason we chose to default to nullable values is the relative cost of changing from one to the other once you have deployed a schema and have shipped clients which are consuming that schema. Consider the case that non-null is the default, and you set up a schema for a comment discussion feature:
Then, you build Android and iOS apps, and launch them. A week later, you get bug reports that comments is not working for some users. You dig in and realize that if a user deleted their account, the comments they left behind are still there but there is no longer an Let's fix this by changing the schema so
This gets deployed and you check back in with your users who are reporting this issue. They should see comments now, just without the author, right? Nope. Your users are now reporting that when they load a comment view, their app crashes. What? Null-pointer exception. You were indexing directly into that If the default had been nullable, then your original schema as defined would have Just Worked™. For clients that understand nullability (Swift, recent Java and ObjC to a degree), they will warn you about accessing values without first checking for null. If you're checking null for something that you think should never be null, then you can have that conversation and go alter the schema. Perhaps you decide that there's always a message, an empty string can always suffice, so you change the schema to improve developer ergonomics on the client:
Now we've ended up at the same result, but via the incremental application of becoming more specific rather than the late realization that you were too specific from the start and had to add breaking changes to correct. In fact, this change is totally safe. Any client that was previously checking for null will continue to work just fine. This tale is slightly contrived, but we've suffered through versions of it at Facebook and it's a big part of why the default is nullable. As @dschafer pointed out, the probability that something will go wrong in the backend is more than 0 when loading data from storage, which is what GraphQL is designed to help you do. In practice (perhaps unfortunately), most people do not think about nullability when designing their type systems. It's why most languages with strong types have no concept of non-null vs nullable with the obvious exception of those inspired by functional programming. And of course nearly all dynamically typed languages have no concept of this either. If most people do not think about nullability, we wanted to define the one that required less typing to be the safer of the two options, and let the incremental application of knowledge to allow for improvements from that position. |
Many, but not the majority. I would contend that a non-null default would cause more confusion from programmers of more mainstream imperative languages and not used to thinking about this concept at all than a nullable default would cause confusion from those who use languages where they must think about nullability when defining types all the type because they have access to Option/Either/Maybe/?.
I strongly support this approach. Ultimately, the default for nullability is a moot-point when you have good GraphQL core libraries which respect the ergonomics of the particular language or environment. For strong typed languages that replace the concept of null with Maybe/Option/Either, I would expect to use a GraphQL library in exactly the way you define. |
Closing this task now. |
It looks like the real problem was lack of automated BC breaking detector. By making If you put "the floor is slippery" sign everywhere and even when floor is dry, you don't make your place safer. |
Just found this issue 9 years late looking for why this is the default. I agree with the above comment, that is a reasonable reason to mark a field null from the get-go. I think this does make sense for databases. By default data may not be available, but where you want to get an error you explicitly mark it as NOT NULL, basically it's a "constraint". In protobuf on the other hand you must specify I think graphql is an abstraction at the layer where protobuf is operating, not SQL. However, now that there is tooling on the language to generate schema in a code-first fashion the only annoyance is the default mismatch between the schema and the code. |
Thanks @alrz for resurecting this just in time for Halloween 🎃 🙂! I believe 9 years in, this issue might be worth reopening as the landscape has changed quite a bit. There's even a dedicated working group discussing this. Meeting is happening in 2.5hours if anyone's interested. |
I don't think we should re-open this issue specifically, but opening a new issue in the context of the semantic nullability discussions (i.e. semantically non-null by default) would be okay. |
Follow up issue there: #1122 |
I can understand why making types nullable by default might be a reasonable decision for growing systems adding new features but I think there are reasons why this might not be ideal:
This conflates the idea of nullability with providing reasonable default values. By allowing new features to be nullable by default, we push more responsibility to the application logic for determining what might be the right behavior.
Because making types nullable is so easy in practice, it promotes not considering if data should even be nullable or not. This could escalate the system's entropy more quickly making it hard to reason about the system's intended behaviour.
I'd love to hear other thoughts or ideas on this!
The text was updated successfully, but these errors were encountered: