โ† Back to DevBytes

Technical Writing: Documenting Code and APIs

Technical Writing: Documenting Code and APIs

Technical writing is the practice of creating documentation that explains how software works, how to use it, and how to integrate with it. For developers, this means writing clear, accurate, and maintainable documentation for code, libraries, and APIs. Good documentation is often the difference between a widely adopted project and one that is ignored, because no one can figure out how to use it.

What Is Technical Writing for Code and APIs?

At its core, technical writing for software involves translating complex technical concepts into language that a specific audience can understand. When documenting code, this includes inline comments, function descriptions, and architectural overviews. When documenting APIs, it includes endpoint references, authentication guides, request and response examples, and error handling instructions. The goal is always the same: reduce the friction a reader experiences when trying to accomplish a task.

Unlike marketing copy or prose, technical documentation must be precise. Ambiguity leads to bugs, wasted time, and frustrated users. A well-documented function tells the reader exactly what inputs it expects, what it returns, what errors it can throw, and what side effects it may have.

Why Documentation Matters

How to Document Code

The most effective code documentation happens at multiple levels. At the file level, a brief header explains the purpose of the module. At the function level, a docstring describes inputs, outputs, and behavior. Inline comments explain why something is done, not what is being done, since the code itself already shows the what.

Most languages support standardized documentation comment formats that can be parsed by tools to generate reference docs automatically. In Python, this is docstrings processed by tools like Sphinx. In JavaScript, JSDoc is the standard. In Java, Javadoc is built into the language.

Here is an example of a well-documented Python function using a Google-style docstring:

def calculate_discount(price: float, discount_percent: float, min_price: float = 0.0) -> float:
    """Apply a percentage discount to a price, enforcing a minimum floor.

    This function computes the discounted price by subtracting the discount
    amount from the original price. If the result falls below the specified
    minimum price, the minimum price is returned instead.

    Args:
        price: The original price before discount. Must be non-negative.
        discount_percent: The discount as a percentage between 0 and 100.
        min_price: The lowest allowable price. Defaults to 0.0.

    Returns:
        The final price after applying the discount, guaranteed to be
        greater than or equal to min_price.

    Raises:
        ValueError: If price is negative or discount_percent is outside
            the range 0 to 100.

    Example:
        >>> calculate_discount(100.0, 20.0)
        80.0
        >>> calculate_discount(100.0, 90.0, min_price=50.0)
        50.0
    """
    if price < 0:
        raise ValueError("price must be non-negative")
    if not 0 <= discount_percent <= 100:
        raise ValueError("discount_percent must be between 0 and 100")

    discounted = price - (price * discount_percent / 100)
    return max(discounted, min_price)

Notice how the docstring covers every aspect a caller would need: the purpose, the arguments, the return value, possible exceptions, and a concrete usage example. A developer reading this never has to look at the implementation to understand how to use the function.

For JavaScript, the equivalent using JSDoc looks like this:

/**
 * Formats a date as a human-readable string.
 *
 * @param {Date|string} date - The date to format. Accepts a Date object
 *   or an ISO 8601 string.
 * @param {string} [locale="en-US"] - The BCP 47 locale tag for formatting.
 * @returns {string} The formatted date string, e.g. "January 5, 2024".
 * @throws {TypeError} If the input cannot be parsed as a valid date.
 *
 * @example
 * formatDate("2024-01-05", "en-US");
 * // returns "January 5, 2024"
 */
function formatDate(date, locale = "en-US") {
    const parsed = new Date(date);
    if (isNaN(parsed.getTime())) {
        throw new TypeError("Invalid date input");
    }
    return parsed.toLocaleDateString(locale, {
        year: "numeric",
        month: "long",
        day: "numeric",
    });
}

How to Document APIs

API documentation goes beyond individual functions. It must explain the system as a whole: how to authenticate, what resources are available, how to make requests, and what responses to expect. The best API docs combine a narrative getting-started guide with a structured reference for each endpoint.

A strong API reference entry for a single endpoint typically includes the HTTP method, the path, a description, required and optional parameters, authentication requirements, example requests, example responses, and possible error codes. The OpenAPI Specification (formerly Swagger) is the industry standard for describing REST APIs in a machine-readable format, and tools like Redoc and Swagger UI can render it into interactive documentation.

Here is a minimal OpenAPI 3.0 document describing a single endpoint:

openapi: 3.0.3
info:
  title: Tasks API
  version: 1.0.0
  description: A simple API for managing tasks.
paths:
  /tasks/{taskId}:
    get:
      summary: Retrieve a single task
      description: Returns the task matching the provided ID.
      operationId: getTaskById
      parameters:
        - name: taskId
          in: path
          required: true
          schema:
            type: string
          description: The unique identifier of the task.
      responses:
        "200":
          description: The task was found and returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Task"
        "404":
          description: No task exists with the given ID.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
components:
  schemas:
    Task:
      type: object
      required: [id, title, completed]
      properties:
        id:
          type: string
          example: "task_123"
        title:
          type: string
          example: "Write API documentation"
        completed:
          type: boolean
          example: false
    Error:
      type: object
      required: [code, message]
      properties:
        code:
          type: string
          example: "not_found"
        message:
          type: string
          example: "Task not found."

Even if you do not use OpenAPI, your written endpoint documentation should follow a similar structure. Here is how the same endpoint might look in plain Markdown:

## Get a Task

`GET /tasks/{taskId}`

Retrieves a single task by its unique identifier.

### Path Parameters

| Name    | Type   | Required | Description                        |
|---------|--------|----------|------------------------------------|
| taskId  | string | Yes      | The unique ID of the task to fetch.|

### Example Request

    curl -X GET https://api.example.com/v1/tasks/task_123 \
      -H "Authorization: Bearer YOUR_TOKEN"

### Example Response (200 OK)

    {
      "id": "task_123",
      "title": "Write API documentation",
      "completed": false
    }

### Errors

| Status | Code       | Meaning                                  |
|--------|------------|------------------------------------------|
| 404    | not_found  | No task exists with the given ID.        |
| 401    | unauthorized | The authentication token is missing or invalid. |

Best Practices

Conclusion

Technical writing is a core engineering skill, not an afterthought. By documenting code with clear docstrings and building API references that cover parameters, examples, and errors, you make your software accessible to other developers and to your future self. The investment pays off in faster onboarding, fewer support questions, broader adoption, and more maintainable codebases. Treat documentation with the same care you give to your production code, and your projects will be measurably easier to use and maintain.

๐Ÿ›  Tools from DevBytes

Inventory Tracker Pro โ€” Excel inventory system, low-stock alerts ยท $19
AI Dev Kit for Mac โ€” local AI dev environment templates ยท $9.99
KeyMapper for Mac โ€” custom keyboard shortcut toolkit ยท $7.99

โ† Back to all articles