Rename Representation Element

Updated: Published: EuroPLoP 2023

also known as: Rename Parameter, Rename Payload Part

Context and Motivation

An API endpoint operation expects data from clients and/or delivers data to them. The data representations are structured, and certain parts of the structural elements are named (for instance, keys in key-value pairs or type definitions).

As a data modeler and/or Data Transfer Object (DTO) designer, I want to use expressive and domain-specific terminology so that data representations and their elements become self-explanatory to API developers on the client as well as on the provider side.

Stakeholder Concerns (including Quality Attributes and Design Forces)

#understandability, #explainability, #learnability
A well-chosen name expresses what a representation element (such as a JSON object with its properties) has to offer, which helps clients decide whether and how to use it. The named element has a single meaning and purpose.
#maintainability
The more expressive and meaningful a name (for instance, the key in a key-value pair) is, the easier it is to search and navigate the code base, for instance, when fixing bugs and adding features during API evolution. Split Operation covers this quality in more detail.

Initial Position Sketch

Let us assume the following simple endpoint design (notation: abstract MDSL):

endpoint type Endpoint1
exposes 
  operation operation1 
    expecting payload {
      "v1":D<string>,
      "v2":D<int>}
    // one-way exchange in this example, 
    // so no response message

As the sketch shows, the refactoring targets a single endpoint and one of its operations. Presumably, the v in v1 and v2 stands for “value”, but this does not get clear from the terse one-letter, one-number acronyms v1 and v2.

Design Smells

Cryptic or misleading name
The chosen element name is difficult to understand for stakeholders unfamiliar with the API implementation. For instance, it might not be part of the agreed-upon domain vocabulary or unveil implementation details such as column names in database tables. It might also be ambiguous and overloaded with different meanings (in the same context).
Security by obscurity
Sometimes, it is argued that unlabeled, undocumented data is harder to tamper with. But such a tactic alone does not qualify as a sound security solution. It harms maintainability because it increases the risk of introducing bugs because of a lack of clarity for maintainers and auditors, not just attackers.
Change log jitter or commit chaos
The name has been, and continues to be, frequently changed according to the logs kept by the version control system. Frequent changes indicate that the domain language is not yet stable or has not yet been defined, communicated, and agreed upon sufficiently.
Leaky encapsulation and high coupling
Program-internal names or identifiers might accidentally have leaked into the API. For example, the initial API could have been generated from internal classes. For API clients, such internal names might be hard to understand.

Instructions (Steps)

This refactoring is rather straightforward to apply:

  1. Discuss alternative new names and decide on one. Have this decision reviewed and agreed upon.
  2. Apply code-level rename refactorings; do not forget to update code-level comments when doing so. If the code is generated from a specification, update the API design specification and then re-generate the code.
  3. Update API tests and API Description.1

Several strategies for backward compatibility exist. The implementation could continue to accept the old names as well. However, this approach can quickly become unwieldy if many such changes accumulate over time. By applying the Introduce Version Mediator refactoring, clients can continue using the old names which will then get translated by the version mediator to the new names.

Target Solution Sketch (Evolution Outline)

The element names v1 and v2 in the initial position have been replaced by customerName and customerID in this MDSL sketch:

endpoint type Endpoint1
exposes 
  operation operation1 
    expecting payload {
      "customerName":Data<string>,
      "customerID":Data<int>
    } 

Arguably, customerName is more expressive than v1 unless the representation element is a simple loop counter (which does not seem to be the case). This renaming makes the API specification easier to understand, assuming that a common understanding of the term “customer” has been reached within a context and community. operation1 remains generic in the above sketch, which makes it a candidate target for Rename Operation.

Example(s)

In the Policy Management backend of the Lakeside Mutual sample application, we find a Data Transfer Object (DTO) called InsuranceQuoteDto, which is exposed in the Web API that is provided by a RESTful HTTP controller:

public class InsuranceQuoteDto {
    @Valid
    @NotNull
    private Date expirationDate;

    @Valid
    @NotNull
    private MoneyAmountDto insurancePremium;

    @Valid
    @NotNull
    private MoneyAmountDto policyLimit;

    ...
}

The name InsuranceQuoteDto as well as expirationDate, insurancePremium, and policyLimit are all expressive names; names such as inputData, date, and amount would be less domain-specific and could be renamed with this refactoring.

One could argue that abbreviations and technical terms should be avoided in API naming. Removing Dto from the name in a refactoring would make the name shorter and cleaner but also hide the architectural role played by the class and go against the “architecturally-evident coding style” recommended by Fairbanks [2010]. In this particular case, the acronym is explained in a comment in the source code.

Hints and Pitfalls to Avoid

Coming up with good names is challenging, and some authors consider it one of the hardest problems in software engineering [Benner 2023]. When choosing names, keep in mind that good names should:

  • Precisely reveal the purpose/intent of the named element.
  • Be intelligible, so they do not need to be deciphered first.
  • Be pronounceable so that they can be talked about.
  • Be as simple and short as possible, but not shorter.
  • Adhere to the conventions of the interface specification and/or programming language in use.

When applying this refactoring, one should promote a controlled amount of domain vocabulary into the published language of the API. Optionality and value ranges should be explained in the API documentation. Comments in machine-readable specifications and specification documents targeting humans are valid locations for this information. The reuse of already existing data structures and standard vocabularies may be considered if that is permitted, for instance, microformats or schema.org2.

Mapping implementation-level data structures one-to-one introduces undesired tight coupling between API and implementation and should therefore be avoided. Implementation-level data structures should be wrapped or mapped (see Introduce Data Transfer Object) to hide implementation details.

It makes sense to avoid technical jargon (in particular jargon that might go out of fashion soon or has a different meaning in other communities already). As already touched upon in the example, abbreviations should be avoided in names unless they are widespread in the API stakeholder community. Humor is a good thing, of course, but technical specifications are not necessarily a good place to (try to) be funny; ethical codes of conduct must not be violated when choosing names (e.g., [CoC:ACM]).

Representation elements can be grouped by applying Introduce Data Transfer Object. Rename Operation and Rename Endpoint are available to rename API parts with a larger scope. The Id Element pattern [PatternsForAPIDesign:2022] makes naming suggestions (for instance, regarding uniqueness) and provides pointers to additional information. Data Element is the general pattern for any kind of representation element.

The book “The Programmer’s Brain: What every programmer needs to know about cognition” by Hermans [2021] has a chapter on “How to get better at naming things” that explains the cognitive processes involved in reading names in code and provides practical advice.

Fowler [2018] covers various rename refactorings, such as “Rename Variable” and “Rename Field.” And “The Art of Readable Code” [Dustin Boswell 2011] has helpful hints on naming program elements that also apply to APIs. Benner [2023] provides rich advice as well.

“API Handyman” Arnaud Lauret provides many examples and counterexamples in his book [Lauret 2019] and the online API Stylebook. Applying this refactoring might be the result of an API review, and a new name then has to be decided upon. “A Checklist for API Design Review” is available online.

The blog post “A Definition of Done for Architectural Decisions” presents five criteria to assess whether a decision such as a name change has been elaborated upon sufficiently: evidence, criteria, agreement, documentation, and realization/review.

References

Benner, Tom. 2023. Naming Things: The Hardest Problem in Software Engineering. Independently published.

Dustin Boswell, Trevor Foucher. 2011. The Art of Readable Code. O’Reilly Media, Inc.

Fairbanks, G. 2010. Just Enough Software Architecture: A Risk-Driven Approach. Marshall & Brainerd.

Fowler, Martin. 2018. Refactoring: Improving the Design of Existing Code. 2nd ed. Addison-Wesley Signature Series (Fowler). Boston, MA: Addison-Wesley.

Hermans, F. 2021. The Programmer’s Brain: What Every Programmer Needs to Know about Cognition. Manning.

Lauret, Arnaud. 2019. The Design of Web APIs. Manning.

  1. Step 3 applies to all refactorings and is part of the general Test, Explain, Let Know and Learn (TELL) activities to enact and evaluate refactorings. TELL is featured on the Content Overview page. 

  2. Example: how should we name and structure the attribute to contain a user address? Easy, use the already existing definition https://schema.org/PostalAddress