Robust functionality with minimum verbosity and complexity is a goal for which developers often strive.

To that end, we have refactored the annotations used to indicate which fields are editable for hosts and virtual machines.

The Problem

Prior to the change, the following annotations were used:

For VMs:

  • EditableField
  • EditableHostedEngineField
  • EditableOnVmStatusField

For VM Templates:

  • EditableField
  • EditableOnTemplate

For Hosts:

  • EditableField
  • EditableOnVdsStatus

This lead to fields with an overabundance of individual annotations. For example, take the memSizeMb field in the VmBase class:

@CopyOnNewVersion
@EditableOnVmStatusField(isHotsetAllowed = true)
@EditableOnTemplate
@EditableHostedEngineField
private int memSizeMb;

Logically, each of the Editable* annotations are communicating the same information: the circumstances under which a given field is editable. However, EditableOnVmStatusField and EditableOnHostedEngineField indicate conditions under which a VM entity can be edited, whereas EditableOnTemplate indicates the entity type for which the field is editable (VmTemplate). It is easy to see how this can be a bit confusing for someone reading through this code, especially if they are unfamiliar with it.

The Solution

In the first step of the refactor, new annotation names were created that indicate the entity type for which the annotated field is editable:

  • EditableVmField
  • EditableVdsField
  • EditableVmTemplateField

In the second step, each of the conditions described by the old annotation names were turned into fields in the applicable annotation. For example, the EditableVmField annotation was given three fields: onHostedEngine, hotsetAllowed, and onStatuses.

The third and final step was to address the way entity statuses were handled, as the previous implementation was problematic. EditableOnVmStatus and EditableOnVdsStatus both required each status for which a field is editable to be listed in its onStatus field. Initially, this makes sense, but consider a field that is editable under any status. The obvious long list of statuses aside, this approach requires that the status list for such a field be updated any time a new status is added to the entity. If the individual adding the status isn't aware of this requirement, a silent regression would be introduced.

In addition to this potentially serious problem, the implementation of each annotation provided a default list of statuses to be used if no status list was provided in the annotation declaration. In order to be aware of the existence of these default lists, an individual would have to either be familiar with the implementation code or have been informed by a third party.

In the consolidated version, if no explicit list is provided in the onStatuses field of the EditableVmField and EditableVdsField annotations, it is assumed that the field is editable under all statuses. However, if a list is provided within the annotation declaration, the field will be considered editable only if it is in a status provided in that list. This implementation is clearer, as the onStatuses field will only be used if the statuses list is being restricted, and the restricted list will be displayed alongside the field to which it applies. It also avoids the entity status update problem.

The End State

If we employ the new annotations using our previous memSizeMb example, we get the following:

@CopyOnNewVersion
@EditableVmField(
        onHostedEngine = true,
        hotsetAllowed = true,
        onStatuses = { VMStatus.Down })
@EditableVmTemplateField
private int memSizeMb;

While the line count increased, the conceptual weight of the code has been reduced. The entities for which this field is editable are obvious, and the conditions under which the field is editable for a VM entity are immediately clear.

Note: The line count has only increased due to the styling convention being employed. The same code could be written as:

@CopyOnNewVersion
@EditableVmField(onHostedEngine = true, hotsetAllowed = true, onStatuses = { VMStatus.Down })
@EditableVmTemplateField
private int memSizeMb;

Overall, we feel that this change will provide a cleaner way of representing this information for both the readers and writers of this code.