Development & Code

Flowerboxes: How to Write a Script Header Worth Keeping

The example header here is generalized from one of my own scripts. My name stays on it. Everything else, the company, the client, the servers, is an invented placeholder.

Building a real-world PowerShell deploy script. A 9 part series.

  1. Automating .NET Deployments With One PowerShell Script
  2. Flowerboxes: How to Write a Script Header Worth Keeping (you are here)
  3. The Skeleton and the Two Callers
  4. Choosing What and Where to Deploy
  5. Discovering Components From Disk (coming soon)
  6. Resolving the Deploy Target (coming soon)
  7. The Publish Phase and Its Gotchas (coming soon)
  8. The Main Loop, Safety and Provenance (coming soon)
  9. Testing a Tool That Can Do Damage (coming soon)

Open a script someone else wrote, or one you wrote a year ago, and the first thing you want is a straight answer to four questions. What is this? Who wrote it? How do I run it or learn more? What has changed over time? A flowerbox answers all four in the first screen, before you read a single line of code.

A flowerbox is the boxed banner comment at the top of a file. The name comes from the frame of comment characters around it. Done well, it is the cheapest documentation you will ever write. Done badly, it is a rotting block of lies that everyone learns to ignore. This is how to write one that earns its space.

What a good flowerbox is for

It is not there to explain how the code works. The code explains that, and if it does not, comments in the body are the place for it. The flowerbox is orientation. It tells a human who just opened the file where they are and where to go next.

That means it answers identity, purpose, pointers and history. Nothing more. The moment you start pasting the design rationale into the header, it becomes too long to read and too painful to keep current, so it stops being read and stops being kept current. Keep it lean and point outward for the depth.

The anatomy

Here is the shape I use, section by section.

Identity block. Who and what, at a glance.

  • Script name
  • Project or scope
  • Author
  • Prepared by and prepared for, if it is client or contract work
  • Version
  • Date

Summary. A few lines on what it does and, importantly, its contract. For a tool that other tools call, the contract is the most valuable line in the whole file. Exit 0 means success, exit 1 means failure. State it.

Documentation pointers. This is the section most people miss. Instead of stuffing the design into the header, list the documents that hold it and say what each one covers. The header stays short and the deep material lives where it can breathe.

Acknowledgements. If someone else defined the process you automated, or handed you the domain knowledge, credit them. It is the right thing to do and it tells the next reader who to ask.

Revision history. A small table: version, date, author, notes. This is the part that proves the header is alive. If the last entry is the initial release and the file has clearly changed since, the whole box loses trust.

A full worked example

Here is a complete header from a real deploy script, generalized:

#===============================================================================
# Script Name : Publish-FullFlow.ps1
# Project     : All app packages. One script for every repo, not copy-per-package.
#               Pick the package with -Package.
# Author      : Dennis Ayotte
# Prepared By : Example Consulting
# Prepared For: Contoso Health, State of Example
# Version     : 1.0
# Date        : 2026-08-31
#
# SUMMARY
#   Builds, tests and publishes one package and all of its components in bottom-up
#   deploy order, on one machine. Per component: (1) license check if the project
#   needs the licensed UI component suite, (2) dotnet build, (3) dotnet test (skip
#   with -RunTests $false), (4) dotnet build -p:DeployOnBuild=true, the publish.
#   The run stops at the first failure; a component is never skipped silently.
#
#   CONTRACT: exit 0 = success, exit 1 = failure. A partial deploy is never
#   reported as success.
#
# FULL DOCUMENTATION  (kept out of this script on purpose)
#   DESIGN-NOTES.md   how it works and why: target resolution, discovery, testing
#   RUNBOOK.md        how to run it: commands, environments, promotion path
#   DEV-NOTES.md      repo and environment findings for the dev team
#
# ACKNOWLEDGEMENTS
#   The client's lead developers defined the deployment process this script
#   automates. They walked through the full build-and-publish flow, the component
#   tiers and their bottom-up order, and the publish profiles and their targets.
#   The discovery and tier logic here follows the process they set out.
#
# REVISION HISTORY
#   Ver   Date        Author         Notes
#   ---   ----------  -------------  ---------------------------------------------
#   1.0   2026-08-31  Dennis Ayotte  Initial release. Validated by a clean compile
#                                    sweep and end-to-end deploys to staging.
#===============================================================================

Notice what is not in there. There is no explanation of the discovery algorithm, no list of every parameter, no essay on why a deploy target must never be guessed. All of that is real and important, and all of it lives in the linked documents. The header points at them and stops.

The rules that keep it from rotting

  • Keep it lean, point outward. If a section is growing past a few lines, it belongs in a document the header links to, not in the header.
  • Update the version and the history when you change the file. A stale revision history is worse than none, because it actively lies. Make bumping it part of the change, not an afterthought.
  • Do not restate the code. “Loops through the components” is noise. The reader can see the loop. Say what the script is for and what it guarantees.
  • State the contract. Exit codes, what counts as success, what it will refuse to do. This is the line automation authors and future-you will thank you for.
  • Write it for the person who is stuck, not the person who is showing off. Plain language. No cleverness.

A PowerShell note: the flowerbox is not the whole story

In PowerShell there are two different kinds of header, and the good scripts use both.

The flowerbox above is for a human who opens the file. PowerShell also has comment-based help, a <# ... #> block with keywords the tooling reads, so that Get-Help .\Publish-FullFlow.ps1 -Full works from the console and IDEs show parameter hints.

<#
.SYNOPSIS
    Builds, tests and publishes one package and all of its components.
.DESCRIPTION
    Discovers components from the repo on disk, builds them in bottom-up deploy
    order, and publishes each one. Stops at the first failure.
.PARAMETER Package
    The package to deploy. Omit for an interactive menu.
.PARAMETER Environment
    Target environment. Required. There is no default, on purpose.
.EXAMPLE
    .\Publish-FullFlow.ps1 -Package Orders -Environment Staging
.NOTES
    Exit 0 = success, exit 1 = failure.
#>

The flowerbox orients a person reading the source. The comment-based help feeds the tools. If a script is something other people run, write both. If it is a quick personal utility, the flowerbox alone is plenty.

If you want the wider picture on comments that pull their weight, I wrote about that in How to Actually Comment Code Without Wasting Everyone’s Time, and the same “point outward” idea applies to project docs in Your README Sucks: Here’s How to Write One That Doesn’t.

Your turn

Do you write flowerboxes, or do you consider them clutter? Do you keep a revision history in the file, or leave that entirely to git? And if you use PowerShell, do you bother with comment-based help, or just the banner? Tell me how you document the top of a file.

// comments

← all posts more in Development & Code →