Skip to content

Technical Writing Guidelines

Mark Drake edited this page Nov 3, 2025 · 11 revisions

Our goal for these Technical Writing Guidelines is that they will assist Chainguard's employees, customers, and users with producing and maintaining technical educational content that shares a consistent voice and meets the level of detail and technical accuracy expected of our documentation and educational resources.

Our goal for these writing guidelines is that they provide a single source of truth for the most common questions that come up when drafting and revising technical educational resources. There is no singular “correct” approach to writing; our hope is that these practices and techniques will help you achieve a high level of clarity and teaching effectiveness.

We expect these guidelines to evolve over time, so feedback is welcome!

Educational resources at Chainguard

All of Chainguard’s documentation, tutorials, and other educational resources should have the following characteristics:

Purpose

  • Explains why someone would find value in reading the given content
  • Communicates technical knowledge that is useful and relevant to users
  • Communicates any assumptions regarding the reader’s technical level at the top to set expectations
    • Ideally, we want to educate the broadest audience possible, but this is not always possible in practice
  • Well-scoped
    • Provides enough detail of information for a given topic
    • Is not overwhelming to read
    • Breaks down complex topics into accessible, digestible pieces
  • Encourages readers to put knowledge into practice
    • Provides concrete examples and opportunities to “play” (through something like the terminal, for instance) in order to improve the audience's retention of information
  • Provides guidance for further learning
  • Is community-centric
    • Supports open source
    • Supports developer collaboration
    • Supports growing the technical community
  • Aligns with Chainguard’s company values
    • We are customer obsessed
    • We have a bias for intentional action
    • We do serious work, but don't take ourselves too seriously
    • We trust each other and assume good intentions

Technical details

  • Is correct, accurate, and without copy errors or bugs
  • Follows software security recommended practices
  • Is up to date with the latest version, or tied to a specific version
  • Explains code thoroughly
    • Readers should not be asked to run commands or code that they do not fully understand
    • Links to further reading if not everything can be covered in one document
  • Provides compelling, realistic examples
    • If possible, use concrete examples from open source projects
  • Avoid using placeholder names that are meaningless, like foo and bar
  • Is logically structured and helps the reader follow a procedure or understand a concept from beginning to end
  • Is original and well researched
    • Resources are original and not syndicated from elsewhere
    • However, they can be syndicated elsewhere where Chainguard has the canonical link
  • Links the source when a resource is quoted
  • Is not plagiarized
  • All written text is licensed under CC BY-NC-SA 4.0
    • Any future syndication must maintain the license
    • The license enables language translations
    • Others may build off of our resources
    • Creative Commons is in the spirit of open source

Tone

  • Has an authentic developer voice that is welcoming and inclusive
  • Is conversational but formal
  • Prioritizes objectivity
  • Avoids making value judgments (for example, “This tool is great!“)
  • Can make substantiated recommendations
  • Uses American English spellings
  • Uses clear language
    • Does not use idiomatic expressions
    • Avoids jokes or puns that detract from the meaning or educational value of the resource (there is more room for humor in content types like videos or presentations)
    • Is accessible to many readers
    • Aims towards machine translatability
  • Is grammatically correct
  • Has no errors in copy
  • Provides punctuation that helps with readability

Inclusivity and Accessibility

  • Be mindful of readers who use accessibility tools
    • Avoid terms like “look,” “see,” and “view”. For example, you could use “this command will return output like the following” instead of “you’ll see the following output”
    • Avoids directional language. Use terms like “following” and “previous” instead of “below” and “above”
  • Ensures that images (photos, not Chainguard images) always include descriptive alt text, and are descriptively named
  • Avoids colloquialisms and region-centric analogies

These principles guide authors to create conceptual articles, tutorials, and other learning resources that help our readers better understand how Chainguard’s products work.

Written resources structure

Chainguard’s written documentation and resources should follow a consistent structure. Generally, procedural tutorials should follow the following format:

  • Introductory paragraphs, no heading: Should explain in 1-3 paragraphs why the reader may find this tutorial valuable, and what to expect to have completed by the end of the tutorial
  • Prerequisites, H2 heading: This section outlines every step that a reader must take in order to complete the tutorial before they proceed with it. In order to keep this section short and clear, it should include links to other Chainguard Academy resources
  • Steps, H2 headings: Often following a Step 1 — Do something format, this is the body of the tutorial and where most of the procedure that the reader is following is documented
  • Secondary steps, H3 headings: Nested headings should only be used when needed, and may refer to the same step on a different operating system, or closely related and more granular steps of a procedure
  • Concluding section, H2 heading: Can be Next Steps or Further Resources — here is an opportunity to reiterate what the reader has accomplished and provide them with additional links to learn more

How to Use Chainguard Images is a great example of a procedural tutorial that follows this structure.

Front Matter

Chainguard Academy runs as a static Hugo site. As such, each page of content on the site must begin with what's called Hugo front matter. Front matter allows you to add special metadata about a given page.

A typical front matter section looks like this:

```
title: "How to Use Chainguard Images"
linktitle: "How to Use"
type: "article"
description: "A primer on how to migrate to Chainguard Images"
lead: "A primer on how to migrate to Chainguard Images"
date: 2022-09-01T08:49:31+00:00
lastmod: 2024-03-29T19:42:31+00:00
draft: false
tags: ["Chainguard Images", "Procedural", "Product"]
images: []
menu:
  docs:
	parent: "chainguard-images"
weight: 020
terminalImage: academy/chainguard-images:latest
toc: true
```

The following should be in every page's front matter:

  • title: The title of the given doc. The value should be in quotes and written in Title case.
  • linktitle: The "short title" of a given doc that appears in the left-hand navigation menu. If no linktitle is given, Hugo will default to using the full title in places where the linktitle would appear.
  • type: We only have one "content type" defined for Chainguard Academy. This field should always be set to article.
  • description: A brief, one- or two-sentence description of the resource.
  • date: A timestamp showing the date of initial publication.
  • lastmod: A timestamp of the most recent edit to the page.

Note: You should only update the lastmod field of a resource's front matter when it has been fully reviewed and tech tested. Otherwise, it can mess with our automated maintenance reporting.

  • draft: If sent to true, the resource will be hidden from view when published to Academy. This should generally be set to false.
  • tags: The tags with which to associate the given resource. In general, there should be at least three tags on each resource. Also, tags should always be written in all caps.
  • menu: If set, Hugo adds the page to the given menu or menus.
  • weight: This is the "weight" of content pages in the lefthand nav, higher weights appear lower in lists.
  • toc: This should almost always be set to true - this will allow a table of contents to appear at the right side of the resource.

Code snippets in written resources

Our documentation involves many code examples throughout learning resources. While we may need to make some assumptions of readers’ knowledge at times, it is important to explain code relevant to the tasks within a given tutorial as much as possible. We do not want to encourage readers to run code that they do not understand well.

We prefer code that we expect readers to execute to be written in blocks so that it is readable. Code should be introduced before the block, and summarized after. Ideally, executed code will have a corresponding output.

For example, if we were to explain the “Hello, World!” program in Python, we may do so in a way similar to the following:

We will be creating a small “Hello, World!” program in Python. Here, we will be using the `print()` function to output a string that we pass onto our screen. We’ll be passing the string `'Hello, World!'`, which we can tell is a string by the single quotes on either side of it. If you prefer, in Python you can use double quotes (`"`) instead.

Type the following in your Python interpreter:

```sh
print('Hello, World!')
```

Once you press `ENTER` to run the program, you’ll receive the following output:

```
Hello, World!
```

At this point, the program was executed and you received the output expected from the `print()` statement. You can continue to practice by passing other strings to the function.

In this sample, the print() function is briefly explained as well as the concept of the string data type. It presents the full code snippet that the writer expects the reader to run, and also provides the expected output so that the reader can ensure that they executed the code correctly. Finally, there is a concluding sentence that explains what has happened and what the reader may like to do next given their new knowledge of this command.

Code Blocks and Labels

The site uses Hugo's Goldmark markdown parser with custom rendering for code blocks. Code blocks support syntax highlighting and optional labels.

Basic Syntax

Standard markdown fenced code blocks with language specification:

```python
print("This code block's label will render as 'Python' with syntax highlighting for Python.")
```

You can also leave code blocks unlabeled:

```
This code block will not render with a label.
```

Code Block Attributes

Code blocks support two optional attributes:

Title Attribute - Displays a label above the code block (e.g., filename or descriptor):

```python {title="hello.py"}
print("Hello, World!")
```

Caption Attribute - Displays explanatory text below the code block:

```yaml {caption="Example configuration for production environments"}
apiVersion: v1
kind: Service
```

Language Label Normalization

The site automatically normalizes language labels for consistent display:

  • Shell variants (bash, sh, shell) → display as "Shell"
    • This is preferred to labels like Bash, as Shell is more generic and therefore more broadly applicable.
  • YAML variants (yaml, yml) → display as "YAML"
  • SQL variants (sql, mysql, postgresql, mariadb, postgres) → display as "SQL"
  • Other languages → Capitalized (e.g., python → "Python", json → "JSON")

Automatic Features

Code blocks automatically include:

  • Copy button - Appears in the top-right corner with tooltip
  • Language indicator - Shows normalized language name
  • Expand/collapse - Automatically added for code blocks exceeding 25 lines
  • Syntax highlighting - Uses Dracula theme with Highlight.js

Accessibility

Code blocks are rendered with semantic HTML including:

  • aria-labelledby attributes linking titles to code blocks
  • Unique IDs generated from title attributes
  • data-language and data-lang attributes for programmatic access

Supported Languages

Syntax highlighting is available for: JavaScript, JSON, Bash, Shell, TOML, INI, YAML, Markdown, PHP, Python, and Go.

Creating and editing files

We prefer our documentation to be declarative, meaning that readers can follow a given procedure or instruction by copying and pasting the given commands and keeping interaction with the command line to a minimum.

To maintain this declarative approach when a piece of technical content requires readers to create a new file, we default to using the cat command to write text to a file as in this example:

cat > sample-policy.yaml <<EOF
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
 name: sample-policy
spec:
 images:
 - glob: "ghcr.io/chainguard-dev/*/*"
 - glob: "ghcr.io/chainguard-dev/*"
 - glob: "index.docker.io/*"
 - glob: "index.docker.io/*/*"
 - glob: "cgr.dev/chainguard/**"
 authorities:
 - keyless:
     url: https://fulcio.sigstore.dev
     identities:
       - issuer: "*"
         subject: "*"
EOF

In the first line of this example, the cat command redirects all of the remaining lines into a file named sample-policy.yaml until the content reaches the string EOF. Using this approach instead of manually creating a file with a text editor like vim or nano can help our readers integrate Chainguard’s products with their existing automation systems.

With that said, if a procedure requires you to use a text editor to change an existing file, we default to using nano in examples due to its wide install base and beginner-friendly interface.

Edit the `sample-policy.yaml` file with your preferred text editor. This example uses `nano`.

```sh
nano sample-policy.yaml
```

After making your changes, save and close the file. If you used `nano` to edit the file, do so by pressing `CTRL + O` and then `CTRL + X`. 

Notice that this example doesn’t tell the reader to use nano directly. Instead, it presents the text editor as a valid option. It also provides clear instructions for how to save and close a file after editing it with nano, which is crucial information for readers who aren’t already familiar with the tool.

Video Transcript Timestamps

Videos are one of our official Academy content types and, in order to keep our resources inclusive, we aim to include a transcript with each video we publish.

Our videos are hosted on YouTube which generates video transcripts automatically. However, there isn’t an easy way to copy the transcript of a video while retaining the timestamp URLs.

We were stuck doing this manually until Adrian kindly assembled the following Python script which does this for us.

import sys

import re


def is_time_format(line):
   return re.match(r"^\d+:\d{2}$", line.strip())

def time_to_seconds(time_str):
   minutes, seconds = time_str.split(":")
   return int(minutes) * 60 + int(seconds)

def convert_line(line, code):
   if is_time_format(line):  # Check if it's a time line
       time_str = line.strip()
       seconds = time_to_seconds(time_str)
       return f'<a href="https://youtu.be/{code}?t={seconds}" target="_blank">{time_str}</a> '
   else:
       return line

def main():
   if len(sys.argv) < 3:
       print("Usage: python script_name.py <input_file> <youtube_code>")
       sys.exit(1)

   input_file = sys.argv[1]
   code = sys.argv[2]
   output_file = "output.txt"

   with open(input_file, 'r') as fin, open(output_file, 'w') as fout:
       lines = fin.readlines()
       for i, line in enumerate(lines):
           fout.write(convert_line(line, code))

if __name__ == "__main__":
   main()

Copy this script to a file (in the following example, convert.py). Then, go to the Youtube video and copy the transcript to an input file (input.txt). Then, retrieve the video’s code. This is an 11-digit string at the end of a YouTube video URL. For example, the code for a YouTube video with the URL https://www.youtube.com/watch?v=rqIcDrg1XOs is rqIcDrg1XOs.

After this, run a command like the following to run the script and add hyperlinks to the plaintext transcript:

python3 convert.py input.txt rqIcDrg1XOs

This will output the updated transcript (including hyperlinks) to an output file named output.txt.

Be sure to review the final transcript for any errors before publishing. Although YouTube generally does a fair job with generating transcription data, it isn’t always perfect.

Headings

H2s should follow the Title case, meaning every word other than minor words (such as articles, short prepositions, or conjunctions in certain cases) should be capitalized.

H3s should follow the Sentence case, meaning that only the first word is capitalized.

Although we do make certain exceptions, please avoid using H4s. We never use H1s or anything smaller than an H4.

Organizing Resources

Chainguard Academy resources are organized based on their location within the Hugo filesystem in the content/ directory. Each piece of content should reside in what we refer to as "buckets"

You can organize resources within the same bucket by assigning them a weight value in the Hugo front matter. Docs with higher weights will appear lower in, say, the left-hand navigation menu.

It often makes sense to organize docs in Alphabetical order (as in the case of our Vulnerability Comparison docs) but other times we may want to sort docs for different purposes. For example, docs in the Working with Images bucket are sorted by what we think readers will want to know first.

Other House Style and Grammar Rules

When it comes to punctuation, we've elected to strictly enforce use of the Oxford comma in resources on Chainguard Academy. This is a comma placed before the coordinating conjunction ("and" or "or") preceding the final element in a series of three or more terms. For example, between the following two examples, only example A would be allowed on Chainguard Academy:

Example A Example B
"My heroes are my parents, Superman, and Wonder Woman." "My heroes are my parents, Superman and Wonder Woman."

Our Academy resources adhere to a "friendly but formal" tone in our documentation. This means our docs aim to be conversational and informative, but generally lack overly expressive language. Avoid exclamation marks, jokes, puns, or strong opinions (unless those opinions are relevant to the case of the resource, as in security best practices).

Generally, the sentence preceding a code block, command, or command output should end in a colon, rather than a period. We haven't always been consistent about this as a team, so if you are editing an older doc and see a period that could be replaced with a colon, do it!

When it comes to bulleted lists, the stem sentence preceding the list should always end with a colon. The list items themselves can end with periods or with no punctuation, but this should be consistent for each item in a given list. They should not end with semicolons.

Additional Tips and Strategies

  • One of the most important parts of any written resource is its title. The title (which is housed in the given tutorial’s front matter), should be specific, reasonably succinct, and make it immediately clear to readers what the tutorial is about. Titles often follow the How to … format, but this isn’t always necessary
  • Never end a section of a tutorial with a code block. There should be introductory and concluding lines in each section to ensure that the reader knows what to expect and understands whether or not they have achieved relevant goals
  • Before publishing or submitting a draft for review, always run through each command within the tutorial in order to ensure that everything works as written

If you have any questions or suggestions regarding our Style Guide, please create an issue and we will follow up accordingly.

Clone this wiki locally