asciidoctor -a env-internal --out-file=docs.html docs.adoc
Brian Dominick, DocOps Lab
docopslab.org
25 years software development experience
10 years tech-docs specialization
Tech writer for BigData Engineering startup (2015-2018)
DocOps contractor since acquisition (2018-present)
Working on DocOps framework, utilities, libraries, and trainings/courses
Write the Docs participant since 2017
You are either a product developer or technical writer (or aspiring).
You are looking to initiate or improve the handling of internal documentation at some kind of engineering organization (or expect to).
Product is enterprise software
Subject-matter experts are developers and product managers
Product audience includes downstream developers and end users
| Git | Leading (dominant) version-control system for flat-file tracking |
| API | Application programming interface |
| SDK | Software development kit |
| docs-as-code | Documentation authored using the same tools and processes as product code |
Someone who contributes substantially to docs
Technical writer
Project or product manager
Customer support technician or represntative
Someone who uses the most-abstract interfaces of the product (mobile apps, web forms, even CLIs)
Or “dev user”, someone who uses the product’s APIs, SDKs, etc
focus on interfaces
consumer is customer / client / prospect / user
includes downstream “dev docs”: API/SDK/etc
also focus on interfaces
including private APIs, etc
also cover resources
assets, infrastructure, processes, styles
consumer is coworker / collaborator / successor / you
may be publicly accessible (open source)
Documentation of overlapping subjects is authored and output in different ways, using different tools.
Content divergence is an inevitable problem that can be solved with innovation.
Source duplication is a problem that can only be solved with manual effort (or elimintation).
developers, TWs, PMs
WYSIWYG, CCMS/XML, lightweight markup, Git, etc
end users, downstream devs, internal devs, etc
static site, wiki, PDF, etc
| Internal | User/Dev Docs |
|---|---|
Confluence | Markdown & GitHub Pages |
Google Docs/Sheets | ReadTheDocs |
Notion | OpenAPI/Markdown |
SharePoint / Office 365 | MadCap Flare / DITA |
The product source code.
The documentation source code.
It matters not at all what is written in Confluence or Markdown or Flare if the source code does not agree.
| Internal Docs | Public Docs | |
|---|---|---|
Internal Devs | Public & private APIs, SDKs, policies | N/A |
Downstream Devs | N/A | Public APIs, SDKs, etc |
End Users | N/A | Public UIs, tutorials, etc |

Same API might have 90% overlap, 10% private endpoints. Docs sourced in code or aside.
Config is defined internally, used by downstream devs and end users. Docs usually sourced aside.
2 API references: public on Mintlify or SwaggerHub, used by both internal and downstream devs, private in Confluence or Notion used by internal devs.
2-4 config guides: public on ReadTheDocs, premium for paid users in the WebUI, private versions of both on GitHub, used by devs and QA testers.
Internal Docs are:
more directly maintained by SMEs
fewer constraints on delivery methods/formats
not expected to be as “polished”
backed up by code (real source of truth)
External/User Docs are:
easy for users to discover and access
expected to be more polished and accurate
| Audience | Potential Cost |
|---|---|
customer | revenue loss |
prospect | deal loss |
coworker | frustration, cycles |
open-source dev | code contributions |
All docs are first-class docs.
Meet developers where they’re at (authoring and discovery).
Minimize the number of technologies.
Don’t repeat yourself (DRY).
edited and architected by content professionals
delivered conveniently
accurate
polished
Use existing dev platforms
JIRA/GitHub Issues
Confluence/Wiki
Docs-as-code via Git
Commandline tools
Deliver to accessible platform
Could be Confluence
Could be static site on company VPN
Could be GitHub wiki or Notion
SSG or Wiki/CMS?
collaboration platform
runtime environments / Docker
linters
deployment platform
Content markup (Markdown, AsciiDoc*, rST)
Data markup (YAML*, JSON, CSV)
Template markup (Liquid*, Jinja2, Handlebars)
Scripting (Bash*, Python, Rust, Golang)
DRY / Single Source of Truth (SSoT)
True single sourcing defines product and docs
Minimize "hand-offs" between SMEs & documentarians
Generate multiple versions of the same document
differ by audience role
differ by delivery method
Use modular content
if/else conditions
transclusion of reusable content

asciidoctor -a env-internal --out-file=docs.html docs.adoc
properties:
base_url:
summary: The base path to the resource.
default: /resource
timeout:
summary: The timeout for the request in seconds.
default: 30
retries:
summary: The number of retries for the request.
default: 3
private: true



base_url: /resource # The base path to the resource.
timeout: 30 # The timeout for the request in seconds.import yaml
with open('config.yaml', 'r') as file:
config_file = yaml.safe_load(file)
class Config:
def __init__(self, properties):
self.properties = {k: Property(**v) for k, v in properties.items()}
class Property:
def __init__(self, summary, default=None, private=None):
self.summary = summary
self.default = default
self.private = private
config = Config(config_file['properties'])use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::BufReader;
#[derive(Debug, Deserialize, Serialize)]
struct Config {
properties: std::collections::HashMap,
}
#[derive(Debug, Deserialize, Serialize)]
struct Property {
summary: String,
default: Option,
private: Option,
} import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.io.File;
import java.util.Map;
public class ConfigLoader {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
Map config = mapper.readValue(new File("config.yaml"), Map.class);
System.out.println(config);
}
} require 'yaml'
config = YAML.load_file('config.yaml')
class Config
attr_accessor :properties
def initialize(properties)
@properties = properties.transform_values { |v| OpenStruct.new(v) }
end
end
config_obj = Config.new(config['properties'])
import unittest
config = Config(config_file['properties'])
class TestConfig(unittest.TestCase):
def test_properties(self):
for key, prop in self.config['properties'].items():
with self.subTest(key=key):
self.assertIn('summary', prop)
if 'default' in prop:
self.assertIsInstance(prop['default'], str)
if 'private' in prop:
self.assertIsInstance(prop['private'], bool)
if __name__ == '__main__':
unittest.main()Bridging the internal/external divide
Work tasks are tracked internally in Jira (or GitHub Issues, etc)
Release notes drafted in Confluence (or Google Doc, etc)
This is an extra burden at release-time
Release notes are often incomplete or inaccurate
Use a "Release Note" field in each qualifying Jira work item
Use a script to:
download the notes and
draft a Markdown file (via template)
Edit and save the file in Git
python release_notes.py --project acme-cloud --version 2.3.0
## Release Notes for {{ project }} {{ version }}
{% for issue in issues %}
### {{ issue.summary }} ({{ issue.key }})
{% if issue.release_notes %}
{{ issue.release_notes }}
{% endif %}
{% endfor %}## Release Notes for ACME-Cloud 2.3.0 ### Fix login issue (CLOUD-453) Some users were unable to log in to the ACME-Cloud platform. The authentication service has been updated to resolve this issue. ### Improve performance of database queries (CLOUD-456) ### Add new API endpoint for image management (CLOUD-489) This issue adds a new API endpoint (`/images`) for managing image assets in the ACME-Cloud platform. See [API Reference](/docs/api-reference/endpoints/images) for details.
Meets developers where they are
Uses existing tools (Jira, Git, Markdown, static site)
Reduces duplication of effort
Improves accuracy and completeness of release notes
Unified search
Documentation audits
Alignment with product versioning
Document lifecycle management
Docs-as-code
REST APIs (Confluence, GitHub, etc)
Templating languages/engines
Conditional content
Structured / modular documentation
https://www.writethedocs.org/slack (@BrianD)
Making Yourself Redundant on Day One
(WtD Australia 2018 Talk by Alexandra Perkins)
Sociological considerations in designing an internal documentation platform
(WtD Portland 2024 Talk by Alistair Gray)