2025008. Adoption of JSpecify for Null-Safety

  • Status: Accepted – Partially implemented accross multiple PRs

  • Deciders: Hiroshi Miura, Jean-Christophe Helary, OmegaT Development Team

  • Date: 2025-11-18

Context and Problem Statement

NullPointerExceptions (NPEs) are a common source of runtime errors in Java applications. While static code analysis tools (like SpotBugs and NullAway) can detect potential nullability issues, they rely on accurate annotations to function effectively.

Historically, the Java ecosystem has suffered from a fragmentation of nullability annotations (javax.annotation, com.intellij.annotations, android.support.annotation, etc.), leading to inconsistent analysis and library interoperability issues.

In OmegaT:

  1. We have previously relied on transitive dependencies (e.g., via language-tool) or IDE-specific annotations (IntelliJ) for null checks.

  2. Discussions in December 2022 identified the need for a standard approach.

  3. In March 2025, an RFC was raised to actively use nullity annotations to reduce the 860+ warnings/errors detected by SpotBugs.

  4. Modern Java development practices (influenced by Kotlin and other null-safe languages) advocate for explicit nullability definitions.

We need a standardized, robust, and modern way to declare nullability intent in the codebase to improve quality assurance and developer productivity.

Decision Drivers

  • Standardization: The need to move away from proprietary or deprecated annotation libraries (like JSR-305).

  • Code Quality: Reducing the risk of NPEs through compile-time checks and static analysis.

  • Tooling Support: Ensuring compatibility with modern static analysis tools like NullAway and SpotBugs.

  • Clarity: Making APIs self-documenting regarding whether they accept or return null.

Considered Options

  • Option 1: Continue using ad-hoc annotations (IntelliJ/FindBugs).

    • Pros: No immediate changes required.

    • Cons: Fragmentation, lack of standard enforcement, dependency on specific IDEs or older libraries.

  • Option 2: Adopt JSpecify (org.jspecify.annotations).

    • Pros: Industry standard for JVM nullability, supported by major industry players (Google, JetBrains, etc.), provides @NullMarked for comprehensive defaults.

    • Cons: Requires adding a dependency (though lightweight), requires migration effort.

Decision

We will adopt JSpecify as the standard library for nullability annotations in the OmegaT project.

  1. Dependency: We add org.jspecify:jspecify as a compile-time dependency.

  2. Default Non-Null Strategy: We will utilize the @NullMarked annotation at the package level (via package-info.java) or class level. This inverts the default behavior: everything is assumed non-null unless specified otherwise.

  3. Explicit Nullable: We will use @Nullable explicitly for any field, parameter, or return value that can legitimately be null.

  4. Migration: Existing annotations (from IntelliJ or other sources) will be replaced with JSpecify equivalents progressively.

This decision aligns with ADR 2025003 Comprehensive Static Code Analysis Strategy.

Consequences

Positive

  • Reduced Bugs: Static analysis tools can now rigorously enforce null safety, catching bugs before runtime.

  • Clearer API Contracts: Developers immediately know if a method returns null or if an argument requires a non-null value.

  • Reduced Boilerplate: Using @NullMarked reduces the visual noise of having to annotate every single non-null parameter with @NonNull.

  • Modernization: Aligns OmegaT source code with current Java ecosystem standards.

Negative

  • Initial Noise: Enabling stricter checks may result in a temporary spike in linter warnings that need to be resolved or suppressed.

  • Learning Curve: Developers must strictly adhere to annotating nullable types, otherwise, the “default non-null” assumption may hide issues (though tools usually catch inconsistencies).

  • Short‑term Workload Increase: The project will experience a sustained period of warning cleanup and annotation work across modules (e.g., MissingOverride, Javadoc tag hygiene, nullability contracts). This adds overhead until the codebase reaches a new steady state with low warning counts.

  • CI Signal‑to‑Noise Risk: The elevated number of warnings can obscure genuinely critical findings unless baselines and thresholds are tuned.

Note: The observed messages reported as “errors” are warnings emitted by Error Prone/NullAway and related linters; the build remains successful. The heightened visibility is intentional, following the introduction of Error Prone to the core (PR #1496) and later expansion to modules (PR #1692), and supports the migration to JSpecify.

Implementation Progress

The adoption of JSpecify is being carried out incrementally across the codebase through focused pull requests:

Under review

Merged

References