On Mon, Mar 15, 2021 at 11:42 PM Reuven Lax
<re...@google.com <mailto:re...@google.com>> wrote:
On Mon, Mar 15, 2021 at 9:12 PM Kenneth Knowles
<k...@apache.org <mailto:k...@apache.org>> wrote:
I will be blunt about my opinions about the
general issue:
- NullPointerExceptions (and similar) are a
solved problem.
* They have been since 2003 at the latest
[1] (this is when the types were hacked into
Java - the foundation dates back to the 70s
or earlier)
Huh - Fahndrich tried to hire me once to work on
a project called Singularity. Small world. Also
note that Sanjay Ghemawat is listed in the
citations!
Umm, I was confusing names. Fahndrich is actually a
former coworker at Google :)
* Checkerframework is a _pluggable_
system where that nullness type system is a
"hello, world" level demo, since 2008 at the
latest [2].
* Our users should know this and judge us
accordingly.
- Checkerframework should be thought of and
described as type checking, because it is.
It is not heuristic nor approximate.
- If your code was unclear about whether
something could be null, it was probably
unclear to a person reading it also, and
very likely to have actual bugs.
- APIs that accept a lot of nullable
parameters are, generally speaking, bad
APIs. They are hard to use correctly, less
readable, and very likely to cause actual
bugs. You are forcing your users to deal
with accidental complexity you left behind.
* Corollary to the above two points:
Almost all the time, the changes to clearify
nullness make your code better, more
readable, easier for users or editors.
- It is true that there is a learning curve
to programming in this way.
I agree with the above in a closed system.
However as mentioned, some of the APIs we use
suffer from this.
In a previous life, I saw up close an effort to
add such analysis to a large codebase. Two
separate tools called Prefix and Prefast were
used (the difference between the two is actually
quite interesting, but not relevant here).
However in order to make this analysis useful,
there was a massive effort to properly annotate
the entire codebase, including all libraries
used. This isn't a perfect example - this was a
C++ codebase which is much harder to analyze,
and these tools identified far more than simply
nullness errors (resource leaks, array indices,
proper string null termination, exception
behavior, etc.). However the closer we can get
to properly annotating the transitive closure of
our dependencies, the better this framework will
work.
- There are certainly common patterns in
Java that do not work very well, and
suppression is sometimes the best option.
* Example: JUnit's @Setup and @Test
conventions do not work very well and it is
not worth the effort to make them work. This
is actually because if it were "normal code"
it would be bad code. There are complex
inter-method dependencies enforced only by
convention. This matters: sometimes a JUnit
test class is called from another class,
used as "normal code". This does go wrong in
practice. Plain old JUnit test cases
frequently go wrong as well.
And here is my opinion when it comes to Beam:
- "Community over code" is not an excuse for
negligent practices that cause easily
avoidable risk to our users. I will be very
disappointed if we choose that.
- I think having tooling that helps
newcomers write better code by default is
better for the community too. Just like
having automatic formatting is better. Less
to haggle about in review, etc.
- A simple search reveals about 170 bugs
that we know of [4].
- So far in almost every module I have fixed
I discovered actual new null errors. Many
examples at [5].
- It is extremely easy to suppress the type
checking. Almost all of our classes have it
suppressed already (I did this work, to
allow existing errors while protecting new
code).
- Including the annotations in the shipped
jars is an important feature. Without this,
users cannot write null-safe code themselves.
* Reuven highlighted this: when methods
are not annotated, we have to use/implement
workarounds. Actually Guava does use
checkerframework annotations [6] and the
problem is that it requires its *input* to
already be non-null so actually you cannot
even use it to convert nullable values to
non-nullable values.
* Beam has its own [7] that is annotated,
actually for yet another reason: when
Guava's checkNotNull fails, it throws NPE
when it should throw
IllegalArgumentException. Guava's
checkNotNull should not be used for input
validation!
- It is unfortunate that IntelliJ inserts a
bunch of annotations in user code. I wonder
if there is something we can do about that.
At the Java level, if they are not on the
classpath they should be ignored and not
affect users. Coincidentally, the JDK has
had NullPointerExceptions in this area :-) [8].
I understand the pain of longer compile
times slowing people down. That is actually
a problem to be solved which does not
require lowering our standards of quality.
How about we try moving it to a separate CI
job and see how it goes?
In my experience stories like Reuven's are
much more frustrating in a separate CI job
because you find out quite late that your
code has flaws. Like when spotless fails,
but much more work to fix, and would have
been prevented long ago if it were
integrated into the compile.
I agree with this. I prefer to be able to detect
on my computer that there are failures, and not
have to wait for submission. The complaint was
that some people are experiencing trouble on
their local machine however, so it seems
reasonable to add an opt-out flag (though I
would prefer opt out to opt in).
Kenn
[1]
https://web.eecs.umich.edu/~bchandra/courses/papers/Fahndrich_NonNull.pdf
<https://web.eecs.umich.edu/~bchandra/courses/papers/Fahndrich_NonNull.pdf>
[2]
https://homes.cs.washington.edu/~mernst/pubs/pluggable-checkers-issta2008.pdf
<https://homes.cs.washington.edu/~mernst/pubs/pluggable-checkers-issta2008.pdf>
[3]
https://github.com/google/guava/blob/fe3fda0ca54076a2268d060725e9a6e26f867a5e/pom.xml#L275
<https://github.com/google/guava/blob/fe3fda0ca54076a2268d060725e9a6e26f867a5e/pom.xml#L275>
[4]
https://issues.apache.org/jira/issues/?jql=project%20%3D%20BEAM%20AND%20(summary%20~%20%22NPE%22%20OR%20summary%20~%20%22NullPointerException%22%20OR%20description%20~%20%22NPE%22%20OR%20description%20~%20%22NullPointerException%22%20OR%20comment%20~%20%22NPE%22%20OR%20comment%20~%20%22NullPointerException%22)
<https://issues.apache.org/jira/issues/?jql=project%20%3D%20BEAM%20AND%20(summary%20~%20%22NPE%22%20OR%20summary%20~%20%22NullPointerException%22%20OR%20description%20~%20%22NPE%22%20OR%20description%20~%20%22NullPointerException%22%20OR%20comment%20~%20%22NPE%22%20OR%20comment%20~%20%22NullPointerException%22)>
[5]
https://github.com/apache/beam/pull/12284
<https://github.com/apache/beam/pull/12284>
and
https://github.com/apache/beam/pull/12162
<https://github.com/apache/beam/pull/12162> and
[6]
https://github.com/google/guava/blob/fe3fda0ca54076a2268d060725e9a6e26f867a5e/guava/src/com/google/common/base/Preconditions.java#L878
<https://github.com/google/guava/blob/fe3fda0ca54076a2268d060725e9a6e26f867a5e/guava/src/com/google/common/base/Preconditions.java#L878>
[7]
https://github.com/apache/beam/blob/master/sdks/java/core/src/main/java/org/apache/beam/sdk/util/Preconditions.java
<https://github.com/apache/beam/blob/master/sdks/java/core/src/main/java/org/apache/beam/sdk/util/Preconditions.java>
[8]
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8152174
<https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8152174>
On Mon, Mar 15, 2021 at 2:12 PM Reuven Lax
<re...@google.com <mailto:re...@google.com>>
wrote:
I have some deeper concerns with the
null checks. The fact that many
libraries we use (including guava) don't
always annotate their methods forces a
lot of workarounds. As a very simple
example, the return value from
Preconditions.checkNotNull clearly can
never be null, yet the nullability
checks don't know this. This and other
similar cases require constantly adding
extra unnecessary null checks in the
code just to make the checker happy.
There have been other cases where I
haven't been able to figure out a way to
make the checker happy (often these seem
to involve using lambdas), and after
10-15 minutes of investigation have
given up and disabled the check.
Now you might say that it's worth the
extra pain and ugliness of writing
"useless" code to ensure that we have
null-safe code. However I think this
ignores a sociological aspect of
software development. I have a
higher tolerance than many for this sort
of pain, and I'm willing to spend some
time figuring out how to rewrite my code
such that it makes the checker happy
(even though often it forced me to write
much more awkward code). However even I
have often found myself giving up and
just disabling the check. Many others
will have less tolerance than me, and
will simply disable the checks. At that
point we'll have a codebase littered
with @SuppressWarnings("nullness"),
which doesn't really get us where we
want to be. I've seen similar struggles
in other codebases, and generally having
a static checker with too many false
positives often ends up being worse than
having no checker.
On Mon, Mar 15, 2021 at 10:37 AM Ismaël
Mejía <ieme...@gmail.com
<mailto:ieme...@gmail.com>> wrote:
+1
Even if I like the strictness for
Null checking, I also think that
this is adding too much extra time
for builds (that I noticed locally
when enabled) and also I agree with
Jan that the annotations are
really an undesired side effect. For
reference when you try to auto
complete some method signatures on
IntelliJ on downstream projects
with C-A-v it generates some extra
Checkers annotations like @NonNull
and others even if the user isn't
using them which is not desirable.
On Mon, Mar 15, 2021 at 6:04 PM Kyle
Weaver <kcwea...@google.com
<mailto:kcwea...@google.com>> wrote:
>>
>> Big +1 for moving this to
separate CI job. I really don't like
what annotations are currently added
to the code we ship. Tools like Idea
add these annotations to code they
generate when overriding classes and
that's very annoying. Users should
not be exposed to internal tools
like nullability checking.
>
>
> I was only planning on moving this
to a separate CI job. The job would
still be release blocking, so the
same annotations would still be
required.
>
> I'm not sure which annotations you
are concerned about. There are two
annotations involved with nullness
checking, @SuppressWarnings and
@Nullable. @SuppressWarnings has
retention policy SOURCE, so it
shouldn't be exposed to users at
all. @Nullable is not just for
internal tooling, it also provides
useful information about our APIs to
users. The user should not have to
guess whether a method argument etc.
can be null or not, and for better
or worse, these annotations are the
standard way of expressing that in Java.