Introduction

The C++ guidelines are for the benefit of client library designers targeting service applications written in C++. You do not have to write a client library for C++ if your service is not normally accessed from C++.

Design Principles

The Azure SDK should be designed to enhance the productivity of developers connecting to Azure services. Other qualities (such as completeness, extensibility, and performance) are important but secondary. Productivity is achieved by adhering to the principles described below:

Idiomatic

  • The SDK should follow the general design guidelines and conventions for the target language. It should feel natural to a developer in the target language.
  • We embrace the ecosystem with its strengths and its flaws.
  • We work with the ecosystem to improve it for all developers.

Consistent

  • Client libraries should be consistent within the language, consistent with the service and consistent between all target languages. In cases of conflict, consistency within the language is the highest priority and consistency between all target languages is the lowest priority.
  • Service-agnostic concepts such as logging, HTTP communication, and error handling should be consistent. The developer should not have to relearn service-agnostic concepts as they move between client libraries.
  • Consistency of terminology between the client library and the service is a good thing that aids in diagnosability.
  • All differences between the service and client library must have a good (articulated) reason for existing, rooted in idiomatic usage rather than whim.
  • The Azure SDK for each target language feels like a single product developed by a single team.
  • There should be feature parity across target languages. This is more important than feature parity with the service.

Approachable

  • We are experts in the supported technologies so our customers, the developers, don’t have to be.
  • Developers should find great documentation (hero tutorial, how to articles, samples, and API documentation) that makes it easy to be successful with the Azure service.
  • Getting off the ground should be easy through the use of predictable defaults that implement best practices. Think about progressive concept disclosure.
  • The SDK should be easily acquired through the most normal mechanisms in the target language and ecosystem.
  • Developers can be overwhelmed when learning new service concepts. The core use cases should be discoverable.

Diagnosable

  • The developer should be able to understand what is going on.
  • It should be discoverable when and under what circumstances a network call is made.
  • Defaults are discoverable and their intent is clear.
  • Logging, tracing, and exception handling are fundamental and should be thoughtful.
  • Error messages should be concise, correlated with the service, actionable, and human readable. Ideally, the error message should lead the consumer to a useful action that they can take.
  • Integrating with the preferred debugger for the target language should be easy.

Dependable

  • Breaking changes are more harmful to a user’s experience than most new features and improvements are beneficial.
  • Incompatibilities should never be introduced deliberately without thorough review and very strong justification.
  • Do not rely on dependencies that can force our hand on compatibility.

General Guidelines

DO follow the General Azure SDK Guidelines.

Support for non-HTTP Protocols

This document contains guidelines developed primarily for typical Azure REST services, i.e. stateless services with request-response based interaction model. Many of the guidelines in this document are more broadly applicable, but some might be specific to such REST services.

Azure SDK API Design

The API surface of your client library must have the most thought as it is the primary interaction that the consumer has with your service.

DO use clear, concise, and meaningful names.

⚠️ YOU SHOULD NOT use abbreviations unless necessary or when they are commonly used and understood. For example, az is allowed since it is commonly used to mean Azure, and iot is used since it is a commonly understood industry term. However, using kv for Key Vault would not be allowed since kv is not commonly used to refer to Key Vault.

DO consult the Architecture Board if you wish to use a dependency that is not on the list of approved dependencies.

Service Client

Service clients are the main starting points for developers calling Azure services with the Azure SDK. Each client library should have at least one client in its main namespace, so it’s easy to discover. The guidelines in this section describe patterns for the design of a service client.

There exists a distinction that must be made clear with service clients: not all classes that perform HTTP (or otherwise) requests to a service are automatically designated as a service client. A service client designation is only applied to classes that are able to be directly constructed because they are uniquely represented on the service. Additionally, a service client designation is only applied if there is a specific scenario that applies where the direct creation of the client is appropriate. If a resource can not be uniquely identified or there is no need for direct creation of the type, then the service client designation should not apply.

DO name service client types with the Client suffix (for example, ConfigurationClient).

DO place service client types that the consumer is most likely to interact with in the root namespace of the client library (for example, Azure::<group>::<service>). Specialized service clients should be placed in sub-packages.

DO make service clients classes, not structs.

DO ensure that all service client classes thread safe (usually by making them immutable and stateless).

DO expose a GetUrl() method which returns the URL.

Service Client Constructors

DO provide a minimal constructor that takes only the parameters required to connect to the service.

TODO: Add service client factory pattern examples for connection strings.

⛔️ DO NOT use default parameters in the simplest constructor.

DO provide constructor overloads that allow specifying additional options via an options parameter. The type of the parameter is typically a subclass of ClientOptions type, shown below.

Client Configuration

DO use relevant global configuration settings either by default or when explicitly requested to by the user, for example by passing in a configuration object to a client constructor.

DO allow different clients of the same type to use different configurations.

DO allow consumers of your service clients to opt out of all global configuration settings at once.

DO allow all global configuration settings to be overridden by client-provided options. The names of these options should align with any user-facing global configuration keys.

⛔️ DO NOT Change the default values of client configuration options based on system or program state.

⛔️ DO NOT Change default values of client configuration options based on how the client library was built.

⛔️ DO NOT change behavior based on configuration changes that occur after the client is constructed. Hierarchies of clients inherit parent client configuration unless explicitly changed or overridden. Exceptions to this requirement are as follows:

  1. Log level, which must take effect immediately across the Azure SDK.
  2. Tracing on/off, which must take effect immediately across the Azure SDK.

⛔️ DO NOT use client library specific runtime configuration such as environment variables or a config file. Keep in mind that many IoT devices won’t have a filesystem or an “environment block” to read from.

Using ClientOptions

TODO: This section needs to be driven by code in the Core library.

Service Versions

TODO: This section needs to be driven by code in the Core library.

DO call the highest supported service API version by default.

DO allow the consumer to explicitly select a supported service API version when instantiating the service client.

Use a constructor parameter called version on the client options type.

  • The version parameter must be the first parameter to all constructor overloads.
  • The version parameter must be required, and default to the latest supported service version.
  • The type of the version parameter must be ServiceVersion; an enum nested in the options type.
  • The ServiceVersion enum must use explicit values starting from 1.
  • ServiceVersion enum value 0 is reserved. When 0 is passed into APIs, ArgumentException should be thrown.
Mocking

TODO: This section needs to be driven by code in the Core library.

Service Methods

TODO: This section needs to be driven by code in the Core library.

Service methods are the methods on the client that invoke operations on the service.

Sync and Async

The C++ SDK is designed for synchronous api calls.

DO provide a synchronous programming model.

⛔️ DO NOT provide an async programming model.

TODO: This section needs to be driven by code in the Core library.

Naming
  • PascalCase identifiers start with an uppercase letter and then use additional capital letters to divide words. Acronyms and initialisms are capitalized if they are 2 letters or shorter; otherwise only the first letter is capitalized. For example, PascalCase, HttpRequest, JsonParser, or IOContext.

  • camelCase identifiers start with a lowercase letter and then use additional capital letters to divide words. Acronyms and initialisms that start an identifier are all lowercase if they begin an identifier, otherwise they follow the same 2 letters rule as PascalCase. For example, camelCase, httpRequest, processHttp, ioContext, startIO.

  • ALL_CAPITAL_SNAKE_CASE identifiers are composed of entirely capital letters and divide words with underscores.

DO name functions with PascalCase. For example:

namespace Azure { namespace Group { namespace Service {
namespace _detail {
// Part of the private API
[[nodiscard]] int64_t ComputeHash(int32_t a, int32_t b) noexcept;
} // namespace _detail

// Part of the public API
[[nodiscard]] CatHerdClient CatHerdCreateClient(char* herdName);

// Bad - private API in public namespace.
[[nodiscard]] int64_t ComputeHash(int32_t a, int32_t b) noexcept;
}}} // namespace Azure::Group::Service

☑️ YOU SHOULD declare all functions that can never throw exceptions noexcept.

DO name namespace scope variables intended for user consumption with PascalCase.

DO name namespace scope const or constexpr variables intended for user consumption with PascalCase.

DO name namespace scope non-constant variables intended only for internal consumption with a g_ prefix followed by camelCase. For example, g_applicationContext. Note that all such cases will be in a _detail namespace or an unnamed namespace.

DO name local variables and parameters with camelCase.

// Examples of the above naming rules:

namespace Azure { namespace Group { namespace Service {
int PublicNamespaceScopeVariable; // these should be used sparingly
const int PublicNamespaceScopeConstant = 42;
constexpr int OtherPublicNamespaceScopeConstant = 42;
constexpr char * PublicNamespaceScopeConstantPointer = nullptr; // const pointer to modifiable

void Function(int parameterName) {
    int localName;
}

namespace _detail {
    extern int g_internalUseGlobal;
} // namespace _detail

}}} // namespace Azure::Group::Service

DO use types to enforce units where possible. For example, the C++ standard library provides std::chrono which makes time conversions automatic.

// Bad
uint32 Timeout;

// Good
std::chrono::milliseconds Timeout;

DO include units in names when a type based solution to enforce units is not present. If a variable represents weight, or some other unit, then include the unit in the name so developers can more easily spot problems. For example:

// Bad
uint32 Timeout;
uint32 MyWeight;

// Good
std::chrono::milliseconds Timeout;
uint32 MyWeightKg;

DO Declare or define each variable on its own line, except when declaring bitfields. An exception can be made when declaring bitfields (to clarify that the variable is a part of one bitfield). The use of bitfields in general is discouraged.

Cancellation

DO ensure all service methods, both asynchronous and synchronous, take an optional Context parameter called context.

The context should be further passed to all calls that take a context. DO NOT check the context manually, except when running a significant amount of CPU-bound work within the library, e.g. a loop that can take more than a typical network call.

Mocking

TODO: This section needs to be driven by code in the Core library.

Return Types

DO return Response<T> or Response from synchronous methods.

T represents the content of the response, as described below.

The T can be either an unstructured payload (e.g. bytes of a storage blob) or a model type representing deserialized response content.

DO use one of the following return types to represent an unstructured payload:

TODO: What type should be used for large streaming payloads?

  • TODO - for large payloads
  • byte[] - for small payloads

DO return a model type if the content has a schema and can be deserialized.

For more information, see Model Types

Requests to the service fall into two basic groups - methods that make a single logical request, or a deterministic sequence of requests. An example of a single logical request is a request that may be retried inside the operation. An example of a deterministic sequence of requests is a paged operation.

The logical entity is a protocol neutral representation of a response. For HTTP, the logical entity may combine data from headers, body and the status line. A common example is exposing an ETag header as a property on the logical entity in addition to any deserialized content from the body.

DO optimize for returning the logical entity for a given request. The logical entity MUST represent the information needed in the 99%+ case.

DO make it possible for a developer to get access to the complete response, including the status line, headers and body. The client library MUST follow the language specific guidance for accomplishing this.

For example, you may choose to do something similar to the following:

namespace Azure { namespace Group { namespace Service {
struct JsonShortItem {
    // JSON decoded structure.
};

struct JsonShortPagedResults {
    uint32 Size;
    JsonShortItem* Items;
};

struct JsonShortRawPagedResults {
    HTTP_HEADERS* Headers;
    uint16 StatusCode;
    byte* RawBody;
    JsonShortPagedResults* Results;
};

class ShortItemsClient {
    JsonShortPagedResults* JsonGetShortListItems() const;
    JsonShortRawPagedResults* JsonGetShortListItemsWithResponse(client, /* extra params */);
};
}}} // namespace Azure::Group::Service

DO document and provide examples on how to access the raw and streamed response for a given request, where exposed by the client library. We do not expect all methods to expose a streamed response.

For methods that combine multiple requests into a single call:

⛔️ DO NOT return headers and other per-request metadata unless it is obvious as to which specific HTTP request the methods return value corresponds to.

DO provide enough information in failure cases for an application to take appropriate corrective action.

Thread Safety

DO be thread-safe. All public members of the client type must be safe to call from multiple threads concurrently.

Service Method Parameters

TODO: This section needs to be driven by code in the Core library.

Parameter Validation

The service client will have several methods that perform requests on the service. Service parameters are directly passed across the wire to an Azure service. Client parameters are not passed directly to the service, but used within the client library to fulfill the request. Examples of client parameters include values that are used to construct a URI, or a file that needs to be uploaded to storage.

DO validate client parameters.

⛔️ DO NOT validate service parameters. This includes null checks, empty strings, and other common validating conditions. Let the service validate any request parameters.

DO validate the developer experience when the service parameters are invalid to ensure appropriate error messages are generated by the service. If the developer experience is compromised due to service-side error messages, work with the service team to correct prior to release.

Methods Returning Collections (Paging)

Although object-orientated languages can eschew low-level pagination APIs in favor of high-level abstractions, C acts as a lower level language and thus embraces pagination APIs provided by the service. You should work within the confines of the paging system provided by the service.

DO export the same paging API as the service provides.

DO indicate in the return type if the consumer has reached the end of the result set.

DO indicate in the return type how many items were returned by the service, and have a list of those items for the consumer to iterate over.

Methods Invoking Long Running Operations

Some service operations, known as Long Running Operations or LROs take a long time (up to hours or days). Such operations do not return their result immediately, but rather are started, their progress is polled, and finally the result of the operation is retrieved.

Azure::Core library exposes an abstract type called Operation<T>, which represents such LROs and supports operations for polling and waiting for status changes, and retrieving the final operation result. A service method invoking a long running operation will return a subclass of Operation<T>, as shown below.

TODO: This section needs to be driven by code in the Core library.

DO name all methods that start an LRO with the Start prefix.

DO return a subclass of Operation<T> from LRO methods.

✔️ YOU MAY add additional APIs to subclasses of Operation<T>. For example, some subclasses add a constructor allowing to create an operation instance from a previously saved operation ID. Also, some subclasses are more granular states besides the IsDone and HasValue states that are present on the base class.

Conditional Request Methods

TODO: This section needs to be driven by code in the Core library.

Hierarchical Clients

TODO: This section needs to be driven by code in the Core library.

Supporting Types

In addition to service client types, Azure SDK APIs provide and use other supporting types as well.

Model Types

This section describes guidelines for the design model types and all their transitive closure of public dependencies (i.e. the model graph). A model type is a representation of a REST service’s resource.

DO ensure model public properties are const if they aren’t intended to be changed by the user.

Most output-only models can be fully read-only. Models that are used as both outputs and inputs (i.e. received from and sent to the service) typically have a mixture of read-only and read-write properties.

TODO: This section needs to be driven by code in the Core library.

Model Type Naming

TODO: This section needs to be driven by code in the Core library.

Enumerations

TODO: This section needs to be driven by code in the Core library.

DO use an enum for parameters, properties, and return types when values are known.

✔️ YOU MAY use a struct in place of an enum class that declares well-known fields but can contain unknown values returned from the service, or user-defined values passed to the service.

See enumeration-like structure documentation for implementation details.

Using Azure Core Types

The azure-core package provides common functionality for client libraries. Documentation and usage examples can be found in the azure/azure-sdk-for-cpp repository.

Using Primitive Types

TODO: This section needs to be driven by code in the Core library.

DO use Azure::Core::ETag to represent ETags.

The Azure::Core::ETag type is located in azure-core package.

DO use Azure::Core::Uri to represent URIs.

The Azure::Core::Uri type is located in azure-core package.

Exceptions

Error handling is an important aspect of implementing a client library. It is the primary method by which problems are communicated to the consumer. Because we intend for the C client libraries to be used on a wide range of devices with a wide range of reliability requirements, it’s important to provide robust error handling.

We distinguish between several different types of errors:

  • Exhaustion / Act of God
    errors like running out of stack space, or dealing with power failure that, in general, can not be anticipated and after which it may be hard to execute any more code, let alone recover. Code handling these errors needs to be written to very specific requirements, for example not doing any allocations and never growing the stack.
  • Pre-Conditions
    Pre-Condition errors occur when a caller violates the expectations of a function, for example by passing an out-of-range value or a null pointer. These are always avoidable by the direct caller, and will always require a source code change (by the caller) to fix.
  • Post-Conditions
    Post-Condition violations happen when some function didn’t do the correct thing, these are always bugs in the function itself, and users shouldn’t be expected to handle them.
  • Heap Exhaustion (Out of Memory)
    Running out of memory.
  • Recoverable Error
    Things like trying to open a file that doesn’t exist, or trying to write to a full disk. These kinds of errors can usually be handled by a function’s caller directly, and need to be considered by callers that want to be robust.

Exhaustion / Act of God

⛔️ DO NOT return an error to the caller.

DO crash, if possible. This means calling some form of fast failing function, like abort.

Note: if your client library needs to be resilient to these kinds of errors you must either provide a fallback system, or construct your code in a way to facilitate proving that such errors can not occur.

Pre-conditions

✔️ YOU MAY check preconditions on function entry.

✔️ YOU MAY privide a means to disable precondition checks in release / optimized builds.

DO crash, if possible. This means calling some form of fast failing function, like abort.

⛔️ DO NOT throw a C++ exception.

Post Conditions

⚠️ YOU SHOULD NOT check post-conditions in a way that changes the computational complexity of the function.

DO provide a way to disable postcondition checks, and omit checking code from built binaries.

DO crash, if possible. This means calling some form of fast failing function, like abort.

⛔️ DO NOT throw a C++ exception.

Heap Exhaustion (Out of Memory)

✔️ YOU MAY crash. For example, this may mean dereferencing a nullptr returned by malloc, or explicitly checking and calling abort.

Note that on some comonly deployed platforms like Linux, handling heap exhaustion from user mode is not possible in a default configuration.

✔️ YOU MAY propagate a C++ exception of type std::bad_alloc when encountering an out of memory condition. We do not expect the program to continue in a recoverable state after this occurs. Note that most standard library facilities and the built in operator new do this automatically, and we want to allow use of other facilities that may throw here.

⛔️ DO NOT throw bad_alloc from the SDK code itself.

Recoverable errors

DO report errors by throwing C++ exceptions defined in the Azure C++ Core Library.

TODO: The Core library needs to provide exceptions for the common failure modes, e.g. the same values as az_result in the C SDK.

For example:

class Herd {
  bool m_hasShyCats;
  int m_numCats;
public:
  void CountCats(int* cats) {
    if(m_hasShyCats) {
      throw std::runtime_error("shy cats are not allowed");
    }
    *cats = m_numCats;
  }
};

DO produce a recoverable error when any HTTP request fails with an HTTP status code that is not defined by the service/Swagger as a successful status code.

DO document all exceptions each function and its transitive dependencies may throw, except for std::bad_alloc.

C++ Exceptions

⛔️ DO NOT throw exceptions, except those from the Azure C++ Core library as described in the error handling section.

DO propagate exceptions thrown by user code, callbacks, or dependencies. Assume any user-provided callback will propagate C++ exceptions unless the SDK documents that the callback must be completely non-throwing.

template<class Callback>
void ApiFunc(const Callback& c) {
    // best
    c();

    // allowed, but results in a less pretty debugging experience for customers
    try {
        c();
    } catch (...) {
        // clean up
        throw; // throw; rethrows the original exception object
    }

    // prohibited
    try {
        c();
    } catch (...) {
        // missing throw;
    }
}

Authentication

Azure services use a variety of different authentication schemes to allow clients to access the service. Conceptually, there are two entities responsible in this process: a credential and an authentication policy. Credentials provide confidential authentication data. Authentication policies use the data provided by a credential to authenticate requests to the service.

DO support all authentication techniques that the service supports and are available to a client application (as opposed to service side). C is used only for client applications when talking to Azure, so some authentication techniques may not be valid.

DO use credential and authentication policy implementations from the Azure Core library where available.

DO provide credential types that can be used to fetch all data needed to authenticate a request to the service in a non-blocking atomic manner for each authentication scheme that does not have an implementation in Azure Core.

DO provide service client constructors or factories that accept any supported authentication credentials.

⚠️ YOU SHOULD NOT support providing credential data via a connection string. Connection string interfaces should be provided ONLY IF the service provides a connection string to users via the portal or other tooling.

⛔️ DO NOT support constructing a service client with a connection string unless such connection string. Provide a CreateFromConnectionString static member function which returns a client instead to encourage customers to choose non-connection-string-based authentication.

When implementing authentication, don’t open up the consumer to security holes like PII (personally identifiable information) leakage or credential leakage. Credentials are generally issued with a time limit, and must be refreshed periodically to ensure that the service connection continues to function as expected. Ensure your client library follows all current security recommendations and consider an independent security review of the client library to ensure you’re not introducing potential security problems for the consumer.

⛔️ DO NOT persist, cache, or reuse security credentials. Security credentials should be considered short lived to cover both security concerns and credential refresh situations.

If your service implements a non-standard credential system (one that is not supported by Azure Core), then you need to produce an authentication policy for the HTTP pipeline that can authenticate requests given the alternative credential types provided by the client library.

DO Use a “secure” function to zero authentication or authorization credentials as soon as possible once they are no longer needed. Examples of such functions include: SecureZeroMemory, memset_s, and explicit_bzero. Examples of insecure functions include memset. An optimizer may notice that the credentials are never accessed again, and optimize away the call to memset, resulting in the credentials remaining in memory.

DO provide a suitable authentication policy that authenticates the HTTP request in the HTTP pipeline when using non-standard credentials. This includes custom connection strings, if supported.

Namespaces

Grouping services within a cloud infrastructure is common since it aids discoverability and provides structure to the reference documentation.

DO name namespaces using PascalCase.

DO use a root namespace of the form Azure::<Group>::<Service>[::<Feature>].

For example, Azure::Storage::Blobs or Azure::Storage::Files::Shares

DO use one of the following pre-approved namespace groups:

  • Azure::AI for artificial intelligence, including machine learning
  • Azure::Analytics for client libraries that gather or process analytics data
  • Azure::Communication communication services
  • Azure::Core for libraries that aren’t service specific
  • Azure::Cosmos for object database technologies
  • Azure::Data for client libraries that handle databases or structured data stores
  • Azure::DigitalTwins for DigitalTwins related technologies
  • Azure::Identity for authentication and authorization client libraries
  • Azure::IoT for client libraries dealing with the Internet of Things.
    • Use Iot for Pascal cased compound words, such as IotClient, otherwise follow language conventions.
    • Do not use IoT more than once in a namespace.
  • Azure::Management for client libraries accessing the control plane (Azure Resource Manager)
  • Azure::Media for client libraries that deal with audio, video, or mixed reality
  • Azure::Messaging for client libraries that provide messaging services, such as push notifications or pub-sub.
  • Azure::Search for search technologies
  • Azure::Security for client libraries dealing with security
  • Azure::Storage for client libraries that handle unstructured data

If you think a new group should be added to the list, contact [adparch].

DO pick a shortened service name that allows the consumer to tie the package to the service being used. As a default, use the compressed service name. The namespace does NOT change when the branding of the product changes, so avoid the use of marketing names that may change.

A compressed service name is the service name without spaces. It may further be shortened if the shortened version is well known in the community. For example, “Azure Media Analytics” would have a compressed service name of MediaAnalytics, whereas “Azure Service Bus” would become ServiceBus.

DO use the following list as the group of services:

Namespace Group Functional Area
AI Artificial intelligence, including machine learning
Analytics Gathering data for metrics or usage
Data Dealing with structured data stores like databases
Diagnostics Gathering data for diagnosing issues
DigitalTwins Digital Twins, digital representations of physical spaces and IoT devices
Identity Authentication and authorization
IoT Internet of Things
Management Control Plane (Azure Resource Manager)
Media Audio, video, or mixed reality
Messaging Messaging services, like push notifications or pub-sub
Search Search technologies
Security Security and cryptography
Storage Storage of unstructured data

If the client library does not seem to fit into the group list, contact the Architecture Board to discuss the namespace requirements.

DO place the management (Azure Resource Manager) API in the Management group. Use the grouping Azure::Management::<Group>::<Service> for the namespace. Since more services require control plane APIs than data plane APIs, other namespaces may be used explicitly for control plane only. Data plane usage is by exception only. Additional namespaces that can be used for control plane SDKs include:

Namespace Group Functional Area
Appmodel Application models, such as Functions or App Frameworks
Compute Virtual machines, containers, and other compute services
Integration Integration services (such as Logic Apps)
Management Management services (such as Cost Analytics)
Networking Services such as VPN, WAN, and Networking

Many management APIs do not have a data plane because they deal with management of the Azure account. Place the management library in the Azure::Management namespace. For example, use Azure::Management::CostAnalysis instead of Azure::Management::Management::CostAnalysis.

⛔️ DO NOT choose similar names for clients that do different things.

DO register the chosen namespace with the Architecture Board. Open an issue to request the namespace. See the registered namespace list for a list of the currently registered namespaces.

DO place private implementation details in a _detail namespace.

namespace Azure { namespace Group { namespace Service {
namespace _detail {
// Part of the private API
struct HashComputation {
    int InternalBookkeeping;
};

const int g_privateConstant = 1729;
} // namespace _detail

// Part of the public API
struct UploadBlobRequest {
    unsigned char* Data;
    size_t DataLength;
};

// Bad - private API in public namespace.
struct HashComputation {
    int InternalBookkeeping;
};
const int g_privateConstant = 1729;
}}} // namespace Azure::Group::Service

Example Namespaces

Here are some examples of namespaces that meet these guidelines:

  • Azure::Data::Cosmos
  • Azure::Identity::ActiveDirectory
  • Azure::IoT::DeviceProvisioning
  • Azure::Storage::Blobs
  • Azure::Messaging::NotificationHubs (the client library for Notification Hubs)
  • Azure::Management::Messaging::NotificationHubs (the management library for Notification Hubs)

Here are some namespaces that do not meet the guidelines:

  • microsoft::azure::CosmosDB (not in the Azure namespace and does not use grouping, uses lowercase letters)
  • azure::mixed_reality::kinect (the grouping is not in the approved list and uses snake_case)
  • Azure::IoT::IoTHub::DeviceProvisioning (too many levels in the group)

Support for Mocking

TODO: This section needs to be driven by code in the Core library.

Azure SDK Library Design

Packaging

☑️ YOU SHOULD provide both dynamic and static linking options for your library. Each has its own merits and use cases.

DO publish your package in source code format. Due to differences in platforms, this is the most common publishing mechanism for C libraries.

DO publish your package to vcpkg, a C++ library manager supporting Windows, Linux, and MacOS.

Common Libraries

TODO: This section needs to be driven by code in the Core library.

Versioning

Client Versions

DO be 100% backwards compatible with older versions of the same package.

DO introduce a new package (with new assembly names, new namespace names, and new type names) if you must do an API breaking change.

Breaking changes should happen rarely, if ever. Register your intent to do a breaking change with [adparch]. You’ll need to have a discussion with the language architect before approval.

Package Version Numbers

Consistent version number scheme allows consumers to determine what to expect from a new version of the library.

DO use MAJOR.MINOR.PATCH format for the version of the library dll and the NuGet package.

Use -beta._N suffix for beta package versions. For example, 1.0.0-beta.2.

DO change the version number of the client library when ANYTHING changes in the client library.

DO increment the patch version when fixing a bug.

⛔️ DO NOT include new APIs in a patch release.

DO increment the major or minor version when adding support for a service API version.

DO increment the major or minor version when adding a new method to the public API.

☑️ YOU SHOULD increment the major version when making large feature changes.

DO select a version number greater than the highest version number of any other released Track 1 package for the service in any other scope or language.

Dependencies

Dependencies bring in many considerations that are often easily avoided by avoiding the dependency.

  • Versioning - Many programming languages do not allow a consumer to load multiple versions of the same package. So, if we have an client library that requires v3 of package Foo and the consumer wants to use v5 of package Foo, then the consumer cannot build their application. This means that client libraries should not have dependencies by default.
  • Size - Consumer applications must be able to deploy as fast as possible into the cloud and move in various ways across networks. Removing additional code (like dependencies) improves deployment performance.
  • Licensing - You must be conscious of the licensing restrictions of a dependency and often provide proper attribution and notices when using them.
  • Compatibility - Often times you do not control a dependency and it may choose to evolve in a direction that is incompatible with your original use.
  • Security - If a security vulnerability is discovered in a dependency, it may be difficult or time consuming to get the vulnerability corrected if Microsoft does not control the dependencies code base.

DO use the C++ standard library.

DO depend on the Azure Core library for functionality that is common across all client libraries. This library includes APIs for HTTP connectivity, global configuration, and credential handling.

⛔️ DO NOT be dependent on any other packages within the client library distribution package. Dependencies are by-exception and need a thorough vetting through architecture review. This does not apply to build dependencies, which are acceptable and commonly used.

☑️ YOU SHOULD consider copying or linking required code into the client library in order to avoid taking a dependency on another package that could conflict with the ecosystem. Make sure that you are not violating any licensing agreements and consider the maintenance that will be required of the duplicated code. “A little copying is better than a little dependency” (YouTube).

⛔️ DO NOT depend on concrete logging, dependency injection, or configuration technologies (except as implemented in the Azure Core library). The client library will be used in applications that might be using the logging, DI, and configuration technologies of their choice.

Documentation Comments

DO document every exposed (public or protected) type and member within your library’s code.

DO use doxygen comments for reference documentation.

See the documentation guidelines for language-independent guidelines for how to provide good documentation.

Repository Guidelines

DO locate all source code and README in the [azure/azure-sdk-for-cpp] GitHub repository.

DO follow Azure SDK engineering systems guidelines for working in the [azure/azure-sdk-for-cpp] GitHub repository.

TODO: Content in this section below here should be moved to a better location.

DO format your source code with clang-format, using the configuration file located in the azure-sdk-for-cpp repo.

DO name all files as lowercase, in a directory of the service short name. Separate words with underscores, and end with the appropriate extension (.cpp or .hpp). For example, iot_credential.cpp is valid, while IoTCredential.cl is not.

DO place an include file that is not part of the public API in an internal directory. Do not include the service short name. For example, <azure/internal/credential.hpp>.

DO use characters in the range [a-z0-9_] for the name portion (before the file extension). No other characters are permitted.

Filenames should be concise, but convey what role the file plays within the library.

DO use #pragma once

#pragma once

// Contents of a given header

✔️ YOU MAY have a header file that includes an entire client library. For example, <azure/speech.hpp>.

☑️ YOU SHOULD have headers for smaller components that make sense to be used together. For example, <azure/speech/translation.hpp>.

⛔️ DO NOT substantially change the names exposed by the header in response to macros or other controls. For example, NOMINMAX or WIN32_LEAN_AND_MEAN from <Windows.h>.

Documentation Style

There are several documentation deliverables that must be included in or as a companion to your client library. Beyond complete and helpful API documentation within the code itself (docstrings), you need a great README and other supporting documentation.

  • README.md - Resides in the root of your library’s directory within the SDK repository; includes package installation and client library usage information. (example)
  • API reference - Generated from the docstrings in your code; published on docs.microsoft.com.
  • Code snippets - Short code examples that demonstrate single (atomic) operations for the champion scenarios you’ve identified for your library; included in your README, docstrings, and Quickstart.
  • Quickstart - Article on docs.microsoft.com that is similar to but expands on the README content; typically written by your service’s content developer.
  • Conceptual - Long-form documentation like Quickstarts, Tutorials, How-to guides, and other content on docs.microsoft.com; typically written by your service’s content developer.

DO include your service’s content developer in the Architecture Board review for your library. To find the content developer you should work with, check with your team’s Program Manager.

DO follow the Azure SDK Contributors Guide. (MICROSOFT INTERNAL)

DO adhere to the specifications set forth in the Microsoft style guides when you write public-facing documentation. This applies to both long-form documentation like a README and the docstrings in your code. (MICROSOFT INTERNAL)

☑️ YOU SHOULD attempt to document your library into silence. Preempt developers’ usage questions and minimize GitHub issues by clearly explaining your API in the docstrings. Include information on service limits and errors they might hit, and how to avoid and recover from those errors.

As you write your code, doc it so you never hear about it again. The less questions you have to answer about your client library, the more time you have to build new features for your service.

Docstrings

DO include docstrings compatible with the doxygen tool for generating reference documentation.

For example, a (very) simple docstring might look like:

/**
 *   @class client
 *   @brief The client represents the resources required for a connection to an Azure AppcConfiguration resource.
 */

DO use doxygen’s @cmd style docstrings

DO format parameter documentation like the following:

/* <....documentation....>
* @param[<direction>] <param_name> description
* <....documentation....>
*/

For example:

namespace azure::storage::blob {
/**
 * @brief execute a blocking get request
 *
 * @param[in] client
 * @param[in] path_and_query The query to execute relative to the base url of the client
 * @param[out] result
 * @param[out] result_sz size of the result
 */
void req_get(client* client, const char* path_and_query, unsigned char** result, size_t* result_sz);
} // namespace azure::storage::blob

DO document what happens to parameters that are set to null.

For example:

namespace azure::animals::cats {
/**
 * @brief get properties of a cat (e.g. hair color, weight, floof)
 *
 * @param[in] our_cat the cat to operate on
 * @param[out] props pointer to an array of num_props, or null
 * @param[in,out] num_props pointer to the number of properties to retrieve or to a location to store the number of
 *                          properties queried as described below
 *
 * If @p props is NULL then return the number of properties available in @p num_props,
 * otherwise return @p num_props into the array at @p props
 */
void get_cat_properties(cat* our_cat, cat_properties* props, size_t* num_props);
} // namespace azure::animals::cats

DO document which exceptions your function can propagate.

Code snippets

DO include example code snippets alongside your library’s code within the repository. The snippets should clearly and succinctly demonstrate the operations most developers need to perform with your library. Include snippets for every common operation, and especially for those that are complex or might otherwise be difficult for new users of your library. At a bare minimum, include snippets for the champion scenarios you’ve identified for the library.

DO build and test your example code snippets using the repository’s continuous integration (CI) to ensure they remain functional.

DO include the example code snippets in your library’s docstrings so they appear in its API reference. If the language and its tools support it, ingest these snippets directly into the API reference from within the docstrings.

For example, consider a function called do_something_or_other:

namespace azure::sdks::example {
/**
 * @brief some class type
 */
struct some_class {
    int member;

    /**
    * @brief do something, or maybe do some other thing
    */
    void do_something_or_other();
};
} // azure::sdks::example

It can be used as follows:

/**
 * @example example_1.cpp
 */
int main() {
    using azure::sdks::example::some_class;
    some_class a;
    a.do_something_or_other();
    return 1;
}

When doxygen processes these files, it will see the @example command in example_1.cpp and add it to the “examples” section of the documentation, it will also see the usage of some_struct in the example and add a link from the documentation of some_struct to the documentation (including source code) for example_1.cpp.

Use @include or @snippet to include examples directly in the documentation for a function or structure. For example:

//
// @brief some structure type
//
struct some_struct {
    int member;
};

//
// @brief do something, or maybe do some other thing
// see @snippet example_1.cpp use some_struct
//
void do_something_or_other(some_struct* s);

It can be used as follows:

/**
 * @example example_1.cpp
 */
int main() {
    /** [use some_struct] */
    some_struct a;
    do_something_or_other(&a);
    /** [use some_struct] */
    return 1;
}

Note that automatic links from documentation to examples will only be generated in struct documentation, not in function documentation. To generate a link from a function’s documentation to an example use @dontinclude. For example:

/**
 * @brief do something, or maybe do some other thing
 * @dontinclude example_1.cpp
 */
void do_something_or_other(const some_struct& s);

⛔️ DO NOT combine more than one operation in a code snippet unless it’s required for demonstrating the type or member, or it’s in addition to existing snippets that demonstrate atomic operations. For example, a Cosmos DB code snippet should not include both account and container creation operations–create two different snippets, one for account creation, and one for container creation.

Combined operations cause unnecessary friction for a library consumer by requiring knowledge of additional operations which might be outside their current focus. It requires them to first understand the tangential code surrounding the operation they’re working on, then carefully extract just the code they need for their task. The developer can no longer simply copy and paste the code snippet into their project.

Buildsystem integration

DO Provide a buildsystem target called “docs” to build the documentation

DO Provide an option BUILD_DOCS to control building of the docs, this should default to OFF

To provide this you can use the CMake FindDoxygen module as follows:

option(BUILD_DOCS "Build documentation" OFF)
if(BUILD_DOCS)
    find_package(Doxygen REQUIRED doxygen)

    # note: DOXYGEN_ options are strings to cmake, not
    # booleans, thus use only YES and NO
    set(DOXYGEN_GENERATE_XML YES)
    set(DOXYGEN_GENERATE_HTML YES)

    set(DOXYGEN_EXTRACT_PACKAGE YES)
    set(DOXYGEN_INLINE_SIMPLE_STRUCTS YES)
    set(DOXYGEN_TYPEDEF_HIDES_STRUCT YES)
    doxygen_add_docs(docs
        ${PROJECT_SOURCE_DIR}/inc
        ${PROJECT_SOURCE_DIR}/doc
        ${PROJECT_SOURCE_DIR}/src
        COMMENT "Generate Documentation")
endif()

Formatting

DO use cpp-format for formatting your code. Use the .clang-format options.

In general, cpp-format will format your code correctly and ensure consistency. However, these are few additional rules to keep in mind.

DO place all conditional or loop statements on one line, or add braces to identify the conditional/looping block.

if (meow == 0) purr += 1; // OK
if (meow == 0) {
    purr += 1; // OK
}
if (meow == 0) { purr += 1; } // OK (although will probably be changed by cpp-format)
if (meow == 0)
    purr += 1; // NOT OK

DO add comments to closing braces. Adding a comment to closing braces can help when you are reading code because you don’t have to find the begin brace to know what is going on.

while (1) {
    if (valid) {
        ...
    } /* if valid */
    else {

    } /* not valid */
} /* end forever */

DO add comments to closing preprocessor directives to make them easier to understand. For example:

#if _BEGIN_CODE_

#ifndef _INTERNAL_CODE_

#endif /* _INTERNAL_CODE_ */

#endif /* _BEGIN_CODE_ */

⚠️ YOU SHOULD NOT use parens in return statements when it isn’t necessary.

DO place constants on the right of comparisons. For example if (a == 0) and not if (0 == a)

DO include a comment for falling through a non-empty case statement. For example:

switch (...) {
    case 1:
        DoSomething();
        break;
    case 2:
        DoSomethingElse();
        /* fall through */
    case 3:
        {
            int v;

            DoSomethingMore(v);
        }
        break;
    default:
        Log(LOG_DEBUG, "default case reached");
}

⚠️ YOU SHOULD NOT use goto statements. The main place where goto statements can be usefully employed is to break out of several levels of switch, for, or while nesting, although the need to do such a thing may indicate that the inner constructs should be broken out into a separate function with a success/failure return code. When a goto is necessary, the accompanying label should be alone on a line and to the left of the code that follows. The goto should be commented as to its utility and purpose.

README

DO have a README.md file in the component root folder.

An example of a good README.md file can be found here.

DO optimize the README.md for the consumer of the client library.

The contributor guide (CONTRIBUTING.md) should be a separate file linked to from the main component README.md.

Samples

TODO: Provide sample guidelines.

Commonly Overlooked C++ API Design Guidelines

TODO: Provide C++ specific API design guidelines. Example:

⚠️ YOU SHOULD NOT use goto statements. The main place where goto statements can be usefully employed is to break out of several levels of switch, for, or while nesting, although the need to do such a thing may indicate that the inner constructs should be broken out into a separate function with a success/failure return code. When a goto is necessary, the accompanying label should be alone on a line and to the left of the code that follows. The goto should be commented as to its utility and purpose.