On this page
Start from the script you actually have
The example is a script that reads a CSV export of webinar feedback, groups comments by session and writes a short report. It works on the author's laptop, but the file path is hard-coded, it crashes on an empty row and nobody else knows how to run it. That is a normal starting point. The aim is not to rewrite it as a framework but to make it dependable with a handful of deliberate changes.
Before changing anything, run the script once on a real export and save the output. That output becomes your first regression check: after each improvement, the report for the same input should not change unless you intended it to.
Give the code a shape
Split the script into three kinds of function: reading input, transforming data and writing output. Transformation functions should take plain Python values and return plain values, with no file or network access. They are the part most worth testing and the part most likely to be reused.
def load_rows(path: Path) -> list[dict[str, str]]: ...
def group_by_session(rows: list[dict[str, str]]) -> dict[str, list[str]]: ...
def render_report(groups: dict[str, list[str]]) -> str: ...
def main(path: Path, out: Path) -> None:
rows = load_rows(path)
out.write_text(render_report(group_by_session(rows)), encoding="utf-8")Add type hints as you go. They document what each function expects, help your editor catch mistakes and make the boundaries between steps visible. Use pathlib.Path instead of string paths so joining, reading and checking files is explicit.
Handle real input, not ideal input
Real exports contain blank rows, missing columns, different encodings and duplicate entries. Decide how the tool should treat each case and make the decision visible. Skipping a malformed row silently hides problems; failing on the first one makes the tool unusable. A good middle ground is to skip invalid rows, count them and report the count with examples at the end.
- Open files with an explicit encoding, usually utf-8, and handle a byte-order mark if your exports include one.
- Check required columns once, when the file is opened, and stop with a clear message if any are missing.
- Strip whitespace and normalise case for keys such as session IDs before grouping.
- Collect row-level problems in a list and print a summary instead of raising on the first bad row.
Move settings out of the code
Hard-coded paths and thresholds are the main reason a script works only for its author. Accept input and output paths as command-line arguments, and read optional settings such as the minimum comment length from arguments or environment variables with sensible defaults. The standard library's argparse module is enough for a small tool and produces a --help message automatically.
parser = argparse.ArgumentParser(description="Summarise webinar feedback")
parser.add_argument("input", type=Path)
parser.add_argument("--out", type=Path, default=Path("report.md"))
args = parser.parse_args()
main(args.input, args.out)Use the if __name__ == "__main__": guard so the module can be imported by tests without running the command.
Test the parts that make decisions
Write tests for the transformation functions with small, hand-made inputs: an empty list, a single session, several sessions and rows with messy whitespace. Add one end-to-end test that runs main on a tiny CSV in a temporary directory and compares the output with an expected report. pytest's tmp_path fixture makes this straightforward.
The earlier saved output from the real export can serve as a regression test as long as it contains no personal data. If it does, create a synthetic file with the same structure instead.
Package it so someone else can run it
Create a virtual environment, list dependencies in a pyproject.toml or requirements file and add a short README with three things: how to install, how to run with an example command and what the output looks like. If the tool uses only the standard library, say so; it is a useful property.
Finally, ask a colleague to run it from the README without your help. Every question they ask points to a missing line in the documentation or a missing check in the code.
Share or cite this guide
Innovate with Ajay Editorial (2026-10-07). Python for AI Builders: Turn a Script into a Small, Reliable Tool. https://innovatewithajay.com/articles/python-from-script-to-reliable-tool
