Skip to content
Bryan Call edited this page Mar 19, 2026 · 2 revisions

Coding Style

This page documents the formatting rules, naming conventions, and best practices for Apache Traffic Server code. Some rules are enforced automatically by CI (via clang-format and clang-tidy); others are conventions enforced during code review.

Language Standard

  • C++20 — all code must compile with C++20. Do not use C++23 features.
  • The project is approximately 500K lines of C++.

Formatting

Automated Enforcement

Formatting is enforced by clang-format in CI. The configuration lives in .clang-format at the repo root (based on Mozilla style with significant customizations). clang-tidy runs additional static analysis checks via .clang-tidy.

Always run the formatter before submitting a PR:

cmake --build build --target format

Line Length

132 characters maximum. This is enforced by both clang-format and EditorConfig.

Indentation

  • 2 spaces for C/C++ code (never tabs)
  • 4 spaces for Python code
  • Tab width is 8 when tabs appear (e.g., Makefiles)

Brace Style

Linux kernel style — opening brace on the same line for control statements, on the next line for functions and classes:

// Control statements: opening brace on same line
if (condition) {
  do_something();
}

for (const auto &item : container) {
  process(item);
}

// Functions: opening brace on next line
void
my_function()
{
  // ...
}

// Classes: opening brace on next line
class MyClass
{
public:
  void method();
};

Always use braces, even for single-line bodies:

// Correct
if (x > 0) {
  foo();
}

// Wrong — missing braces
if (x > 0)
  foo();

Pointer and Reference Alignment

Right-aligned (pointer/reference binds to the variable name, not the type):

Type *ptr;         // Correct
Type &ref;         // Correct
const Type *cptr;  // Correct

Type* ptr;         // Wrong
Type& ref;         // Wrong

Variable Declarations

Keep declarations grouped together at the top of the scope, with an empty line before the first statement:

void
function()
{
  int count        = 0;
  std::string name = "test";
  auto *buffer     = new_buffer();

  process_data(count, name, buffer);
}

Running the Formatter

# Via CMake build target (recommended)
cmake --build build --target format

# Via helper script
tools/clang-format.sh

Naming Conventions

Element Style Examples
Classes CamelCase HttpSM, NetVConnection, CacheProcessor
Functions snake_case handle_request(), process_buffer()
Variables snake_case server_port, cache_key, connection_count
Constants / Macros UPPER_CASE HTTP_STATUS_OK, MAX_BUFFER_SIZE
Member variables snake_case (no prefix) buffer_size, connection_count

C++20 Patterns

Prefer Modern C++

// Good — modern C++20
auto buffer = std::make_unique<MIOBuffer>(size);
for (const auto &entry : container) {
  if (auto *ptr = entry.get(); ptr != nullptr) {
    process(ptr);
  }
}

// Avoid — legacy C-style
MIOBuffer *buffer = (MIOBuffer *)malloc(sizeof(MIOBuffer));
for (int i = 0; i < container.size(); i++) {
  process(container[i]);
}

Smart Pointers and RAII

  • Use std::unique_ptr and std::shared_ptr over raw new/delete when possible
  • Use ats_malloc()/ats_free() for large allocations (not raw malloc)
  • Use IOBuffer for network data (zero-copy design)
  • Note: some subsystems legitimately use delete this (e.g., continuation-based code)

What NOT to Use

Avoid Use Instead
C++23 features C++20 only
malloc/free for large allocations ats_malloc/ats_free, or prefer stack/heap allocators
Blocking operations in event threads Async event system (schedule_in(), schedule_imm())
Creating threads manually Event system (eventProcessor.schedule_imm())

Comments and Documentation

Comment Philosophy

The project follows a minimal comments philosophy. Only add comments where the code isn't self-explanatory. Explain why, not what.

// Bad — stating the obvious
// Increment the counter
counter++;

// Good — explaining why
// Skip the first element since it's always the sentinel value
counter++;

// Bad — describing what
// Loop through all connections and close them
for (auto &conn : connections) {
  conn.close();
}

// Good — explaining non-obvious intent
// Must close connections before destroying the acceptor to avoid use-after-free
for (auto &conn : connections) {
  conn.close();
}

When to Comment

  • Non-obvious algorithms or math
  • Workarounds for bugs in dependencies
  • Performance optimizations that reduce clarity
  • Security-critical sections
  • Complex state machine transitions

When NOT to Comment

  • Self-documenting code with clear names
  • Obvious operations
  • Function/variable names that already explain themselves

License Headers

All new source files (.cc, .h, .py, etc.) must include the Apache License 2.0 header:

/** @file
 *
 *  Brief description of file
 *
 *  @section license License
 *
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

Debug Logging

Use the Dbg() macro with a DbgCtl tag for debug output:

// At file scope
static DbgCtl dbg_ctl{"http_sm"};

// In code
Dbg(dbg_ctl, "Processing request for URL: %s", url);

Python Style

  • Python 3.11+ with type hints
  • 4-space indentation (never tabs)
  • Type annotations on all function signatures

See Also

Clone this wiki locally