Skip to main content
Good documentation is critical for the workerd project. This guide covers best practices for writing clear, accurate, and maintainable documentation.

Types of documentation

Code comments

Always use // line comments, never /* */ block comments:
// GOOD: Clear explanation before the declaration
// Encodes a string to UTF-8 bytes.
// Returns an array of bytes representing the encoded string.
kj::Array<byte> encodeUtf8(kj::StringPtr text);

// BAD: States the obvious
int count;  // stores the count

// BAD: Block comment
/* This is a block comment */
void foo();

Header file documentation

Every declaration in a header file should have a comment if its purpose isn’t immediately obvious:
class RequestTracker {
public:
  // Begins tracking a new request with the given ID.
  // Returns a handle that automatically stops tracking when destroyed.
  kj::Own<Request> startRequest(kj::StringPtr id);

  // Returns the number of currently active requests.
  uint getActiveCount() const;

private:
  // Map of active request IDs to their start times.
  kj::HashMap<kj::String, kj::Date> activeRequests;
};

Implementation comments

Comment every few lines explaining what the next few lines are doing:
void processData(kj::ArrayPtr<byte> data) {
  // Parse the header to determine the payload size.
  auto header = parseHeader(data.slice(0, HEADER_SIZE));

  // Validate that we have enough data for the payload.
  KJ_REQUIRE(data.size() >= HEADER_SIZE + header.payloadSize,
      "incomplete data");

  // Extract and process the payload.
  auto payload = data.slice(HEADER_SIZE, HEADER_SIZE + header.payloadSize);
  handlePayload(payload);
}

TODO comments

Use structured TODO comments:
// TODO(now): Must be fixed before merging
// TODO(soon): Should be addressed in near future
// TODO(someday): Nice to have improvement
// TODO(perf): Performance optimization opportunity
// TODO(security): Security consideration
// TODO(cleanup): Code cleanup needed
// TODO(test): Additional test needed
TODO(now) comments must be addressed before merging a pull request.
You can also reference specific projects:
// TODO(websockets): Complete when WebSocket redesign lands

Architecture documentation

Subdirectories contain AGENTS.md files with component-specific context:
  • Key classes and their roles
  • Where to look for specific functionality
  • Local conventions and anti-patterns
  • File organization

Structure of AGENTS.md files

# Component Name

## OVERVIEW

Brief description of what this component does.

## KEY CLASSES

| Class | File | Role |
|-------|------|------|
| `MyClass` | `my-class.{h,c++}` | What it does |

## WHERE TO LOOK

| Task | File(s) |
|------|----------|
| Do X | `file.c++` |

## CONVENTIONS

- List of conventions specific to this component

## ANTI-PATTERNS

- List of things to avoid

Update AGENTS.md when needed

When you find new high-level information or make significant architectural changes, update the relevant AGENTS.md file:
  • Adding new key classes
  • Documenting new patterns
  • Identifying anti-patterns
  • Clarifying where to find functionality

API documentation

For JavaScript APIs exposed to users:

TypeScript definitions

TypeScript definitions are generated from C++ code. Use JSG TypeScript macros to customize:
class Response: public jsg::Object {
  // ...

  JSG_RESOURCE_TYPE(Response) {
    JSG_TS_OVERRIDE(type Response<T = any> = {
      json<U = T>(): Promise<U>;
    });
  }
};

JSDoc comments

For TypeScript and JavaScript code, use JSDoc:
/**
 * Encodes a string to a Uint8Array.
 *
 * @param input - The string to encode
 * @returns The encoded bytes
 */
export function encode(input: string): Uint8Array {
  return new TextEncoder().encode(input);
}

Cap’n Proto schema documentation

Document Cap’n Proto schemas with comments:
struct WorkerOptions {
  # Configuration options for a worker.

  compatibilityDate @0 :Text;
  # Date in YYYY-MM-DD format determining which compatibility flags
  # are enabled by default.

  compatibilityFlags @1 :List(Text);
  # Explicitly enabled compatibility flags that override the date-based
  # defaults.
}

Markdown documentation

File locations

  • General documentation: docs/
  • Component-specific: src/workerd/component/AGENTS.md
  • Reference guides: docs/reference/

Style guidelines

  • Use sentence case for headings
  • Keep paragraphs short (2-4 sentences)
  • Use code blocks with language tags
  • Include examples for complex concepts
  • Link to related documentation

Code blocks

Always specify the language:
```cpp
auto value = kj::str("hello");
```

```javascript
const value = "hello";
```

```bash
bazel test //src/...
```

Examples

Good examples are essential for understanding:
// GOOD: Shows typical usage
// Example:
//   auto promise = fetchUrl("https://example.com");
//   auto response = promise.wait(waitScope);
//   auto text = response.text.wait(waitScope);
kj::Promise<HttpResponse> fetchUrl(kj::StringPtr url);

// GOOD: Shows both success and error cases
// Example:
//   KJ_IF_SOME(user, findUser("alice")) {
//     // user found
//   } else {
//     // user not found
//   }
kj::Maybe<User> findUser(kj::StringPtr name);

What to document

Always document

  • Public APIs and their parameters
  • Non-obvious design decisions
  • Complex algorithms or business logic
  • Error conditions and how to handle them
  • Thread safety and lifetime requirements
  • Performance characteristics

Don’t document

  • Obvious things that are clear from the code
  • Implementation details that callers don’t need
  • Temporary debugging code

Writing style

Be concise

// GOOD
// Returns the user's name, or null if not set.
kj::Maybe<kj::StringPtr> getName();

// BAD
// This method is used to retrieve the name of the user. It will return
// the name if one has been set, otherwise it returns null to indicate
// that no name is available.
kj::Maybe<kj::StringPtr> getName();

Be precise

// GOOD
// Returns a borrowed pointer valid until this object is destroyed.
const Data* getData() const;

// BAD
// Returns the data.
const Data* getData() const;

Use present tense

// GOOD
// Validates the input and throws if invalid.
void validate(Input input);

// BAD
// Will validate the input and will throw if it is invalid.
void validate(Input input);

Deprecation

When deprecating APIs, document why and what to use instead:
// DEPRECATED: Use getCount() instead. This method will be removed
// in a future version.
// 
// The old name was confusing because it actually returns the count,
// not the size in bytes.
uint getSize() { return getCount(); }

// Returns the number of items.
uint getCount();

Reference documentation

For reference-style documentation (like type mappings, macro catalogs), use tables:
| C++ Type | JavaScript Type | Notes |
|----------|-----------------|-------|
| `bool` | `boolean` | Direct mapping |
| `double` | `number` | IEEE 754 |
| `kj::String` | `string` | UTF-8 encoded |

Next steps

Code style

Review code style guidelines

JSG architecture

Learn about the JavaScript binding layer

Build docs developers (and LLMs) love