Open-Source Wikis

/

Envoy

/

How to contribute

/

Patterns and conventions

envoyproxy/envoy

Patterns and conventions

Envoy's C++ has a strong house style. The complete style guide is STYLE.md; this page captures the patterns that come up most often when reading or writing the code.

File layout: interface, impl, mock

Almost every component in Envoy follows a three-way split:

envoy/upstream/cluster_manager.h        # Public abstract interface
source/common/upstream/cluster_manager_impl.h
source/common/upstream/cluster_manager_impl.cc
test/mocks/upstream/cluster_manager.h   # gMock implementation
  • The interface header in envoy/ declares pure virtual classes. No method implementations, almost no member fields.
  • The *_impl.h declares the concrete class that implements the interface, plus its private members.
  • The *_impl.cc is where logic lives.
  • The mock under test/mocks/ mirrors the interface one-for-one with MOCK_METHOD.

When you add a new public interface, add the matching mock in the same PR. Reviewers reject otherwise.

Smart pointer aliases

STYLE.md requires aliasing every smart pointer type:

class Foo;
using FooPtr = std::unique_ptr<Foo>;
using FooSharedPtr = std::shared_ptr<Foo>;
using FooConstSharedPtr = std::shared_ptr<const Foo>;

Raw pointers (Foo*) are not aliased.

unique_ptr is preferred over shared_ptr wherever ownership is unambiguous. The factory pattern is used to navigate test cases that need access to memory the production code owns.

Optional references

absl::optional<std::reference_wrapper<T>> has a project-wide alias:

using FooOptRef = OptRef<T>;
using FooOptConstRef = OptRef<const T>;

See envoy/common/optref.h.

Function naming

  • Functions are lowerCamelCase.
  • Classes / structs are UpperCamelCase.
  • Member fields end with _: int connections_per_host_;.
  • Enum values are PascalCase: enum class Mode { RoundRobin, LeastRequest };.
  • Constants are ConstantVar or CONSTANT_VAR (not kConstantVar).
  • Type aliases use using, not typedef.

Move semantics

When a function takes ownership, prefer && arguments rather than unique_ptr by value:

void onHeaders(Http::HeaderMapPtr&& headers, ...);

This forces the caller to write std::move(...) at the call site, making intent obvious.

Exceptions

The data plane is exception-free. New code in the request path may not throw. Existing exception sites are being migrated to absl::StatusOr. Some control plane code still uses exceptions, but new exception sites are discouraged.

When a function can fail, return absl::Status or absl::StatusOr<T>:

absl::StatusOr<RouteConfigConstSharedPtr> resolveRoute(...);

The RETURN_IF_NOT_OK, THROW_IF_NOT_OK, and RETURN_IF_NOT_OK_REF macros in source/common/common/statusor.h propagate errors compactly.

Factory registration

Every extension is plugged in through a typed factory. The pattern:

  1. Declare the abstract factory in envoy/ (e.g. Server::Configuration::NamedHttpFilterConfigFactory).
  2. Implement the concrete factory in source/extensions/.../config.cc and instantiate the static registration:
REGISTER_FACTORY(MyFilterFactory,
                 Server::Configuration::NamedHttpFilterConfigFactory);

The macro expands to a static Registry<...>::Add<MyFilterFactory> instance whose constructor runs at process start. This is why simply linking an extension into the binary makes it available — the BUILD dependency is the registration.

The registry implementation is in envoy/registry/registry.h.

Threading model

Code on the data path runs on a worker thread. The rules:

  • No blocking I/O on the worker.
  • No locking, except for read-mostly state through ThreadLocal::SlotImpl.
  • Communicate with the main thread via Event::Dispatcher::post. Never share mutable state directly.
  • Event-driven only: there are no busy loops. If you need a periodic action, use Event::Timer.

The full architectural rationale is on the threading model page.

Headers and includes

#pragma once is preferred over header guards.

Includes are grouped by the formatter into:

#include "system_header.h"      // <foo>

#include "envoy/...h"            // public Envoy headers
#include "source/common/...h"   // implementation headers
#include "source/extensions/...h"

#include "absl/...h"             // third-party
#include "fmt/format.h"
#include "openssl/...h"

tools/code_format/check_format.py enforces ordering and forbids cross-cutting includes (source/extensions/foo.h from source/common/, etc.).

Stat naming

Stat names are dotted, lowercase, deduped through the symbol table. The pattern:

  • Define an ALL_FOO_STATS(COUNTER, GAUGE, HISTOGRAM) X-macro.
  • Generate a FooStats struct that wraps the counters/gauges/histograms.
  • Construct it with POOL_COUNTER(...) etc., scoped to a Stats::Scope.

Examples are everywhere; source/server/server.h has the canonical "all server stats" macro at the top.

Logging

ENVOY_LOG(debug, "request received: {}", header_value);
ENVOY_LOG_TO_LOGGER(my_logger, info, "...");
ENVOY_CONN_LOG(trace, "...", connection_);
ENVOY_STREAM_LOG(debug, "...", stream_);

Use the highest level that won't be too chatty. Trace is for byte-level debugging.

Comments and documentation

Public API comments use Doxygen with @param, @return, etc. Internal comments may be either // or Doxygen at developer discretion. Comment blocks gated by clang-format off/clang-format on are allowed but discouraged.

Banned constructs

tools/code_format/check_format.py enforces a long list of bans. Highlights:

  • using namespace ... (except Envoy::ProtobufWkt in narrow places).
  • Non-PoD globals (use MUTABLE_CONSTRUCT_ON_FIRST_USE or static-initialization-on-first-use).
  • whitelist, blacklist, master, slave per the inclusive language policy.
  • Direct calls to RELEASE_ASSERT(false, ...) instead of ENVOY_BUG.
  • Direct manipulation of std::condition_variable instead of Thread::CondVar.
  • Bare std::regex (use Regex::Engine::matcher instead).
  • Bare std::random_device and friends (must go through Random::RandomGenerator).

API conventions

Anything in api/ follows protobuf style with project-specific overlays. The full rules are in api/STYLE.md. Highlights:

  • Versioning: v3 is current, new fields land as Bootstrap.foo rather than a new version unless they break compatibility.
  • Field numbers are tightly scoped — never renumber, never reuse.
  • Validation rules use [(validate.rules) = ...] (protoc-gen-validate).
  • Comments at the proto level are user-facing documentation.

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – Envoy wiki | Factory