The Annotated Programmer
Annotations are where it’s @
This article is exclusive to Pointer — a reading club for developers.
Sign up at Pointer.io for our weekly newsletter.
I think we can all agree that annotations are exciting. In fact, they’re so exciting that many current libraries offer the promise of never having to write actual code again, because their examples all seem to consist purely of annotations instead. I’m sure there’s code in there somewhere, but it’s difficult to see the code trees through the @ forest.
But for all of the power and simplicity of annotations, I still can’t find the ones that I want in my programming life. So here are some proposals.
This annotation expresses a developer’s true, innermost feelings toward an API, nay, toward most APIs they are responsible for. APIs, like tattoos, cannot be easily removed, but can only be obscured, apologized for, and eternally embarrassed about. The @Regretted annotation bears a resemblance to the @Deprecated annotation, but where Deprecated tells the developer to use something else and forget about the older API entirely, the @Regretted is admitting that there is not anything better and that there’s simply nothing to be done but suffer its ugliness.
The @Regretted annotation says to the API user, “We’re very sorry for this [field, method, class, package, library, entire product], but can’t do anything about it now. We know that you will suffer its consequences for generations to come. But know, as you grind your teeth and curse us to coding Hell, that we hate it just as much as you do. Like the parents of a serial killer, we take some awkward responsibility for what we foisted upon the world, but cannot now do anything except watch the consequences with parental squeamishness.”
This annotation looks similar to the standard @Override annotation, which indicates that the current method overrides a method with the same signature in a superclass. But the @Overridden annotation is quite different, expressing the important information that it was implemented at the explicit protest of the author. Typically, an API is designed by engineers and then iterated on internally through design and code reviews, so that the artifacts that external users finally see is a compromise between the original intentions and the set of evolving requirements and constraints. The @Overridden annotation formalizes the notion that, somewhere along the line, the desires of the implementer ran afoul of the decision-makers, and the resulting API represents not the ideal solution, but rather the capricious wishes of those with more political clout in the organization.
This annotation is saying, essentially, “I offer this API to you as evidence of the fact that I disagreed… and lost.”
This annotation look similar to the meta-annotation @Retention, which expresses instructions to the annotation processor about how the marked annotation is supposed to be retained. However, the @Retentive annotation is simply a declaration that the developer put entirely too much thought and care into the associated annotations. The annotation takes an argument which is a constant from the RetentivePolicy class, which currently has only one: RetentivePolicy.ANAL.
This annotation on a method admits that the author does not trust what’s going on with the method’s parameters. Unlike the @SafeVarargs annotation, which is meant as a reassuring nod to the developer using those arguments, @DangerousArgs is a warning to any and all developers that they tread in bug-infested code at their own risk.
The method arguments to such a method could be null, undefined, and completely malicious. Use of this annotation typically indicates many late nights spent by the developer chasing incomprehensible errors, including race conditions, null-pointer exceptions, and ill-conceived temporary workarounds checked-in toward the end of the release without ever knowing what the root causes were. But there’s definitely something fishy going on, probably with these dangerous arguments.
This annotation is an expression of the API developer’s frustration that they inherited this code without having any idea of what it is for, what is is doing, and how it is (or is not) doing its job. Prior developers working on this code not only produced a steaming pile of text worthy of the best obfuscation products and techniques, but they did so without ever bothering to write any comments, public or private, to indicate what was in their brains apart from sadistic thoughts and shots of cheap liquor.
Unlike the standard @Documented annotation, which is merely an indication about documentation compiled by the standard tools, @Undocumented is a warning to the reader that they are completely on their own.
@ Unknown, @NotKnowable
The type annotations @Nullable and @NotNull are useful for informing users of an API about the constraints around permitted parameters and return types. Similarly, @Unknown and @NotKnowable indicate the API developer’s complete confusion over the state of these values. Where @Unknown is a vague statement that the developer is not entirely certain what is allowed or possible, the stronger @NotKnowable is a statement that the developer defies anyone to figure it out.
This annotation is a declaration of the API developer’s seething anger. Sometimes, frustrated and foul-mouthed developers have gone as far as cursing in the code and comments. But in this day of open source, blogs, social media, lawyers, HR departments, and the human desire for not getting fired, the prudent API developer will maintain some sense and decorum, avoid putting their true thoughts into a check-in, count to 0x10, and write, instead, @Unrepeatable next to the API in question.
Unlike the @Repeatable annotation, which is merely about repeated application of the same annotation with different parameters, @Unrepeatable is all about the underlying fury and the thinly-veiled attempt to swear at the code, the team, and the universe.