The Art of Templating

ArchUnit Logo

As your documentation project expands, you might reuse similar content across various sections. This process starts simple. Then it often becomes more complicated when you incorporate variants and placeholders into your text. And sometimes you don’t want to write content by hand, but generate it from some data you already have.

Different technologies provide unique patterns for this challenge. This summarizes our Continuous Documentation Regulars meetup with lots of examples and links to explore.

If you find this useful, subscribe to the meetup and visit one of our upcoming events!

Templates when writing

Templates help when authoring content. Some templates help you structuring it, others contain the repeating pieces to help you with documentation-as-code syntax.

When using an IDE from the IntelliJ family, the live templates help you with the latter. The AsciiDoc plugin for IntelliJ installs a set of templates, and the IDE prompts a drop down list once you type ad-. You can then select the admonition template with an autocomplete for the different admonition types, or one of the other templates like tables, lists or source code snippets. A similar feature is called snippets in VSCode. To optimize your workflow, consider adding your own to the IDE to simplify it. You might even share them with your colleagues, or contribute them to the plugin for everyone to use them.

Other templates provide structure to your content:

  • The arc42 template is a well-known template for IT architecture documentation. Such a template allows multiple projects to have a similar structure documentation, which allows readers to find their way in the docs faster. Having the structure in place, it simplifies contributions from non-writers: They can figure out where to put their contents faster and have examples of how to write the documentation at hand.
  • In a similar way, the Good Docs Project has gone to some length to provide templates.
  • The service readme.so generates a README file for your project.
  • The Grafana’s Writers Toolkit defined templates for each topic type to simplify writing.

Reusing content for rendering

While the templates listed above help with writing, you don’t want to go down the route of duplicating the same content in multiple places.

With AsciiDoc as a language, you have includes available, which allow you to include the same content in multiple places while writing and maintaining it only once. But what to do when you want to have parameters for your template? Attributes to the rescue! Here’s a pattern we use in the Keycloak documentation regularly. They look very low tech at the start, still they are maintainable by technical writers with minimal coding experience, and they render nicely in the preview of an IDE.

Call of a template from the documentation
// parent document
:param_deprecated_feature: flux compensator 1.0
:param_deprecated_alternative: flux compensator 2.0
include::template/deprecated.adoc[]
Common template for all deprecated features
// template file 'deprecated.adoc'
[WARNING]
====
The feature {param_deprecated_feature} is depreated!
ifdef::param_deprecated_alternative[]
Use {param_deprecated_alternative} as the alternative.
endif::param_deprecated_alternative[]
====
// After the template, clear the parameters so they don't end up in the next call of the template by accident
:param_deprecated_feature!:
:param_deprecated_alternative!:

Once you set the AsciiDoc attribute attribute-missing to the value warn, you’ll see warnings on the console when converting the document for any missing attribute. Going one step further, by defining the failure level, you can make the conversion fail when there is any warning. Using both settings together, you’ll never publish the docs with an undefined attribute any more when you forget to set it or misspell it.

As an alternative, you can check within your template if an important attribute is missing and output a corresponding warning. An automated build process can then look for the text in the output and fail the build.

Example code to test for a missing attribute
ifndef::param_deprecated_feature[#please specify :param_deprecaded_feature#]

If your markup language doesn’t allow for including content from other files, maybe your static site generator can jump to your rescue. Some support macros to support includes or macros in your content. See, for example, Jekyll’s includes for examples how this works, or Hugo’s shortcodes.

Once those are not enough, you might consider writing your own macros for the markup language of your choice. For AsciiDoc, there is the Asciidoctor extension lab which custom macros that you can use as a starting point.

Producing variants of your documentation

Once you’ve written a technical guide, you might want to create a variant for a another programming language. The tools from the previous paragraph might help here, so you could write a long page covering all languages, and then use the AsciiDoc ifdef::[] or a similar pattern of the markup language or your choice to render multiple variants of the same page.

Or you choose to render all content in one page, and have the user choose between the different language examples via different tabs. This example shows the Couchbase documentation, which is based on the Asciidoctor tabs extension.

couchbase
Figure 1. Screenshot from the Couchbase documentation

Excursion: A Stripe reference API docs contain a similar switcher for the language. Still we can’t look into the setup of the site to see how it has been generated.

Using JavaScript to switch contents can show user-specific keyboard shortcuts in the docs. Visit the Using Hotkeys page of the AsciiDoc plugin for IntelliJ, and use the drop-down to switch the operating system. Thanks to the IntelliJ online documentation that inspired this feature. With client-side JavaScript it detects the user’s operating system to present a good default and allows the user to switch to different operating systems as needed. In this custom Antora theme, this drop-box is activated once there is any content marked to be operating specific with a CSS class, like in the following example:

Table with keyboard shortcuts marked for a specific operating system
|===
| Description | Shortcut

| Basic code completion
| [.windows.linux]#kbd:[Ctrl+Space]# [.macos]#kbd:[⌃ Space]#
// ...
|===

Template languages for generating content

Once you set up a build process, other templating languages come into play which may be specific to the (static) site generator. See Comparing HTML Preprocessor Features for some of the generic templating languages, and what those can do for you. Beware: there are lots of other templating languages around. To add to the list, the Keycloak project I work on uses Freemarker, and the static site generator Hugo uses Go templating.

Those templating languages can do two things:

  1. Generate AsciiDoc contents that are then processed like the hand-written AsciiDoc contents.

    This can generate, for example, a list of available command-line options or possible error messages directly from annotations in the source code.

  2. Add navigation and theming once the AsciiDoc content has been converted to HTML.

    Antora uses Handlebar templates to do just that (which you can extend), and Hugo uses Go templating for that.

Examples for generating contents

During the Meetup, we looked at some examples.

  • To convert an OpenAPI specification to documentation to AsciiDoc, have a look at docToolchain’s exportOpenAPI task, or supply your set of templates to the OpenAPI Generator to create the contents you need.
  • The templating language Nunjucks stands out as it checks a lot of the boxes in Comparing HTML Preprocessor Features. If your into Markdown, which doesn’t contain a preprocessor for includes and conditionals, try out the combination of Markdown and Nunjucks with the static site generator Eleventy, as it adds templating functionality when using Markdown and HTML contents. From my experiments, this is not available when you write your contents in AsciiDoc.
  • The people at Couchbase created a custom template extension, which allows them to JSON data and Handlebars templates to create AsciiDoc content. That is so cool!

    [template,example$some-data.json]
    --
    // include the Handlebar template, or write inline
    include::partial$templates/test.hbs[]
    --
  • PlantUML is well-known to create diagrams from text. Today we saw it can visualize JSON, and that you can have contents in JSON as data for a PlantUML diagram template, OMG! To read the JSON data from a file, consider mixing in an AsciiDoc include::[] preprocessor macro inside the PlantUML diagram definition similar to the previous paragraph.

Example: AsciiDoc WG website built with Hugo

An example we looked at during the Meetup is the AsciiDoc Working group’s website that is built with Hugo and a custom Eclipse template. It contains the following parts:

  1. Content written in AsciiDoc with metadata in the frontmatter, describing the two working group members Amarantha and Guillaume.
  2. A HTML template with Go templating to render the contents on the list of members.

See the working group’s member page and see that because of the additional metadata, Guillaume’s summary has a link “Read more”, while Amarantha’s hasn’t.

Is it already over?

We’ve covered a lot in this 90 minutes meetup. Templates are an important part of technical writing. Using them can make creating content simpler, and the result more consistent.

Thank you very much everyone who joined the discussion during the Meetup! Did I forget something? Let me know, and I’ll add it.

Do you want more? Subscribe to the Continuous Documentation Regulars to get notified about upcoming meetings.

Back to overview...