Wesley Dean
template-bash, a starter for Bash engineering image

template-bash, a starter for Bash engineering

7 min read

Over the past several weeks, I have written about a few small Bash tools that grew out of larger projects: mktext for literal text substitution, bash-doxygen for source documentation, bashdeps for exact artifact materialization, and bashlog for predictable logging and explicit redaction.

Those tools each have intentionally narrow responsibilities. At some point, though, I also needed a place to answer a different question: what does a Bash project look like when these practices are assembled into one coherent starting point?

That is the role of template-bash.

template-bash is a private repository I maintain alongside Getting Started with Bash Engineering. It is a starter for maintainable, documented, tested, and releasable Bash projects, and it consolidates patterns that have been exercised across adrctl, bashdeps, Bootstrap, mktext, bashlog, and related work.

The GitHub repository itself is private, and I am not distributing repository access. I publish a versioned export of the starter as the Bash Engineering Starter Kit. That archive is available free to people who request it through the book’s companion-resource page.

A starter rather than a framework

The most important distinction is that template-bash is a starter rather than a framework.

It is not intended to become a Bash application framework with an opinion about every project. It does not prescribe the same module structure, runtime extension model, or feature set for every Bash repository, and its files are not meant to be copied unchanged and then forgotten.

The repository is designed to be adapted.

A new project starts by changing its project identity, replacing example behavior, and reviewing the starter’s Architecture Decision Records to decide which constraints still apply. Behavior that does not belong in the derived project should be removed rather than preserved merely because it came from the starter.

That distinction matters to me. A template is useful when it shortens the path to a well-considered project. It becomes harmful when inherited choices turn into invisible requirements.

Make the lifecycle visible

GNU Make is the canonical orchestration surface in the starter. The targets are intended to keep important lifecycle boundaries visible:

make deps        prepare repository dependencies; network access may occur
make deps-check  verify prepared dependency state offline
make build       build from current local inputs
make check       run syntax and static-analysis checks
make format      apply the project's formatting policy
make test        run behavior tests against shipped artifacts
make docs        generate reference documentation
make clean       remove generated outputs

The distinction between deps and build is deliberate. A build should not silently reach out to the network because a dependency happens to be missing. Dependency acquisition is a different operation with a different trust and failure boundary.

That principle appears throughout the starter: operations that may change state, access the network, publish artifacts, or affect trust should be visible rather than hidden behind convenient-looking commands.

Modular maintained source, standalone artifacts

The maintained Bash source is modular, with responsibility-focused files and an explicit core source order. The starter also demonstrates deterministic additive module discovery, including an example plugin registry, but that runtime model is teaching material rather than a requirement.

A derived project may keep the registry, replace it, enumerate every module explicitly, or remove plugin discovery entirely.

The more durable lesson is that source organization and consumer packaging are separate concerns. Maintainers can work with smaller files while consumers still receive standalone artifacts.

A normal build produces three representations:

dist/PROJECT.dev.bash
dist/PROJECT.bash
dist/PROJECT.min.bash

Each artifact has a corresponding .sha256 checksum companion, and every artifact flavor is expected to satisfy the same public behavior tests.

The development artifact retains verbose Doxygen documentation. The ordinary artifact removes full-line comments while remaining readable, and the minified artifact provides a compact representation for cases where that is useful.

Documentation is part of the project architecture

One of the recurring ideas in Getting Started with Bash Engineering is that documentation should carry different kinds of information at the level where that information belongs.

template-bash puts that idea into practice.

The repository separates several documentation concerns:

  • the README provides orientation and adaptation guidance;
  • an engineering-philosophy document captures reusable engineering instincts;
  • a concise decisions document provides an architectural map;
  • Architecture Decision Records preserve durable reasoning and tradeoffs;
  • Doxygen comments describe implementation contracts beside maintained source;
  • a threat-modeling exercise helps projects examine security-relevant authority;
  • repository-facing contribution, support, and security files are treated as maintained product surfaces; and
  • behavior tests provide executable evidence for documented contracts.

The hierarchy matters because those documents answer different questions. A short contributor summary should not silently replace the reasoning preserved in an ADR, and a passing test does not by itself explain why a particular behavior exists.

Dependencies are part of the trust boundary

The starter uses bashdeps for pinned repository dependencies such as scripts, libraries, filters, and assets. It deliberately distinguishes dependency acquisition from dependency trust.

A checksum can establish that the bytes on disk match the bytes a project approved. It cannot establish that those bytes are safe, appropriate, or worthy of the authority the consuming project gives them.

That leads to a broader rule in the starter: every dependency expands the trusted computing base. Runtime libraries, build tools, CI actions, documentation filters, and release tooling all deserve review according to the code they execute, the data they can observe, and the authority they inherit.

The starter therefore combines pinning and verification with a more explicit question: what are we trusting this dependency to do?

Testing the things people actually consume

The behavior suite uses Bats and runs against every shipped artifact flavor. That is intentional.

A project can have perfectly good tests against maintained source and still ship a broken generated artifact if concatenation, comment stripping, minification, metadata injection, or packaging changes behavior.

Generated artifacts are products when users consume them. The starter treats them that way.

The same idea carries into security-sensitive behavior. Tests should include negative assertions where the contract says something must never appear or must never happen. Demonstrating that the expected output exists is incomplete if a secret, unsafe value, or forbidden side effect can also occur elsewhere.

The engineering posture behind it

The starter contains substantial machinery. The recurring principles matter more to me than any particular file layout:

  • make important policy choices explicit;
  • prefer bounded contracts over hidden inference;
  • keep stdout, stderr, files, exit status, and side effects intentional;
  • separate build, dependency, test, documentation, and release concerns where their boundaries matter;
  • treat readability and auditability as correctness properties;
  • keep public interfaces conservative;
  • make network and external-command boundaries visible;
  • describe both promises and non-promises for consequential behavior;
  • treat dependencies as additions to the trusted computing base; and
  • record deliberate divergence when a project’s needs differ from the starter.

Those ideas are also a large part of what I am exploring in Getting Started with Bash Engineering. The book explains the reasoning and consequences in considerably more detail; template-bash provides a concrete project in which those practices can interact.

The free Bash Engineering Starter Kit

The maintained template-bash repository will remain private. The companion resource is a versioned archive exported from that repository and distributed as the Bash Engineering Starter Kit.

The archive includes the adaptable Bash project itself along with examples of the Make-based lifecycle, modular source, Bats testing, static analysis, formatting, CI, releases, documentation, checksums, provenance, Architecture Decision Records, and a scoring rubric for examining an existing Bash project.

The Starter Kit is free. You can request a copy at wesleydean.com/bashengineeringbook. As the engineering evolves, updated versions of the archive can be distributed from the same place without making the private development repository part of the public interface.

That arrangement gives me a maintained upstream project I can continue to evolve, while readers receive a stable, versioned artifact they can inspect, adapt, and use as a reference alongside the book.

Tags