{"id":19687,"date":"2022-11-22T06:00:11","date_gmt":"2022-11-22T14:00:11","guid":{"rendered":"https:\/\/engineering.fb.com\/?p=19687"},"modified":"2022-11-18T14:05:19","modified_gmt":"2022-11-18T22:05:19","slug":"meta-java-nullsafe","status":"publish","type":"post","link":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/","title":{"rendered":"Retrofitting null-safety onto Java at Meta"},"content":{"rendered":"<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">We developed a new static analysis tool called Nullsafe that is used at Meta to detect NullPointerException (NPE) errors in Java code.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Interoperability with legacy code and gradual deployment model were key to Nullsafe\u2019s wide adoption and allowed us to recover some null-safety properties in the context of an otherwise null-unsafe language in a multimillion-line codebase.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Nullsafe has helped significantly reduce the overall number of NPE errors and improved developers\u2019 productivity. This shows the value of static analysis in solving real-world problems at scale.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Null dereferencing is a common type of programming error in Java. On Android, <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">NullPointerException<\/span><span style=\"font-weight: 400;\"> (NPE) errors are the <\/span><a href=\"https:\/\/developer.android.com\/games\/optimize\/crash#prevent-crashes-null-pointer\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">largest cause of app crashes on Google Play<\/span><\/a><span style=\"font-weight: 400;\">. Since Java doesn\u2019t provide tools to express and check nullness invariants, developers have to rely on testing and dynamic analysis to improve reliability of their code. These techniques are essential but have their own limitations in terms of time-to-signal and coverage.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">In 2019, we started a project called <\/span><span style=\"font-weight: 400;\">0NPE<\/span><span style=\"font-weight: 400;\"> with the goal of addressing this challenge within our apps and significantly improving null-safety of Java code through static analysis.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Over the course of two years, we developed Nullsafe, a static analyzer for detecting NPE errors in Java, integrated it into the core developer workflow, and ran a large-scale code transformation to make many million lines of Java code Nullsafe-compliant.<\/span><\/p>\n<figure id=\"attachment_19860\" aria-describedby=\"caption-attachment-19860\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-19860\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-1-revised.png?w=1024\" alt=\"nullsafe\" width=\"1024\" height=\"552\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-1-revised.png 1484w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-1-revised.png?resize=916,494 916w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-1-revised.png?resize=768,414 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-1-revised.png?resize=1024,552 1024w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-1-revised.png?resize=96,52 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-1-revised.png?resize=192,104 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><figcaption id=\"caption-attachment-19860\" class=\"wp-caption-text\">Figure 1: Percent null-safe code over time (approx.).<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">Taking <a href=\"https:\/\/engineering.fb.com\/2022\/11\/04\/web\/instagram-video-processing-encoding-reduction\/\" target=\"_blank\" rel=\"noopener\">Instagram<\/a>, one of Meta\u2019s largest Android apps, as an example, we observed a 27 percent reduction in production NPE crashes during the 18 months of code transformation. Moreover, NPEs are no longer a leading cause of crashes in both alpha and beta channels, which is a direct reflection of improved developer experience and development velocity.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">The problem of <\/span><span style=\"font-weight: 400;\">null<\/span><span style=\"font-weight: 400;\">s<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Null pointers are notorious for causing bugs in programs. Even in a tiny snippet of code like the one below, things can go wrong in a number of ways:<\/span><\/p>\n<p><i><span style=\"font-weight: 400;\">Listing 1<\/span><\/i><span style=\"font-weight: 400;\">: <\/span><i><span style=\"font-weight: 400;\">buggy <\/span><\/i><span style=\"font-weight: 400; font-family: 'courier new', courier;\">getParentName<\/span><i><span style=\"font-weight: 400;\"> method<\/span><\/i><\/p>\n<pre class=\"line-numbers\"><code class=\"language-java\">Path getParentName(Path path) {\r\n  return path.getParent().getFileName();\r\n}\r\n<\/code><\/pre>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-family: 'courier new', courier;\"><span style=\"font-weight: 400;\">getParent<\/span><span style=\"font-weight: 400;\">()<\/span><\/span><span style=\"font-weight: 400;\"> may produce <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">null<\/span><span style=\"font-weight: 400;\"> and cause a <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">NullPointerException <\/span><b>locally<\/b><span style=\"font-weight: 400;\"> in <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">getParentName(&#8230;)<\/span><span style=\"font-weight: 400;\">.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-family: 'courier new', courier;\"><span style=\"font-weight: 400;\">getFileName<\/span><span style=\"font-weight: 400;\">()<\/span><\/span><span style=\"font-weight: 400;\"> may return <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">null<\/span><span style=\"font-weight: 400;\"> which may propagate further and cause a crash in some other place.<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">The former is relatively easy to spot and debug, but the latter may prove challenging \u2014 especially as the codebase grows and evolves.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Figuring out nullness of values and spotting potential problems is easy in toy examples like the one above, but it becomes extremely hard at the scale of millions of lines of code. Then adding thousands of code changes a day makes it impossible to manually ensure that no single change leads to a <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">NullPointerException<\/span><span style=\"font-weight: 400;\"> in some other component. As a result, users suffer from crashes and application developers need to spend an inordinate amount of mental energy tracking nullness of values.<\/span><\/p>\n<blockquote class=\"blockquote\"><p><span style=\"font-weight: 400;\">The problem, however, is not the <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">null<\/span><span style=\"font-weight: 400;\"> value itself but rather the lack of explicit nullness information in APIs and lack of tooling to validate that the code properly handles nullness.<\/span><\/p><\/blockquote>\n<h3><span style=\"font-weight: 400;\">Java and nullness<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">In response to these challenges Java 8 introduced <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">java.util.Optional&lt;T&gt;<\/span><span style=\"font-weight: 400;\"> class. But its performance impact and legacy API compatibility issues meant that <\/span><span style=\"font-weight: 400;\">Optional<\/span><span style=\"font-weight: 400;\"> could not be used as a general-purpose substitute for nullable references.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">At the same time, annotations have been used with success as a language extension point. In particular, adding annotations such as <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">@Nullable<\/span><span style=\"font-weight: 400;\"> and <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">@NotNull<\/span><span style=\"font-weight: 400;\"> to regular nullable reference types is a viable way to extend Java\u2019s types with explicit nullness while avoiding the downsides of <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">Optional<\/span><span style=\"font-weight: 400;\">. However, this approach requires an external checker.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">An annotated version of the code from <\/span><i><span style=\"font-weight: 400;\">Listing 1 <\/span><\/i><span style=\"font-weight: 400;\">might look like this:<\/span><\/p>\n<p><i><span style=\"font-weight: 400;\">Listing 2<\/span><\/i><span style=\"font-weight: 400;\">: <\/span><i><span style=\"font-weight: 400;\">correct and annotated <\/span><\/i><span style=\"font-weight: 400; font-family: 'courier new', courier;\">getParentName<\/span><i><span style=\"font-weight: 400;\"> method<\/span><\/i><\/p>\n<pre class=\"line-numbers\"><code class=\"language-java\">\/\/ (2)                          (1)\r\n@Nullable Path getParentName(Path path) {\r\n  Path parent = path.getParent(); \/\/ (3)\r\n  return parent != null ? parent.getFileName() : null;\r\n            \/\/ (4)\r\n}\r\n<\/code><\/pre>\n<p><span style=\"font-weight: 400;\">Compared to a null-safe but not annotated version, this code adds a single annotation on the return type. There are several things worth noting here:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Unannotated types are considered not-nullable<\/b><span style=\"font-weight: 400;\">. This convention greatly reduces the annotation burden but is applied only to first-party code.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Return type is marked <\/b><span style=\"font-family: 'courier new', courier;\"><b>@Nullable<\/b><\/span><span style=\"font-weight: 400;\"> because the method can return <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">null<\/span><span style=\"font-weight: 400;\">.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Local<\/b><span style=\"font-weight: 400;\"> variable <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">parent<\/span><span style=\"font-weight: 400;\"> is not annotated, as its <\/span><b>nullness must be inferred<\/b><span style=\"font-weight: 400;\"> by the static analysis checker. This further reduces the annotation burden.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Checking a value for <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">null <\/span><b>refines its type<\/b><span style=\"font-weight: 400;\"> to be not-nullable in the corresponding branch. This is called <\/span><i><span style=\"font-weight: 400;\">flow-sensitive typing, <\/span><\/i><span style=\"font-weight: 400;\">and it allows writing code idiomatically and handling nullness only where it\u2019s really necessary.<\/span><\/li>\n<\/ol>\n<blockquote class=\"blockquote\"><p><span style=\"font-weight: 400;\">Code annotated for nullness can be statically checked for null-safety. The analyzer can protect the codebase from regressions and allow developers to move faster with confidence.<\/span><\/p><\/blockquote>\n<h3><span style=\"font-weight: 400;\">Kotlin and nullness<\/span><\/h3>\n<p><span style=\"font-weight: 400;\"><a href=\"https:\/\/engineering.fb.com\/2022\/10\/24\/android\/android-java-kotlin-migration\/\" target=\"_blank\" rel=\"noopener\">Kotlin<\/a> is a modern programming language designed to interoperate with Java. In Kotlin, nullness is explicit in the types, and the compiler checks that the code is handling nullness correctly, giving developers instant feedback.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We recognize these advantages and, in fact, <\/span><a href=\"https:\/\/engineering.fb.com\/2022\/10\/24\/android\/android-java-kotlin-migration\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">use Kotlin heavily at Meta<\/span><\/a><span style=\"font-weight: 400;\">. But we also recognize the fact that there is a lot of business-critical Java code that cannot \u2014 and sometimes should not \u2014 be moved to Kotlin overnight.\u00a0<\/span><\/p>\n<blockquote class=\"blockquote\"><p><span style=\"font-weight: 400;\">The two languages \u2013 Java and Kotlin \u2013 have to coexist, which means there is still a need for a null-safety solution for Java.<\/span><\/p><\/blockquote>\n<h2><span style=\"font-weight: 400;\">Static analysis for nullness checking at scale<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Meta\u2019s success building other static analysis tools such as <a href=\"https:\/\/fbinfer.com\/\" target=\"_blank\" rel=\"noopener\">Infer<\/a>, <\/span><a href=\"https:\/\/docs.hhvm.com\/hack\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Hack<\/span><\/a><span style=\"font-weight: 400;\">, and <\/span><a href=\"https:\/\/flow.org\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Flow <\/span><\/a><span style=\"font-weight: 400;\">and applying them to real-world code-bases made us confident that we could build a nullness checker for Java that is:\u00a0<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Ergonomic:<\/b><span style=\"font-weight: 400;\"> understands the flow of control in the code, doesn\u2019t require developers to bend over backward to make their code compliant, and adds minimal annotation burden.\u00a0<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Scalable:<\/b><span style=\"font-weight: 400;\"> able to scale from hundreds of lines of code to millions.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Compatible with Kotlin:<\/b><span style=\"font-weight: 400;\"> for seamless interoperability.<\/span><\/li>\n<\/ol>\n<blockquote class=\"blockquote\"><p><span style=\"font-weight: 400;\">In retrospect, implementing the static analysis checker itself was probably the easy part. The real effort went into integrating this checker with the development infrastructure, working with the developer communities, and then making millions of lines of production Java code null-safe.<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">We implemented the first version of our nullness checker for Java as a <\/span><a href=\"https:\/\/fbinfer.com\/docs\/checker-eradicate\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">part of Infer<\/span><\/a><span style=\"font-weight: 400;\">, and it served as a great foundation. Later on, we moved to a compiler-based infrastructure. Having a tighter integration with the compiler allowed us to improve the accuracy of the analysis and streamline the integration with development tools.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This second version of the analyzer is called Nullsafe, and we will be covering it below.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Null-checking under the hood<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Java compiler API was introduced via <\/span><a href=\"https:\/\/jcp.org\/en\/jsr\/detail?id=199\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">JSR-199<\/span><\/a><span style=\"font-weight: 400;\">. This API gives access to the compiler\u2019s internal representation of a compiled program and allows custom functionality to be added at different stages of the compilation process. We use this API to extend Java\u2019s type-checking with an extra pass that runs Nullsafe analysis and then collects and reports nullness errors.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Two main data structures used in the analysis are the abstract syntax tree (AST) and control flow graph (CFG). See Listing 3 and Figures 2 and 3 for examples.<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The AST represents the syntactic structure of the source code without superfluous details like punctuation. We get a program\u2019s AST via the compiler API, together with the type and annotation information.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The CFG is a flowchart of a piece of code: blocks of instructions connected with arrows representing a change in control flow. We\u2019re using the <\/span><a href=\"https:\/\/github.com\/typetools\/checker-framework\/tree\/master\/dataflow\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Dataflow<\/span><\/a><span style=\"font-weight: 400;\"> library to build a CFG for a given AST.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">The analysis itself is split into two phases:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The <\/span><b>type inference<\/b><span style=\"font-weight: 400;\"> phase is responsible for figuring out nullness of various pieces of code, answering questions such as:<\/span>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><i><span style=\"font-weight: 400;\">Can this method invocation return <\/span><\/i><span style=\"font-weight: 400; font-family: 'courier new', courier;\">null<\/span><i><span style=\"font-weight: 400;\"> at program point X<\/span><\/i><span style=\"font-weight: 400;\">?<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><i><span style=\"font-weight: 400;\">Can this variable be <\/span><\/i><span style=\"font-weight: 400; font-family: 'courier new', courier;\">null<\/span><i><span style=\"font-weight: 400;\"> at program point Y<\/span><\/i><span style=\"font-weight: 400;\">?<\/span><\/li>\n<\/ul>\n<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The <\/span><b>type checking<\/b><span style=\"font-weight: 400;\"> phase is responsible for validating that the code doesn\u2019t do anything unsafe, such as dereferencing a nullable value or passing a nullable argument where it\u2019s not expected.<\/span><\/li>\n<\/ol>\n<p><i><span style=\"font-weight: 400;\">Listing 3<\/span><\/i><span style=\"font-weight: 400;\">: <\/span><i><span style=\"font-weight: 400;\">example <\/span><\/i><span style=\"font-weight: 400; font-family: 'courier new', courier;\">getOrDefault<\/span><i><span style=\"font-weight: 400;\"> method<\/span><\/i><\/p>\n<pre class=\"line-numbers\"><code class=\"language-java\">String getOrDefault(@Nullable String str, String defaultValue) {\r\n  if (str == null) { return defaultValue; }\r\n  return str;\r\n}<\/code><\/pre>\n<figure id=\"attachment_19818\" aria-describedby=\"caption-attachment-19818\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-19818\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-2-cropped.webp?w=1024\" alt=\"Nullsafe\" width=\"1024\" height=\"455\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-2-cropped.webp 1496w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-2-cropped.webp?resize=916,407 916w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-2-cropped.webp?resize=768,341 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-2-cropped.webp?resize=1024,455 1024w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-2-cropped.webp?resize=96,43 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-2-cropped.webp?resize=192,85 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><figcaption id=\"caption-attachment-19818\" class=\"wp-caption-text\">Figure 2: CFG for code from Listing 3.<\/figcaption><\/figure>\n<figure id=\"attachment_19734\" aria-describedby=\"caption-attachment-19734\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-19734\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-3.png?w=1024\" alt=\"nullsafe\" width=\"1024\" height=\"739\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-3.png 1141w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-3.png?resize=916,661 916w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-3.png?resize=768,554 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-3.png?resize=1024,739 1024w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-3.png?resize=96,69 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-3.png?resize=192,138 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><figcaption id=\"caption-attachment-19734\" class=\"wp-caption-text\">Figure 3: AST for code from Listing 3<\/figcaption><\/figure>\n<h4><span style=\"font-weight: 400;\">Type-inference phase\u00a0<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">Nullsafe does type inference based on the code\u2019s CFG. The result of the inference is a mapping from expressions to nullness-extended types at different program points.<\/span><\/p>\n<p><em><span style=\"font-weight: 400;\">state = expression x <\/span><span style=\"font-weight: 400;\">program point \u2192 <\/span><span style=\"font-weight: 400;\">nullness \u2013 extended type<\/span><\/em><\/p>\n<p><span style=\"font-weight: 400;\">The inference engine traverses the CFG and <\/span><i><span style=\"font-weight: 400;\">executes<\/span><\/i><span style=\"font-weight: 400;\"> every instruction according to the analysis\u2019 rules. For a program from <\/span><i><span style=\"font-weight: 400;\">Listing 3<\/span><\/i><span style=\"font-weight: 400;\"> this would look like this:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">We start with a mapping at <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">&lt;entry&gt;<\/span><span style=\"font-weight: 400;\"> point:\u00a0<\/span>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-family: 'courier new', courier;\"><span style=\"font-weight: 400;\">{str <em>\u2192 <\/em><\/span><span style=\"font-weight: 400;\"> @Nullable String, defaultValue <em>\u2192 <\/em><\/span><span style=\"font-weight: 400;\">String}<\/span><\/span><span style=\"font-weight: 400;\">.<\/span><\/li>\n<\/ul>\n<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">When we execute the comparison <\/span><span style=\"font-weight: 400;\">str<\/span><span style=\"font-weight: 400;\"> == <\/span><span style=\"font-weight: 400;\">null<\/span><span style=\"font-weight: 400;\">, the control flow splits and we produce two mappings:<\/span>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">THEN: <\/span><span style=\"font-weight: 400;\">{<span style=\"font-family: 'courier new', courier;\">str <em>\u2192 <\/em><\/span><\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\"> @Nullable String, defaultValue <span style=\"font-family: 'courier new', courier;\"><span style=\"font-weight: 400;\"><em>\u2192 <\/em><\/span><\/span><\/span><span style=\"font-weight: 400;\"><span style=\"font-family: 'courier new', courier;\"> String<\/span>}<\/span><span style=\"font-weight: 400;\">.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">ELSE: <\/span><span style=\"font-weight: 400;\">{<\/span><span style=\"font-family: 'courier new', courier;\"><span style=\"font-weight: 400; color: violet;\">str <em>\u2192 <\/em><\/span><span style=\"font-weight: 400; color: violet;\"> String<\/span><span style=\"font-weight: 400;\">, defaultValue <em>\u2192 <\/em><\/span><\/span><span style=\"font-weight: 400;\"><span style=\"font-family: 'courier new', courier;\"> String<\/span>}<\/span><span style=\"font-weight: 400;\">.<\/span><\/li>\n<\/ul>\n<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">When the control flow joins, the inference engine needs to produce a mapping that over-approximates the state in both branches. If we have <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">@Nullable String<\/span><span style=\"font-weight: 400;\"> in one branch and <\/span><span style=\"font-weight: 400;\">String<\/span><span style=\"font-weight: 400;\"> in another, the over-approximated type would be <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">@Nullable String<\/span><span style=\"font-weight: 400;\">.<\/span><\/li>\n<\/ol>\n<figure id=\"attachment_19735\" aria-describedby=\"caption-attachment-19735\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-19735\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-4.png?w=1024\" alt=\"Nullsafe\" width=\"1024\" height=\"491\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-4.png 1026w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-4.png?resize=916,439 916w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-4.png?resize=768,368 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-4.png?resize=1024,491 1024w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-4.png?resize=96,46 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-figure-4.png?resize=192,92 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><figcaption id=\"caption-attachment-19735\" class=\"wp-caption-text\">Figure 4: CFG with the analysis results<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">The main benefit of using a CFG for inference is that it allows us to make the analysis flow-sensitive, which is crucial for an analysis like this to be useful in practice.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The example above demonstrates a very common case where nullness of a value is refined according to the control flow. To accommodate real-world coding patterns, Nullsafe has support for more advanced features, ranging from contracts and complex invariants where we use SAT solving to interprocedural object initialization analysis. Discussion of these features, however, is outside the scope of this post.<\/span><\/p>\n<h4><span style=\"font-weight: 400;\">Type-checking phase<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">Nullsafe does type checking based on the program\u2019s AST. By traversing the AST, we can compare the information specified in the source code with the results from the inference step.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">In our example from Listing 3, when we visit the <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">return str<\/span><span style=\"font-weight: 400;\"> node we fetch the inferred type of <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">str<\/span><span style=\"font-weight: 400;\"> expression, which happens to be <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">String<\/span><span style=\"font-weight: 400;\">, and check whether this type is compatible with the return type of the method, which is declared as <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">String<\/span><span style=\"font-weight: 400;\">.<\/span><\/p>\n<figure id=\"attachment_19816\" aria-describedby=\"caption-attachment-19816\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-19816\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-5-cropped.webp?w=1024\" alt=\"nullsafe\" width=\"1024\" height=\"488\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-5-cropped.webp 2176w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-5-cropped.webp?resize=916,436 916w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-5-cropped.webp?resize=768,366 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-5-cropped.webp?resize=1024,488 1024w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-5-cropped.webp?resize=1536,731 1536w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-5-cropped.webp?resize=2048,975 2048w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-5-cropped.webp?resize=96,46 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-5-cropped.webp?resize=192,91 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><figcaption id=\"caption-attachment-19816\" class=\"wp-caption-text\">Figure 5: Checking types during AST traversal.<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">When we see an AST node corresponding to an object dereference, we check that the inferred type of the receiver excludes <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">null<\/span><span style=\"font-weight: 400;\">. Implicit unboxing is treated in a similar way. For method call nodes, we check that the inferred types of the arguments are compatible with method\u2019s declared types. And so on.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Overall, the type-checking phase is much more straightforward than the type-inference phase. One nontrivial aspect here is error rendering, where we need to augment a type error with a context, such as a type trace, code origin, and potential quick fix.<\/span><\/p>\n<h4><span style=\"font-weight: 400;\">Challenges in supporting generics<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">Examples of the nullness analysis given above covered only the so-called root nullness, or nullness of a value itself. Generics add a whole new dimension of expressivity to the language and, similarly, nullness analysis can be extended to support generic and parameterized classes to further improve the expressivity and precision of APIs.<\/span><\/p>\n<blockquote class=\"blockquote\"><p><span style=\"font-weight: 400;\">Supporting generics is obviously a good thing. But extra expressivity comes as a cost. In particular, type inference gets a lot more complicated.<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">Consider a parameterized class <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">Map&lt;K, List&lt;Pair&lt;V1, V2&gt;&gt;&gt;<\/span><span style=\"font-weight: 400;\">. In the case of <\/span><b>non-generic <\/b><span style=\"font-weight: 400;\">nullness checker, there is only the root nullness to infer:<\/span><\/p>\n<pre class=\"line-numbers\"><code class=\"language-java\">\/\/ NON-GENERIC CASE\r\n   \u2423 Map&lt;K, List&lt;Pair&lt;V1, V2&gt;&gt;\r\n\/\/ ^\r\n\/\/ \\--- Only the root nullness needs to be inferred\r\n\r\n<\/code><\/pre>\n<p><span style=\"font-weight: 400;\">The <\/span><b>generic <\/b><span style=\"font-weight: 400;\">case requires a lot more gaps to fill on top of an already complex flow-sensitive analysis:<\/span><\/p>\n<pre class=\"line-numbers\"><code class=\"language-java\">\/\/ GENERIC CASE\r\n   \u2423 Map&lt;\u2423 K, \u2423 List&lt;\u2423 Pair&lt;\u2423 V1, \u2423 V2&gt;&gt;\r\n\/\/ ^     ^    ^      ^      ^      ^\r\n\/\/ \\-----|----|------|------|------|--- All these need to be inferred\r\n<\/code><\/pre>\n<p><span style=\"font-weight: 400;\">This is not all. Generic types that the analysis infers must closely follow <\/span><i><span style=\"font-weight: 400;\">the shape<\/span><\/i><span style=\"font-weight: 400;\"> of the types that Java itself inferred to avoid bogus errors. For example, consider the following snippet of code:<\/span><\/p>\n<pre class=\"line-numbers\"><code class=\"language-java\">interface Animal {}\r\nclass Cat implements Animal {}\r\nclass Dog implements Animal {}\r\n\r\nvoid targetType(@Nullable Cat catMaybe) {\r\n  List&lt;@Nullable Animal&gt; animalsMaybe = List.of(catMaybe);\r\n}\r\n<\/code><\/pre>\n<p><span style=\"font-weight: 400; font-family: 'courier new', courier;\">List.&lt;T&gt;of(T\u2026)<\/span><span style=\"font-weight: 400;\"> is a generic method and in isolation the type of <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">List.of(catMaybe)<\/span><span style=\"font-weight: 400;\"> could be inferred as <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">List&lt;@Nullable Cat&gt;<\/span><span style=\"font-weight: 400;\">. This would be problematic because generics in Java are invariant, which means that <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">List&lt;Animal&gt;<\/span><span style=\"font-weight: 400;\"> is not compatible with <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">List&lt;Cat&gt;<\/span><span style=\"font-weight: 400;\"> and the assignment would produce an error.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The reason this code type checks is that the Java compiler knows the type of the target of the assignment and uses this information to tune how the type inference engine works in the context of the assignment (or a method argument for the matter). This feature is called <\/span><i><span style=\"font-weight: 400;\">target typing<\/span><\/i><span style=\"font-weight: 400;\">, and although it improves the ergonomics of working with generics, it doesn\u2019t play nicely with the kind of forward CFG-based analysis we described before, and it required extra care to handle.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">In addition to the above, the Java compiler itself has bugs (e.g., <\/span><a href=\"https:\/\/bugs.openjdk.org\/browse\/JDK-8225377\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">this<\/span><\/a><span style=\"font-weight: 400;\">) that require various workarounds in Nullsafe and in other static analysis tools that work with type annotations.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Despite these challenges, we see <\/span><b>significant value in supporting generics<\/b><span style=\"font-weight: 400;\">. In particular:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Improved ergonomics<\/b><span style=\"font-weight: 400;\">. Without support for generics, developers cannot define and use certain APIs in a null-aware way: from collections and functional interfaces to streams. They are forced to circumvent the nullness checker, which harms reliability and reinforces a bad habit. We have found many places in the codebase where lack of null-safe generics led to <\/span><b>brittle code and bugs<\/b><span style=\"font-weight: 400;\">.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Safer Kotlin interoperability<\/b><span style=\"font-weight: 400;\">. Meta is a heavy user of Kotlin, and a nullness analysis that supports generics closes the gap between the two languages and significantly <\/span><b>improves the safety of the interop<\/b><span style=\"font-weight: 400;\"> and the development experience in a heterogeneous codebase.<\/span><\/li>\n<\/ul>\n<h3><span style=\"font-weight: 400;\">Dealing with legacy and third-party code<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Conceptually, the static analysis performed by Nullsafe adds a new set of semantic rules to Java in an attempt to retrofit null-safety onto an otherwise null-unsafe language. The ideal scenario is that all code follows these rules, in which case diagnostics raised by the analyzer are relevant and actionable. The reality is that there\u2019s a lot of null-safe code that knows nothing about the new rules, and there\u2019s even more null-unsafe code. Running the analysis on such legacy code or even newer code that calls into legacy components would produce too much noise, which would add friction and undermine the value of the analyzer.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">To deal with this problem in Nullsafe, we separate code into three tiers:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Tier 1: Nullsafe compliant code.<\/b><span style=\"font-weight: 400;\"> This includes first-party code marked as <\/span><span style=\"font-weight: 400;\">@Nullsafe<\/span><span style=\"font-weight: 400;\"> and checked to have no errors. This also includes known good annotated third-party code or third-party code for which we have added nullness models.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Tier 2: First-party code not compliant with Nullsafe.<\/b><span style=\"font-weight: 400;\"> This is internal code written without explicit nullness tracking in mind. This code is checked optimistically by Nullsafe.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Tier 3: Unvetted third-party code.<\/b><span style=\"font-weight: 400;\"> This is third-party code that Nullsafe knows nothing about. When using such code, the uses are checked pessimistically and developers are urged to add proper nullness models.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">The important aspect of this tiered system is that when Nullsafe type-checks Tier <\/span><i><span style=\"font-weight: 400;\">X<\/span><\/i><span style=\"font-weight: 400;\"> code that calls into Tier <\/span><i><span style=\"font-weight: 400;\">Y<\/span><\/i><span style=\"font-weight: 400;\"> code, it uses Tier <\/span><i><span style=\"font-weight: 400;\">Y<\/span><\/i><span style=\"font-weight: 400;\">\u2019s rules. In particular:<\/span><\/p>\n<ol type=\"A\">\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Calls from Tier 1 to Tier 2 are checked optimistically,<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Calls from Tier 1 to Tier 3 are checked pessimistically,<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Calls from Tier 2 to Tier 1 are checked according to Tier 1 component\u2019s nullness.<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">Two things are worth noting here:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">According to point A, Tier 1 code can have unsafe dependencies or safe dependencies used unsafely. This unsoundness is the price we had to pay to streamline and gradualize the rollout and adoption of Nullsafe in the codebase. We tried other approaches, but extra friction rendered them extremely hard to scale. The good news is that as more Tier 2 code is migrated to Tier 1 code, this point becomes less of a concern.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Pessimistic treatment of third-party code (point B) adds extra friction to the nullness checker adoption. But in our experience, the cost was not prohibitive, while the improvement in the safety of Tier 1 and Tier 3 code interoperability was real.<\/span><\/li>\n<\/ol>\n<figure id=\"attachment_19812\" aria-describedby=\"caption-attachment-19812\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-19812\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-6-update.webp?w=1024\" alt=\"Nullsafe\" width=\"1024\" height=\"768\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-6-update.webp 2304w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-6-update.webp?resize=916,687 916w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-6-update.webp?resize=768,576 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-6-update.webp?resize=1024,768 1024w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-6-update.webp?resize=1536,1152 1536w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-6-update.webp?resize=2048,1536 2048w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-6-update.webp?resize=96,72 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-6-update.webp?resize=192,144 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><figcaption id=\"caption-attachment-19812\" class=\"wp-caption-text\">Figure 6: Three tiers of null-safety rules.<\/figcaption><\/figure>\n<h3><span style=\"font-weight: 400;\">Deployment, automation, and adoption<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">A nullness checker alone is not enough to make a real impact. The effect of the checker is proportional to the amount of code compliant with this checker. Thus a migration strategy, developer adoption, and protection from regressions become primary concerns.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We found three main points to be essential to our initiative\u2019s success:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Quick fixes<\/b><span style=\"font-weight: 400;\"> are incredibly helpful. The codebase is full of trivial null-safety violations. Teaching a static analysis to not only check for errors but also to come up with quick fixes can cover a lot of ground and give developers the space to work on meaningful fixes.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Developer adoption<\/b><span style=\"font-weight: 400;\"> is key. This means that the checker and related tooling should integrate well with the main development tools: build tools, IDEs, CLIs, and CI. But more important, there should be a working feedback loop between application and static analysis developers. <\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b style=\"font-size: 1rem;\">Data and metrics<\/b><span style=\"font-weight: 400;\"> are important to keep the momentum. Knowing where you are, the progress you\u2019ve made, and the next best thing to fix really helps facilitate the migration.<\/span><\/li>\n<\/ol>\n<h2><span style=\"font-weight: 400;\">Longer-term reliability impact<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">As one example, looking at 18 months of reliability data for the Instagram Android app:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The portion of the app\u2019s code compliant with Nullsafe grew from 3 percent to 90 percent.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">There was a significant decrease in the relative volume of <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier;\">NullPointerException<\/span><span style=\"font-weight: 400;\"> (NPE) errors across all release channels (see Figure 7). Particularly, in production, the volume of NPEs was reduced by 27 percent.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">This data is validated against other types of crashes and shows a real improvement in reliability and null-safety of the app.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">At the same time, individual product teams also reported significant reduction in the volume of NPE crashes after addressing nullness errors reported by Nullsafe.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The drop in production NPEs varied from team to team, with <\/span><b>improvements ranging<\/b> <b>from 35 percent to 80 percent<\/b><span style=\"font-weight: 400;\">.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">One particularly interesting aspect of the results is the <\/span><b>drastic drop in NPEs in the alpha-channel<\/b><span style=\"font-weight: 400;\">. This directly reflects the improvement in the developer productivity that comes from using and relying on a nullness checker.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Our north star goal, and an ideal scenario, would be to completely eliminate NPEs. However, real-world reliability is complex, and there are more factors playing a role:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">There is still null-unsafe code that is, in fact, responsible for a large percentage of top NPE crashes. But now we are in a position where targeted null-safety improvements can make a significant and lasting impact.<\/span><\/li>\n<\/ul>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The volume of crashes is not the best metric to measure reliability improvement because one bug that slips into production can become very hot and single-handedly skew the results. A better metric might be the number of new unique crashes per release, where we see <\/span><i><span style=\"font-weight: 400;\">n<\/span><\/i><span style=\"font-weight: 400;\">-fold improvement.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Not all NPE crashes are caused by bugs in the app\u2019s code alone. A mismatch between the client and the server is another major source of production issues that need to be addressed via other means.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The static analysis itself has limitations and unsound assumptions that let certain bugs slip into production.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">It is important to note that this is the <\/span><b>aggregate effect of hundreds of engineers using Nullsafe<\/b><span style=\"font-weight: 400;\"> to improve the safety of their code as well as the effect of<\/span><b> other reliability initiatives<\/b><span style=\"font-weight: 400;\">, so we can\u2019t attribute the improvement solely to the use of Nullsafe. However, based on reports and our own observations over the course of the last few years, we\u2019re confident that Nullsafe played a significant role in driving down NPE-related crashes.<\/span><\/p>\n<figure id=\"attachment_19813\" aria-describedby=\"caption-attachment-19813\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-19813\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-7-update.webp?w=1024\" alt=\"\" width=\"1024\" height=\"633\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-7-update.webp 1800w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-7-update.webp?resize=916,566 916w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-7-update.webp?resize=768,475 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-7-update.webp?resize=1024,633 1024w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-7-update.webp?resize=1536,950 1536w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-7-update.webp?resize=96,59 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Nullsafe-image-7-update.webp?resize=192,119 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><figcaption id=\"caption-attachment-19813\" class=\"wp-caption-text\">Figure 7: Percent NPE crashes by release channel.<\/figcaption><\/figure>\n<h2><span style=\"font-weight: 400;\">Beyond Meta<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">The problems outlined above are hardly specific to Meta. Unexpected <\/span><span style=\"font-weight: 400;\">null<\/span><span style=\"font-weight: 400;\">-dereferences have caused <\/span><a href=\"https:\/\/www.infoq.com\/presentations\/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">countless problems in different companies<\/span><\/a><span style=\"font-weight: 400;\">. Languages like C# evolved into having <\/span><a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/nullable-references\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">explicit nullness<\/span><\/a><span style=\"font-weight: 400;\"> in their type system, while others, like Kotlin, had it from the very beginning.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">When it comes to Java, there were multiple attempts to add nullness, starting with <\/span><a href=\"https:\/\/stackoverflow.com\/questions\/2289694\/what-is-the-status-of-jsr-305\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">JSR-305<\/span><\/a><span style=\"font-weight: 400;\">, but none was widely successful. Currently, there are many great static analysis tools for Java that can check nullness, including CheckerFramework, SpotBugs, ErrorProne, and NullAway, to name a few. In particular, Uber walked <\/span><a href=\"https:\/\/arxiv.org\/abs\/1907.02127\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">the same path<\/span><\/a><span style=\"font-weight: 400;\"> by making their Android codebase null-safe using NullAway checker. But in the end, all the checkers perform nullness analysis in different and subtly incompatible ways. The lack of standard annotations with precise semantics has constrained the use of static analysis for Java throughout the industry.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This problem is exactly what the <\/span><a href=\"https:\/\/jspecify.dev\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">JSpecify workgroup<\/span><\/a><span style=\"font-weight: 400;\"> aims to address. The JSpecify started in 2019 and is a collaboration between individuals representing companies such as Google, JetBrains, Uber, Oracle, and others. Meta has also been part of JSpecify since late 2019.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Although the <\/span><a href=\"https:\/\/jspecify.dev\/docs\/spec\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">standard for nullness<\/span><\/a><span style=\"font-weight: 400;\"> is not yet finalized, there has been a lot of progress on the specification itself and on the tooling, with more exciting announcements following soon. Participation in JSpecify has also influenced how we at Meta think about nullness for Java and about our own codebase evolution.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We developed a new static analysis tool called Nullsafe that is used at Meta to detect NullPointerException (NPE) errors in Java code. Interoperability with legacy code and gradual deployment model were key to Nullsafe\u2019s wide adoption and allowed us to recover some null-safety properties in the context of an otherwise null-unsafe language in a multimillion-line [&#8230;]<\/p>\n<p><a class=\"btn btn-secondary understrap-read-more-link\" href=\"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/\">Read More&#8230;<\/a><\/p>\n","protected":false},"author":51,"featured_media":19739,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[66],"tags":[],"coauthors":[1869,1870,1871],"class_list":["post-19687","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developer-tools","fb_content_type-article"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v19.3 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Retrofitting null-safety onto Java at Meta<\/title>\n<meta name=\"description\" content=\"Nullsafe is a new static analysis tool that is used at Meta to detect NullPointerException (NPE) errors in Java code.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Artem Pianykh, Ilya Zorin, Dmitry Lyubarskiy\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"18 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/\"},\"author\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/#author\",\"name\":\"\"},\"headline\":\"Retrofitting null-safety onto Java at Meta\",\"datePublished\":\"2022-11-22T14:00:11+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/\"},\"wordCount\":3405,\"publisher\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2022\\\/11\\\/Debugging-Hero.png\",\"articleSection\":[\"DevInfra\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/\",\"name\":\"Retrofitting null-safety onto Java at Meta\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2022\\\/11\\\/Debugging-Hero.png\",\"datePublished\":\"2022-11-22T14:00:11+00:00\",\"description\":\"Nullsafe is a new static analysis tool that is used at Meta to detect NullPointerException (NPE) errors in Java code.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/#primaryimage\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2022\\\/11\\\/Debugging-Hero.png\",\"contentUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2022\\\/11\\\/Debugging-Hero.png\",\"width\":1920,\"height\":1080},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2022\\\/11\\\/22\\\/developer-tools\\\/meta-java-nullsafe\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/engineering.fb.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Retrofitting null-safety onto Java at Meta\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#website\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/\",\"name\":\"Engineering at Meta\",\"description\":\"Engineering at Meta Blog\",\"publisher\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/engineering.fb.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#organization\",\"name\":\"Meta\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2023\\\/08\\\/Meta_lockup_positive-primary_RGB.jpg\",\"contentUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2023\\\/08\\\/Meta_lockup_positive-primary_RGB.jpg\",\"width\":29011,\"height\":12501,\"caption\":\"Meta\"},\"image\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/Engineering\\\/\",\"https:\\\/\\\/x.com\\\/fb_engineering\"]},[]]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Retrofitting null-safety onto Java at Meta","description":"Nullsafe is a new static analysis tool that is used at Meta to detect NullPointerException (NPE) errors in Java code.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/","twitter_misc":{"Written by":"Artem Pianykh, Ilya Zorin, Dmitry Lyubarskiy","Est. reading time":"18 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/#article","isPartOf":{"@id":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/"},"author":{"@id":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/#author","name":""},"headline":"Retrofitting null-safety onto Java at Meta","datePublished":"2022-11-22T14:00:11+00:00","mainEntityOfPage":{"@id":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/"},"wordCount":3405,"publisher":{"@id":"https:\/\/engineering.fb.com\/#organization"},"image":{"@id":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/#primaryimage"},"thumbnailUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Debugging-Hero.png","articleSection":["DevInfra"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/","url":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/","name":"Retrofitting null-safety onto Java at Meta","isPartOf":{"@id":"https:\/\/engineering.fb.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/#primaryimage"},"image":{"@id":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/#primaryimage"},"thumbnailUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Debugging-Hero.png","datePublished":"2022-11-22T14:00:11+00:00","description":"Nullsafe is a new static analysis tool that is used at Meta to detect NullPointerException (NPE) errors in Java code.","breadcrumb":{"@id":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/#primaryimage","url":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Debugging-Hero.png","contentUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Debugging-Hero.png","width":1920,"height":1080},{"@type":"BreadcrumbList","@id":"https:\/\/engineering.fb.com\/2022\/11\/22\/developer-tools\/meta-java-nullsafe\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/engineering.fb.com\/"},{"@type":"ListItem","position":2,"name":"Retrofitting null-safety onto Java at Meta"}]},{"@type":"WebSite","@id":"https:\/\/engineering.fb.com\/#website","url":"https:\/\/engineering.fb.com\/","name":"Engineering at Meta","description":"Engineering at Meta Blog","publisher":{"@id":"https:\/\/engineering.fb.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/engineering.fb.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/engineering.fb.com\/#organization","name":"Meta","url":"https:\/\/engineering.fb.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/engineering.fb.com\/#\/schema\/logo\/image\/","url":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2023\/08\/Meta_lockup_positive-primary_RGB.jpg","contentUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2023\/08\/Meta_lockup_positive-primary_RGB.jpg","width":29011,"height":12501,"caption":"Meta"},"image":{"@id":"https:\/\/engineering.fb.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Engineering\/","https:\/\/x.com\/fb_engineering"]},[]]}},"jetpack_featured_media_url":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/11\/Debugging-Hero.png","jetpack_shortlink":"https:\/\/wp.me\/pa0Lhq-57x","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts\/19687","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/users\/51"}],"replies":[{"embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/comments?post=19687"}],"version-history":[{"count":22,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts\/19687\/revisions"}],"predecessor-version":[{"id":19861,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts\/19687\/revisions\/19861"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/media\/19739"}],"wp:attachment":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/media?parent=19687"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/categories?post=19687"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/tags?post=19687"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/coauthors?post=19687"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}