Skip to content

Legacy code: tips for migration

Mariha Kozinska edited this page Mar 8, 2019 · 3 revisions

How to add null-safety annotations to already existing code?

Some ideas and tools to consider:

  • start by adding @NonNullApi only, annotate the scope and then continue with @NonNullFields (or vice versa)
    Api can be split to return and param if we think it will make migration easier

  • start from splitting the codebase by scope into smaller chunks: module, package, class, [method]
    What makes a good split? Independence of 'chunks' one from each others - few dependencies between them (low coupling). It gives flexibility to choose different approach to annotate each 'chunk' it's own way.

  • try analyzing flow
    from entry points?

  • try starting from adding @Nullable only and adding defaults later

  • try searching for patterns
    return null;, = null;, null,, Optional.ofNullable, etc.

  • try IntelliJ Infer nullity
    based on static analyzes

  • try Daikon
    based on null values detection during program execution

Dev tools thresholds

Start from weaker requirements to have some time to adapt without breaking the build

  • IDEs parser highlighting -> error-prone compiler

  • NullAway and Checker Framework warn -> error

  • CI pipeline - Github weak -> strong requirement to accept PR

[Clusters of] modules annotating order

  • Where most NPE exceptions flee first
  • Where we change code most often
  • Where we can separate changes easiest
  • Starting from top or bottom of dependency graph

ZONES during migration (phases)

Some IDEs, IntelliJ for instance, allow to define custom code formatter rules for user defined scopes in the code. During migration, different scopes can be used to easily distinguish between code which has been already annotated (but possibly has everything non-nullable) and code which hasn't.

  • Red: not annotated

  • Yellow: under migration – goal: bring attention to potential issues
    IDE highlighting
    @Nullable + @NonNullApi and @NonNullFields

  • Green: annotated – compile time detection of most of NPE
    NullAway (via errorprone)
    CI pipeline + IDEs compiler

  • Greener: all type uses annotated – compile time verification of NPE absence
    Checker Framework

  • Greenest: system boundaries guarded – runtime verification of input data

  • Gray: outside scope of our code
    until? - many libraries start describe API contracts by annotating it with nullable annotations
    CheckerFramework provides method to annotate 3rd party jars by annotating API in external files