Notes from the terminal

Small scripts beat big frameworks

toolingSeptember 14, 2026 · 6 min read

Every team I have worked on eventually builds the same thing: a tool to automate a boring task. The interesting part is what shape that tool takes. Left alone, it grows from a short script into a command-line application, then into a small internal platform with its own configuration format and its own documentation that nobody reads.

I want to argue for the opposite habit. Write the small script. Keep it small. Throw it away when it stops being useful.

The forty-line rule

My rule of thumb is simple. If a task can be done in about forty lines of shell, it should be. Forty lines fit on one screen, which means the whole thing can be understood without scrolling. A colleague can read it during a code review and actually know what it does.

#!/usr/bin/env bash
set -euo pipefail

# Rotate the oldest log files, keep the last seven days.
LOG_DIR="${1:-/var/log/myapp}"

find "$LOG_DIR" -name '*.log' -mtime +7 -print0 \
  | xargs -0 --no-run-if-empty gzip -9

find "$LOG_DIR" -name '*.log.gz' -mtime +30 -delete
echo "done: $(date -Is)"

That is a real script from a real project. It has run every night for three years. It has never needed a dependency update, and nobody has ever asked me how to configure it.

Why frameworks tempt us

A framework promises that the next script will be easier. That is true, but only if the next script looks like the previous one. Tasks rarely do. A year later you have a framework with four plugins, one of which only works with an older version of the language, and a script that used to take a minute to write now takes an afternoon of reading documentation.

There is also a quieter cost. Once something is a framework, people start to ask how to extend it instead of asking whether they need it at all.

A few rules that keep scripts honest

  • Fail loudly. Use set -euo pipefail and print errors to standard error. A script that silently continues after a failure is worse than no script.
  • Make it safe to run twice. If running it a second time breaks something, the script is not finished yet.
  • Prefer arguments over configuration files. A file is another thing to find, another thing to forget.
  • Write the dry-run first. Print what you would do before you do it. Most of my scripts have a --dry-run flag, and it has saved me more than once.
  • Leave a comment about why. The code says what happens. The comment says why anyone cared.

When to stop

There is a point where a script should become something else. For me the signals are fairly clear. When it needs a data structure more complicated than a list of lines, when it has to talk to three different APIs, or when more than one person needs to change it regularly, it is time to rewrite it in a proper language with tests.

The important part is to rewrite it deliberately, as a new project, rather than letting the script grow one flag at a time until nobody dares to touch it.

The best code I have written this year is code I deleted because a shorter script did the job.

A small habit

Keep a directory of your own scripts under version control, with a short README listing what each one does. It takes ten minutes to set up. Six months later, when you vaguely remember solving this exact problem before, you will know where to look.

That is all. Write small things, keep them small, and be willing to delete them.

← Back to all posts