Production engineering guide for platform documentation covering patterns, implementation strategies, and operational best practices.
Platform documentation in an engineering wiki is the structured, versioned, and discoverable body of reference material that describes how a platform is built, deployed, operated, and evolved. It matters whenever a team needs to understand a component without direct tribal knowledge, when onboarding new engineers, during incident triage, or before modifying platform infrastructure. Unlike narrative tutorials or marketing overviews, platform documentation focuses on concrete specifications: API schemas, resource definitions, deployment manifests, runbooks, and configuration boundaries. It is the difference between guessing how a service starts and reading the exact manifest that kubectl applies, or between hunting through chat history and finding the exact psql command that resets a feature flag.
The first challenge is locating the authoritative source among potentially many files, repositories, or generated outputs. In a typical platform setup, documentation lives alongside code in the same version control system, often under a docs/ directory at the repository root. Use git to scope your search:
git log --all --oneline -- docs/ | head -20
This lists every commit that touched the documentation directory, ordered by recency. To see what changed in a specific file:
git log --follow -- docs/services/auth.md
If the platform publishes versioned references (e.g., OpenAPI or JSON Schema), those are usually tagged:
git tag --list 'v*' | sort -V
git show v1.4.2:docs/api/openapi.yaml
For platforms that expose machine‑readable descriptors over HTTP, a simple curl fetch can reveal the latest spec:
curl -sSL https://raw.githubusercontent.com/owner/repo/main/docs/api/openapi.json | jq '.info.version'
When documentation is split across multiple repositories or services, a centralized index—often a README.md at the root of a platform repo—maps component names to their doc paths. Treat that index as the single source of truth for navigation; if it diverges from the actual file layout, the index itself becomes the first point of failure.
Once the right file is located, the reader needs to extract the minimal information required to act. Real tools provide immediate, concrete output.
Kubernetes manifests are the most common spec format in platform engineering. To inspect the approved schema for a resource without applying it:
kubectl explain deployment.spec.replicas --default=1
The output is a human‑readable description of the field type, default, and whether it’s immutable. For a machine‑parseable view, the same command with --recursive prints the full JSON schema that kubectl internally uses:
kubectl explain deployment.spec --o=json | jq '.properties'
PostgreSQL platforms often rely on SQL schemas that double as documentation. To list all columns, their types, and constraints for a table:
psql -c "\d+ users"
Typical output includes column name, type, not‑null status, default, and any check constraints. If the platform uses role‑based access control, the same command reveals which columns are visible to the current user.
Raw HTTP APIs are frequently documented as OpenAPI or AsyncAPI files. To fetch the live description and inspect a single endpoint:
curl -sSL https://platform.example.com/v1/openapi.json | jq '.paths["/deployments"].get'
The returned JSON shape—summary, parameters, responses—is the exact contract the platform enforces. If the API returns a different shape, the documentation is out of sync with the running service.
Docker and container images often carry metadata in their LABEL instructions. To view the recorded version or build date from an image that’s already running on a host:
docker inspect my-platform-api --format='{{index .Config.Labels "build-version"}}'
This command prints the exact label value that was set during docker build. If the label is missing, the image was built without the agreed‑upon metadata pipeline, a common sharp edge.
Keeping documentation in sync with the platform is a persistent problem. The most reliable pattern is to treat docs as code: store them in the same repository as the manifests, schemas, or source they describe, and gate updates through the same CI/CD pipeline.
A typical workflow looks like this:
git checkout -b docs/update-replicas
replicas: 2 to replicas: 3 in deployment.yaml).markdownlint or prettier) and, if the repo uses mkdocs or docfx, builds the site locally: mkdocs build
When the platform uses a GitOps operator (e.g., Argo CD or Flux), documentation can be synchronized automatically. The operator watches a Documentation custom resource and renders the site into a Kubernetes ConfigMap or Secret. An update to the source YAML triggers a new Documentation revision, which the operator pushes to the serving endpoint. The exact command to trigger a manual sync, if needed, is:
argocd app sync platform-docs
If the platform does not use GitOps, a simple make target often builds and publishes docs:
make docs-deploy
The Makefile typically runs mkdocs gh-deploy or docfx publish, pushing the generated static site to a gh-pages branch or a web‑root. Any deviation—such as a stale mkdocs.yml that references removed pages—will cause the build to fail with a clear error:
ERROR: Material reference 'services/auth.md' not found
Sharp operators know to treat such failures as signals that the source file was deleted or renamed without a corresponding index update.
When a platform change produces unexpected behavior, documentation is often the first place investigators look—and the first place they’re misled.
Stale OpenAPI descriptions are a frequent root cause. A service may have added a new query parameter, but the published JSON file still lists the old set. A consumer reads the doc, builds a client, and receives 400 Bad Request because the server rejects the unfamiliar parameter. The error text typically reads:
{"error":"unexpected query parameter", "expected":["id","name"], "received":["id","name","status"]}
Re‑generating the spec from the server code (npm run generate-openapi) and overwriting the doc file resolves the mismatch.
Missing runbook cross‑references surface during incidents. A kubectl get pods output shows a container crashing with OOMKilled, but the wiki’s troubleshooting section only documents memory‑pressure handling for a different component. The operator spends twenty minutes grepping logs before realizing the correct runbook is under environment-management/network-timeouts.md. The silence of the wrong section is itself a failure mode: the doc exists, but it’s not the one the operator needs.
Authentication tokens embedded in documentation are a security sharp edge. A docker run command documented in the wiki might include --env API_TOKEN=abc123. If that repo is public, the token is compromised. The exact failure mode is not a service outage but a leaked secret that appears in CI logs, alerting tools, and eventually requiring rotation. The recommended pattern is to never commit secrets; instead, document the env var name only, and rely on the platform’s secret management (e.g., Kubernetes Secret or cloud‑native vault) to inject the value at runtime. A kubectl get secret check will confirm whether the secret exists:
kubectl get secret api-token -n platform -o yaml
If the output is Error: secrets "api-token" not found, the documentation references a missing resource, and the platform will either fall back to a default (often documented elsewhere) or reject the request outright.
Several failure modes are learned only after a production incident.
kubectl apply --validate=false skips server‑side schema checking. It’s useful for migrating resources that don’t conform to the new spec, but it silently accepts malformed YAML. A subsequent kubectl get may show Invalid field value for a field that the operator quietly ignores during apply. The only way to detect this is to follow apply with kubectl get -o yaml and verify every field.kubectl create --dry-run=client prints the object as it would be sent to the API, while kubectl dry-run (without client) actually sends a dry‑run request to the server. Mixing them up leads to “object would be created” messages that are actually just local prints, not server‑validated previews. The error only appears later when a real apply fails because the server rejected the object.make generate-skipped in CI), the published spec lags behind the actual handlers. The lag isn’t semantic—fields are added or removed at runtime—but the doc still lists the old shape. Consumers build clients against the stale spec, and every request returns 422 Unprocessable Entity. The fix is to enforce the generation step as a pre‑commit hook or a CI gate.kubectl output. Documentation that wasn’t updated to note PodSecurityPolicy deprecation will lead operators to attempt creation of PSP resources, which are silently rejected. The error message is cryptic:Error: PodSecurityPolicy "default" is deprecated in v1.28, use SecurityContextConstraints instead
The operator must cross‑reference the release notes and update the doc’s “migration” section. Until then, any attempt to apply a PSP manifest fails without obvious cause.
config.yaml with defaults for replicaCount, imageTag, and resources.limits. If the wiki documents only the production values, a developer working on a local Minikube instance may copy the production block verbatim, causing resource exhaustion on a single‑node cluster. The exact failure is kubelet has insufficient memory available followed by pod eviction. The sharp edge is the assumption that documented defaults are universal; in reality, most platforms use a values.yaml per environment, and the top‑level config.yaml is just the production overlay.Understanding these edges comes from reading the exact error text, running the exact commands, and never assuming that a documented “default” applies outside its intended scope. Platform documentation that acknowledges these boundaries—listing the exact flag, the exact error, the exact config key—becomes a reliable operational tool rather than a decorative afterthought.
This page was rewritten on 18 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy