From Private Script to Public Utility - Arthur Dick

Saturday, July 5th, 2025

Every developer has one: a collection of scripts, tucked away in a ~/bin directory, written for an audience of one. They’re often messy, beautifully pragmatic, and tailored perfectly to a personal workflow. My command-line task manager, which I’ve used for years, was exactly that. It was my digital garden shed, functional and familiar, but not exactly ready for guests.

Recently, I decided to clean it up for a public release. What started as a simple tidying exercise quickly evolved into a complete architectural overhaul. It was a journey of refactoring, future-proofing, and adding the polish required to turn a personal hack into a tool that others might find genuinely useful. I’m calling it Perennial Task, because like the wood lilies that reliably return to our Alberta prairies each summer, some tasks are perennial—they need to be tended to, year after year.

This is the story of that process.

The Starting Point: A Collection of Scripts

Initially, I had a handful of separate PHP scripts: create.php, edit.php, complete.php, and so on. They all knew how to find their tasks/ subdirectory because the path was hardcoded. They each had their own validation logic, their own file-handling code, and their own definitions for constants.

This works perfectly fine when you’re the sole user and you know the system's quirks. But for a public release, it’s a maintenance nightmare. A change to a file path or a validation rule meant editing four or five different files. This was the first and most obvious problem to solve.

Step 1: Centralization with common.php

The first rule of cleaning up code is "Don't Repeat Yourself" (DRY). I created a common.php file to act as a shared library for the entire application. Into this file went all the duplicated logic:

Each script was then refactored to simply require_once 'common.php'; and call these shared functions. Immediately, the individual scripts became shorter, more focused, and infinitely easier to maintain.

Step 2: From Fragile Paths to a Configuration File

The biggest bug in the early version was the hardcoded paths. The scripts assumed they lived in a directory with a tasks/ subdirectory next to them. This breaks the moment you try to install it as a system-wide command.

The solution was to introduce a proper configuration system. An installer script now creates a user-specific configuration file at ~/.config/perennial-task/config.ini. This file stores the absolute paths to the user's tasks directory and their completions log.

A new config.php script was written with one job: find and parse that .ini file and define the paths as global constants. The common.php file, in turn, includes this config loader. Now, every part of the application knows exactly where to find the user's data, no matter where the command is run from.

Step 3: Future-Proofing with XSD Validation

The early scripts used a DTD (Document Type Definition) to define the structure of the task XML files. This is a bit old-school. DTDs are great for simple structural validation, but they have a major weakness: they can't validate data types. A <duration> tag could contain "seven" instead of "7", and the DTD wouldn't care.

Switching to an XSD (XML Schema Definition) was a huge leap forward in data integrity. I authored a task.xsd file that not only defines the structure but also enforces strong data types:

The common.php library was updated to validate task files against this schema. This instantly made the application more robust by catching data errors at the source, long before the PHP logic ever sees them.

Step 4: The User Experience Polish

With a solid architecture in place, I could focus on the user experience. A tool can be powerful, but if it's awkward to use, it won't be adopted.

From My Garden to Yours

What began as a personal utility has completed its journey. It's been refactored, documented, and packaged, ready for others to use. The process was a powerful reminder that the principles of good software architecture—centralization, configuration, and robust validation—aren't just for large-scale enterprise applications. They can transform even the humblest of personal scripts into something durable and shareable.

This is the story of Perennial Task. I hope it might inspire you to look into your own collection of scripts and see if there's a wood lily in there, waiting for a little tending before it's ready to be shared with the world.

Tags: software development

← Cross-Origin Resource SharingBeyond the To-Do List →