Pattern-oriented API Refactoring: Addressing Design Smells and Stakeholder Concerns
Abstract
1 Introduction

2 Related Work
3 The Interface Refactoring Catalog (Second Slice)
Refactoring | Design Smells Addressed | Stakeholder Concerns |
---|---|---|
Extract Information Holder (Section 3.1) As an API client, I prefer to retrieve related data elements step-by-step over having to process large structured data sets appearing in a single response message so that I can process individual responses and the data in them quickly and on demand. | God endpoint, data lifetime mismatches, overfetching,sell what is on the truck | #performance, #green-software, #evolvability, #coupling, #data-currentness, #security |
Inline Information Holder (Section 3.2) As the API provider, I want to reduce indirection by embedding referenced information holder resources so that clients have to issue fewer requests when working with linked data. | Underfetching, leaky encapsulation | #performance, #green-software, #usability, #developer-experience, #offline-support |
Extract Operation (Section 3.3) As the API provider, I want to focus the responsibilities of an endpoint on a single role so that a) API clients serving a particular stakeholder group understand the API design intuitively and b) the release roadmap and scaling of the endpoint can be optimized for each group of stakeholders and clients. | Role and/or responsibility diffusion, low cohesion, REST principle(s) constraints, god endpoint, wrong cuts | #reliability, #stability, #single-responsibility-principle, #independent-deployability, #scalability, #security |
Rename Operation (Section 3.4) As an API provider, I want to express the responsibilities of an operation in its name so that client developers, API developers, and non-technical stakeholders (end users, product managers) understand the API — and each other in conversations about the API. | Curse of knowledge, role and/or responsibility diffusion, ill-motivated naming conventions, sloppy naming, cryptic or misleading name | #maintainability, #understandability (including #explainability and #learnability) |
Make Request Conditional (Section 3.5) As an API provider, I want to be able to tell clients that they already have a recent version of some data so that I do not have to send this data again. | High latency/poor response time, spike load, polling proliferation | #performance, #green-software, #data-access-characteristics, #developer-experience, #simplicity |
Encapsulate Context Representation (Section 3.6) As a conversation participant, I want to consolidate all technical metadata in a single place and keep it close to the domain data so that clients and providers can prepare and process it jointly and so that protocols can be switched if that is required to satisfy requirements and constraints that change over time. | Tight coupling to a communication protocol, quality-of-service (QoS) fragmentation and dispersion | #developer-experience, #learnability, #interoperability, #modifiability, #security, #auditability |
Introduce Version Identifier (Section 3.7) As an API provider, I want to communicate versions and their compatibility properties explicitly so that clients can react accordingly on changes that affect them during API evolution. | Tacit semantic changes up to incompatibilities creep in, resistance to change caused by uncertainty | #maintainability, #compatibility, #developer-experience |
3.1 Refactoring: Extract Information Holder
3.1.1 Context and Motivation.
3.1.2 Stakeholder Concerns.
3.1.3 Initial Position Sketch.

3.1.4 Design Smells.
3.1.5 Instructions.
3.1.6 Target Solution Sketch (Evolution Outline).

3.1.7 Example(s).
3.1.8 Hints and Pitfalls to Avoid.
3.1.9 Related Content.
3.2 Refactoring: Inline Information Holder
3.2.1 Context and Motivation.
3.2.2 Stakeholder Concerns.
3.2.3 Initial Position Sketch.

3.2.4 Design Smells.
3.2.5 Instructions.
3.2.6 Target Solution Sketch (Evolution Outline).

3.2.7 Example(s).
curl http://localhost/policies/fvo5pkqerr { "policyId" : "fvo5pkqerr", "customerId" : "rgpp0wkpec", "creationDate" : "2021-07-07T13:40:52.201+00:00", "policyPeriod" : { "startDate" : "2018-02-04T23:00:00.000+00:00", "endDate" : "2018-02-09T23:00:00.000+00:00" }, } curl http://localhost/customers/rgpp0wkpec { "customerId" : "rgpp0wkpec", "firstname" : "Max", "lastname" : "Mustermann", }
curl http://localhost/policies/fvo5pkqerr { "policyId" : "fvo5pkqerr", "customer" : { "customerId" : "rgpp0wkpec", "firstname" : "Max", "lastname" : "Mustermann", }, "creationDate" : "2021-07-07T13:40:52.201+00:00", "policyPeriod" : { "startDate" : "2018-02-04T23:00:00.000+00:00", "endDate" : "2018-02-09T23:00:00.000+00:00" }, }
3.2.8 Hints and Pitfalls to Avoid.
3.2.9 Related Content.
3.3 Refactoring: Extract Operation
3.3.1 Context and Motivation.
3.3.2 Stakeholder Concerns.
3.3.3 Initial Position Sketch.

3.3.4 Design Smells.
3.3.5 Instructions.
3.3.6 Target Solution Sketch (Evolution Outline).

3.3.7 Example.
3.3.8 Hints and Pitfalls to Avoid.
3.3.9 Related Content.
3.4 Refactoring: Rename Operation
3.4.1 Context and Motivation.
3.4.2 Stakeholder Concerns.
3.4.3 Initial Position Sketch.
3.4.4 Design Smells.
3.4.5 Instructions.
3.4.6 Target Solution Sketch (Evolution Outline).
3.4.7 Example.
3.4.8 Hints and Pitfalls to Avoid.
3.4.9 Related Content.
3.5 Refactoring: Make Request Conditional
3.5.1 Context and Motivation.
3.5.2 Stakeholder Concerns.
3.5.3 Initial Position Sketch.

3.5.4 Design Smells.
3.5.5 Instructions.
3.5.6 Target Solution Sketch (Evolution Outline).

3.5.7 Example(s).
3.5.8 Hints and Pitfalls to Avoid.
3.5.9 Related Content.
3.6 Refactoring: Encapsulate Context Representation
3.6.1 Context and Motivation.
3.6.2 Stakeholder Concerns.
3.6.3 Initial Position Sketch.

3.6.4 Design Smells.
3.6.5 Instructions.
3.6.6 Target Solution Sketch (Evolution Outline).

3.6.7 Example(s).
data type KeyValuePair { "name": ID<string>, "property": D<string> }+ endpoint type SampleService exposes operation sampleOperationInitial expecting headers { "apiKey":ID<int>, "sessionId":D<int>?, "otherQosProperties": KeyValuePair* } payload "regularRequestPayload":D<string> delivering payload "someUnspecifiedResponseData"
data type RequestContext { "apiKey":ID<int>, "sessionId":D<int>?, "otherQosProperties": KeyValuePair* } data type KeyValuePair { "name": ID<string>, "property": D<string> }+ endpoint type SampleService exposes operation sampleOperationTarget expecting payload { <<Context_Representation>> "requestContext": RequestContext, <<Data_Element>> "regularRequestPayload":D<string> } delivering payload "someUnspecifiedResponseData"
3.6.8 Hints and Pitfalls to Avoid.
3.6.9 Related Content.
3.7 Refactoring: Introduce Version Identifier
3.7.1 Context and Motivation.
3.7.2 Stakeholder Concerns.
3.7.3 Initial Position Sketch.

3.7.4 Design Smells.
3.7.5 Instructions.
3.7.6 Target Solution Sketch (Evolution Outline).

3.7.7 Example(s).
GET /customers/1234 Accept: text/json+customer; version=1.0
GET /v2/customers/1234
3.7.8 Hints and Pitfalls to Avoid.
3.7.9 Related Content.
4 Summary
Acknowledgments
A Summary of Patterns for API Design
Pattern Name | Pattern Summary (Problem and Solution) |
---|---|
API Description ![]() | Problem: Which knowledge should be shared between an API provider and its clients? How should this knowledge be documented? Solution: Create an API Description that defines request and response message structures, error reporting, and other relevant parts of the technical knowledge to be shared between provider and client. In addition to static and structural information, also cover dynamic or behavioral aspects, including invocation sequences, pre- and postconditions, and invariants. Complement the syntactical interface description with quality management policies as well as semantic specifications and organizational information. |
API Key ![]() | Problem: How can an API provider identify and authenticate clients and their requests? Solution: As an API provider, assign each client a unique token — the API Key — that the client can present to the API endpoint for identification purposes. |
Backend Integration ![]() | Problem: How can distributed applications and their parts, which have been built independently and are deployed separately, exchange data and trigger mutual activity while preserving system-internal conceptual integrity without introducing undesired coupling? Solution: Integrate the backend of a distributed application with one or more other backends (of the same or other distributed applications) by exposing its services via a message-based remote Backend Integration API. |
Community API ![]() | Problem: How can the visibility of and the access to an API be restricted to a closed user group that does not work for a single organizational unit but for multiple legal entities (such as companies, nonprofit/nongovernment organizations, and governments)? Solution: Deploy the API and its implementation resources securely in an access-restricted location so that only the desired user group has access to it — for instance, in an extranet. Share the API Description only with the restricted target audience. |
Conditional Request ![]() | Problem: How can unnecessary server-side processing and bandwidth usage be avoided when frequently invoking API operations that return rarely changing data? Solution: Make requests conditional by adding Metadata Elements to their message representations (or protocol headers) and processing these requests only if the condition specified by the metadata is met. |
Context Representation ![]() | Problem: How can API consumers and providers exchange context information without relying on any particular remoting protocols? How can identity information and quality properties in a request be made visible to related subsequent ones in conversations? Solution: Combine and group all Metadata Elements that carry the desired information into a custom representation element in request and/or response messages. Do not transport this single Context Representation in protocol headers, but place it in the message payload. Separate global from local context in a conversation by structuring the Context Representation accordingly. Position and mark the consolidated Context Representation element so that it is easy to find and distinguish from other Data Elements. |
Data Element ![]() | Problem: How can domain/application-level information be exchanged between API clients and API providers without exposing provider-internal data definitions in the API? How can API client and API provider be decoupled from a data management point of view? Solution: Define a dedicated vocabulary of Data Elements for request and response messages that wraps and/or maps the relevant parts of the data in the business logic of an API implementation. |
Embedded Entity ![]() | Problem: How can one avoid sending multiple messages when their receivers require insights about multiple related information elements? Solution: For any data relationship that the client wants to follow, embed a Data Element in the request or response message that contains the data of the target end of the relationship. Place this Embedded Entity inside the representation of the source of the relationship. |
Information Holder Resource ![]() | Problem: How can domain data be exposed in an API, but its implementation still be hidden? How can an API expose data entities so that API clients can access and/or modify these entities concurrently without compromising data integrity and quality? Solution: Add an Information Holder Resource endpoint to the API, representing a data-oriented entity. Expose create, read, update, delete, and search operations in this endpoint to access and manipulate this entity. In the API implementation, coordinate calls to these operations to protect the data entity. |
Limited Lifetime Guarantee ![]() | Problem: How can a provider let clients know for how long they can rely on the published version of an API? Solution: As an API provider, guarantee to not break the published API for a fixed timeframe. Label each API version with an expiration date. |
Link Element ![]() | Problem: How can API endpoints and operations be referenced in request and response message payloads so that they can be called remotely? Solution: Include a special type of Id Element, a Link Element, to request or response messages. Let these Link Elements act as human- and machine-readable, network-accessible pointers to other endpoints and operations. Optionally, let additional Metadata Elements annotate and explain the nature of the relationship. |
Linked Information Holder ![]() | Problem: How can messages be kept small even when an API deals with multiple information elements that reference each other? Solution: Add a Link Element to messages that pertain to multiple related information elements. Let this Link Element reference another API endpoint that represents the linked element. |
Master Data Holder ![]() | Problem: How can I design an API that provides access to master data that lives for a long time, does not change frequently, and will be referenced from many clients? Solution: Mark an Information Holder Resource to be a dedicated Master Data Holder endpoint that bundles master data access and manipulation operations in such a way that the data consistency is preserved and references are managed adequately. Treat delete operations as special forms of updates. |
Metadata Element ![]() | Problem: How can messages be enriched with additional information so that receivers can interpret the message content correctly, without having to hardcode assumptions about the data semantics? Solution: Introduce one or more Metadata Elements to explain and enhance the other representation elements that appear in request and response messages. Populate the values of the Metadata Elements thoroughly and consistently; process them as to steer interoperable, efficient message consumption and processing. |
Operational Data Holder ![]() | Problem: How can an API support clients that want to create, read, update, and/or delete instances of domain entities that represent operational data: data that is rather short-lived, changes often during daily business operations, and has many outgoing relations? Solution: Tag an Information Holder Resource as Operational Data Holder and add API operations to it that allow API clients to create, read, update, and delete its data often and fast. |
Processing Resource ![]() | Problem: How can an API provider allow its clients to trigger an action in it? Solution: Add a Processing Resource endpoint to the API exposing operations that bundle and wrap application-level activities or commands. |
Rate Limit ![]() | Problem: How can the API provider prevent API clients from excessive API usage? Solution: Introduce and enforce a Rate Limit to safeguard against API clients that overuse the API. |
Retrieval Operation ![]() | Problem: How can information available from a remote party (the API provider, that is) be retrieved to satisfy an information need of an end user or to allow further client-side processing? Solution: Add a read-only operation ro: (in,S) -> out to an API endpoint, which often is an Information Holder Resource, to request a result report that contains a machine-readable representation of the requested information. Add search, filter, and formatting capabilities to the operation signature. |
Semantic Versioning ![]() | Problem: How can stakeholders compare API versions to detect immediately whether they are compatible? Solution: Introduce a hierarchical three-number versioning scheme x.y.z, which allows API providers to denote different levels of changes in a compound identifier. The three numbers are usually called major, minor, and patch versions. |
Service Level Agreement ![]() | Problem: How can an API client learn about the specific quality-of-service characteristics of an API and its endpoint operations? How can these characteristics, and the consequences of not meeting them, be defined and communicated in a measurable way? Solution: As an API product owner, establish a structured, quality-oriented Service Level Agreement that defines testable service-level objectives. |
Two in Production ![]() | Problem: How can a provider gradually update an API without breaking existing clients but also without having to maintain a large number of API versions in production? Solution: Deploy and support two versions of an API endpoint and its operations that provide variations of the same functionality but do not have to be compatible with each other. Update and decommission the versions in a rolling, overlapping fashion. |
Version Identifier ![]() | Problem: How can an API provider indicate its current capabilities as well as the existence of possibly incompatible changes in order to prevent malfunctioning of API clients due to undiscovered interpretation errors? Solution: Introduce an explicit version indicator. Include this Version Identifier in the API Description and in the exchanged messages. To do the latter, add a Metadata Element to the endpoint address, the protocol header, or the message payload. |
Wish List ![]() | Problem: How can an API client inform the API provider at runtime about the data it is interested in? Solution: As an API client, provide a Wish List in the request that enumerates all desired data elements of the requested resource. As an API provider, deliver only those data elements in the response message that are enumerated in the Wish List ("response shaping"). |
Wish Template ![]() | Problem: How can an API client inform the API provider about nested data that it is interested in? How can such preferences be expressed flexibly and dynamically? Solution: Add one or more additional parameters to the request message that mirror the hierarchical structure of the parameters in the corresponding response message. Make these parameters optional or use Boolean as their types so that their values indicate whether or not a parameter should be included. |
Footnotes
References
Index Terms
- Pattern-oriented API Refactoring: Addressing Design Smells and Stakeholder Concerns
Recommendations
API Refactoring to Patterns: Catalog, Template and Tools for Remote Interface Evolution
EuroPLoP '23: Proceedings of the 28th European Conference on Pattern Languages of ProgramsRefactoring is an essential agile practice for software evolution. While program-internal code-level refactoring is well established, architecture-level refactoring has been researched but not yet widely adopted in practice. As a result, application ...
Role-based refactoring of crosscutting concerns
AOSD '05: Proceedings of the 4th international conference on Aspect-oriented software developmentImproving the structure of code can help developers work with a software system more efficiently and more consistently. To aid developers in re-structuring the implementation of crosscutting concerns using aspect-oriented programming, we introduce a ...
Refactoring of Crosscutting Concerns with Metaphor-Based Heuristics
It has been advocated that Aspect-Oriented Programming (AOP) is an effective technique to improve software maintainability through explicit support for modularising crosscutting concerns. However, in order to take the advantages of AOP, there is a need ...
Comments
Information & Contributors
Information
Published In

Publisher
Association for Computing Machinery
New York, NY, United States
Publication History
Check for updates
Author Tags
Qualifiers
- Research-article
Funding Sources
Conference
Acceptance Rates
Contributors
Other Metrics
Bibliometrics & Citations
Bibliometrics
Article Metrics
- 0Total Citations
- 204Total Downloads
- Downloads (Last 12 months)204
- Downloads (Last 6 weeks)89
Other Metrics
Citations
View Options
View options
View or Download as a PDF file.
PDFeReader
View online with eReader.
eReaderHTML Format
View this article in HTML Format.
HTML FormatLogin options
Check if you have access through your login credentials or your institution to get full access on this article.
Sign in