C# has made it possible to gradually roll out stricter nullability checking as well. The static analysis gets integrated into the regular language analyzers. Incremental migration is the only way to go.
In a similar fashion I was extremely impressed with how Dart approaches this as well and was able to move the entire ecosystem along in about 18 months. https://dart.dev/null-safety
It was a lot of work to get there and we're still not totally there yet. Dart still supports running legacy code that isn't null safe. But I'm really glad we did this and very grateful that Leaf and others on the team were able to design a null safety type system that:
* Defaults to non-nullable.
* Is fully sound.
The latter in particular is very nice because it means the compiler can take advantage of null safety to generate more efficient code, which isn't true in languages like Java, C#, and TypeScript where their null safety type rules deliberately have holes.
Yeah, I did a bit of Dart null safety migration work at Google and I thought they did a good job. (The auto migration tool sometimes made things nullable unnecessarily, and so you had to carefully review its output, but you could always just not use the tool.)
Some rare nice words from me about C# - the not-null facility is great. It's very basic propagation that should be better and I've fought the compiler too many times over it, but I really like the compile time guarantee it gives.
C# have taking it further. New projects give errors if you don't declare vars as nullable when they may be null in some context. It will help for static analyzers to find these cases. I see a big risk in it. That people will just init nonsense objects to get around the warnings.
> That people will just init nonsense objects to get around the warnings.
You technically can do "= null!", which means assign null by default, but assume it is not null. This is currently the recommended way to do deserialization, where you know the value is not null, but you have not explicitly filled it.
No, you get a nullable warning for that as well. You also need to change the reference type to nullable. I am convinced that some people will just init the properties with new(). And by that hide the null errors. Easily the hardest bugs to track down is when things fails long after the real error occur. I think they could have come with a better solution to the problem.
If you assign it with "= null", you get the warning. If you assign it with "= null!" (note the exclamation mark, the null-forgiving operator), you do not.
It sure can be a problem when people initialize stuff with nonsense to avoid running into nullable warnings. However, accidentally running into null pointer exceptions is still worse than people deliberately footgunning themselves with bad workarounds.
I feel C#'s nullable has helped me personally to avoid a lot of potential bugs and also changed the way I write code in a lot of places - like creating `bool Try...(..., out var)` style APIs instead of "old school" returns-null/throws style stuff, which I think make a lot of code cleaner and more easy to read.
Sometimes nullable can get a little messy and annoying, especially when retrofitting old code to make use of it without breaking existing APIs, and all in all the way C# does it is a clear net win in my opinion.
I think you have to separate nullable types from the global nullable directive. The global nullable directive will just make people return nonsense like the trend some years ago when people started to return empty lists instead of null.
C# has made it possible to gradually roll out stricter nullability checking as well. The static analysis gets integrated into the regular language analyzers. Incremental migration is the only way to go.