|
| 1 | +# General Patterns |
| 2 | + |
| 3 | +## @Nullable |
| 4 | + |
| 5 | +All arguments and members are treated as non-null by default. Annotate with `@Nullable` (from |
| 6 | +`javax.annotation`) only when `null` is actually possible. |
| 7 | + |
| 8 | +- **Fields**: annotate only if the field can hold `null` after construction. |
| 9 | +- **Parameters**: annotate only if `null` is actually passed by callers. |
| 10 | +- **Return types**: annotate only if the method actually returns `null`. A non-null implementation |
| 11 | + of a `@Nullable`-declared interface method should omit the annotation — it is more precise. |
| 12 | + |
| 13 | +## API consistency |
| 14 | + |
| 15 | +The project aims to provide a consistent experience across all public APIs. When designing new |
| 16 | +API, look for prior art in the project before introducing new patterns — prefer the style already |
| 17 | +established in the same package, then the same module, then the broader project. |
| 18 | + |
| 19 | +## AutoValue |
| 20 | + |
| 21 | +Use [AutoValue](https://github.com/google/auto/tree/master/value) for new value classes. |
| 22 | + |
| 23 | +- Add `annotationProcessor("com.google.auto.value:auto-value")` in `build.gradle.kts`. |
| 24 | +- `auto-value-annotations` is already available via `otel.java-conventions`. |
| 25 | +- Add a package-private constructor to all AutoValue classes to prevent external extension. |
| 26 | +- `japicmp` allows new abstract methods on AutoValue classes — the `AllowNewAbstractMethodOnAutovalueClasses` rule handles this. |
| 27 | + |
| 28 | +## Class member ordering |
| 29 | + |
| 30 | +In general, order class members as follows: |
| 31 | + |
| 32 | +1. Static fields (final before non-final) |
| 33 | +2. Instance fields (final before non-final) |
| 34 | +3. Constructors |
| 35 | + - In static utility classes, the private constructor goes after methods, not before. |
| 36 | +4. Methods |
| 37 | + - If methods call each other, the calling method should appear above the method it calls. |
| 38 | +5. Nested classes |
| 39 | + |
| 40 | +## Dedicated lock objects |
| 41 | + |
| 42 | +Do not synchronize using a class's intrinsic lock (`synchronized(this)` or `synchronized` method). |
| 43 | +Use a dedicated lock object: |
| 44 | + |
| 45 | +```java |
| 46 | +private final Object lock = new Object(); |
| 47 | + |
| 48 | +public void doSomething() { |
| 49 | + synchronized (lock) { ... } |
| 50 | +} |
| 51 | +``` |
| 52 | + |
| 53 | +## Formatting |
| 54 | + |
| 55 | +Formatting is enforced by three tools. Run `./gradlew spotlessApply` before committing — it fixes |
| 56 | +most violations automatically. |
| 57 | + |
| 58 | +- **Spotless** — formatting rules vary by file type (see `buildSrc/src/main/kotlin/otel.spotless-conventions.gradle.kts`): |
| 59 | + Java uses google-java-format; Kotlin uses ktlint. Also enforces the Apache license header |
| 60 | + (template in `buildscripts/spotless.license.java`) and misc file hygiene (trailing whitespace, |
| 61 | + final newline, etc.). |
| 62 | +- **Checkstyle** — enforces naming conventions, import ordering, Javadoc structure, and other |
| 63 | + rules not covered by formatting alone. Config is in `buildscripts/checkstyle.xml`. |
| 64 | +- **EditorConfig** (`.editorconfig`) — configures IntelliJ to match project style automatically. |
| 65 | + It doesn't cover all rules, so `spotlessApply` is still required. |
| 66 | + |
| 67 | +## Internal code |
| 68 | + |
| 69 | +Prefer package-private over putting code in an `internal` package. Use `internal` only when the |
| 70 | +code must be `public` for technical reasons (e.g. accessed across packages within the same module) |
| 71 | +but should not be part of the public API. |
| 72 | + |
| 73 | +Public classes in `internal` packages are excluded from semver guarantees and Javadoc, but they |
| 74 | +must carry one of the two standard disclaimers (enforced by the `OtelInternalJavadoc` Error Prone |
| 75 | +check in `custom-checks/`): |
| 76 | + |
| 77 | +```java |
| 78 | +// Standard internal disclaimer: |
| 79 | +/** |
| 80 | + * This class is internal and is hence not for public use. Its APIs are unstable and can change |
| 81 | + * at any time. |
| 82 | + */ |
| 83 | + |
| 84 | +// For incubating internal code that may be promoted to public API: |
| 85 | +/** |
| 86 | + * This class is internal and experimental. Its APIs are unstable and can change at any time. |
| 87 | + * Its APIs (or a version of them) may be promoted to the public stable API in the future, but |
| 88 | + * no guarantees are made. |
| 89 | + */ |
| 90 | +``` |
| 91 | + |
| 92 | +Internal code must not be used across module boundaries — module `foo` must not call internal |
| 93 | +code from module `bar`. Cross-module internal usage is a known issue being tracked and cleaned up |
| 94 | +in open-telemetry/opentelemetry-java#6970. |
| 95 | + |
| 96 | +## Javadoc |
| 97 | + |
| 98 | +All public classes, and their public and protected methods, must have complete Javadoc including |
| 99 | +all parameters — this is enforced for public APIs. Package-private and private members may have |
| 100 | +Javadoc at the author's discretion. |
| 101 | + |
| 102 | +- No `@author` tags. |
| 103 | +- New public API elements require a `@since` annotation. This is added automatically during the |
| 104 | + [release process](../../RELEASING.md) — do not include it in your PR. |
| 105 | +- See [section 7.3.1](https://google.github.io/styleguide/javaguide.html#s7.3.1-javadoc-exception-self-explanatory) |
| 106 | + for self-explanatory exceptions. |
| 107 | + |
| 108 | +Published Javadoc is available at https://javadoc.io/doc/io.opentelemetry. |
| 109 | + |
| 110 | +## Language version compatibility |
| 111 | + |
| 112 | +Production code targets Java 8. Test code also targets Java 8. Do not use Java 9+ APIs unless |
| 113 | +the module explicitly sets a higher minimum. See [VERSIONING.md](../../VERSIONING.md) for the full |
| 114 | +language version compatibility policy, including Android and Kotlin minimum versions. |
| 115 | + |
| 116 | +## Logging |
| 117 | + |
| 118 | +Use `java.util.logging` (JUL) in production source sets. Do not use SLF4J or other logging |
| 119 | +frameworks in `src/main/`. Tests bridge JUL to SLF4J via `JulBridgeInitializer` (configured |
| 120 | +automatically by `otel.java-conventions`). |
| 121 | + |
| 122 | +```java |
| 123 | +private static final Logger LOGGER = Logger.getLogger(MyClass.class.getName()); |
| 124 | +``` |
| 125 | + |
| 126 | +## toString() |
| 127 | + |
| 128 | +Adding `toString()` overrides is encouraged for debugging assistance. All `toString()` |
| 129 | +implementations should be considered unstable unless explicitly documented otherwise — do not |
| 130 | +rely on their output programmatically. |
| 131 | + |
| 132 | +## Visibility |
| 133 | + |
| 134 | +Use the most restrictive access modifier that still allows the code to function correctly. Use |
| 135 | +`final` on classes unless extension is explicitly intended. |
0 commit comments