The last five pieces were about constraints that come from where we are. This one is about the constraint that comes from how many of us there are, which turns out to shape the software more than anything else in the series.
A very small team can now ship and operate what used to need a department. It still cannot ship everything, and the interesting question is what you give up on purpose.
Choose boring on purpose
Every technology choice has a maintenance cost that arrives later, usually at the worst time. With a small team the right question is not “what is best” but “what will still be fine in three years when I have forgotten how it works”.
That leads to some unfashionable preferences:
- Static output over a server runtime wherever the content allows it. No runtime is the most reliable runtime.
- Managed services over things we would have to operate. We do not want to be in the business of patching a database host.
- A small number of dependencies, chosen for their maintenance record rather than their feature list. Every package is a future upgrade, a future advisory and a future breaking change.
- One language and one framework across as much as possible, so context switching costs less than the variety would gain.
- Plain files in version control instead of a content system, for anything that changes less than daily. This site’s articles are Markdown next to the code that renders them, and a change goes through the same review as a code change.
The test we apply: could one person, on a bad day, understand this well enough to fix it? If not, it is too clever for the size of the company.
The pipeline is the team’s second member
Automation is what makes a small team viable, and it is worth building before you feel like you need it.
Every change to this site goes through the same path: a branch, a pull request, and a pipeline that checks formatting, runs type checking, builds the site, and then runs a post-build check over the output for broken internal links and missing metadata. That last one exists because a dead link is invisible until a customer finds it, and because we have written one.
On merge, the same pipeline deploys, invalidates the cache, and smoke tests the live URLs. Infrastructure is defined in templates and applied by the same automation, so there is no console-clicking step that only one person remembers.
Three properties matter more than the tooling:
The pipeline authenticates without long-lived keys. Federated identity issues short-lived credentials to the workflow, so there is no cloud access key sitting in a repository secret waiting to leak.
The same checks run locally. One command runs exactly what continuous integration runs. If the local command passes, the pipeline passes, and nobody learns about a formatting failure eight minutes after pushing.
A failure blocks the deploy. A check that can be ignored is a check that will be ignored, and then it is documentation rather than a guarantee.
Where AI assistance actually helps
Coding assistants are part of how this work gets done now, and the honest account is more mixed than either the marketing or the backlash suggests.
Where the gain is real and large:
- Unfamiliar territory. A cloud template, a configuration format, an API you use twice a year. It collapses the hour spent reading documentation into a draft you can check.
- Mechanical transformation. Restructuring a file, converting a data shape, writing the tedious half of a test suite, renaming a concept across a codebase.
- First drafts of anything structured. Scaffolding, boilerplate, migrations, a starting point for a document.
- Review as a second pair of eyes. Asking what breaks in a diff catches the thing you stopped seeing an hour ago.
- Explaining code you inherited, including code you wrote and forgot.
Where it does not help, and where we have been burned:
- Anything load-bearing and specific. A confidently wrong function that compiles is more expensive than no function at all, because it costs the review and the debugging.
- Local legal and regulatory detail. Ask a general model about Australian consumer law or the Privacy Act and you will get plausible text with the wrong jurisdiction’s assumptions baked in. Every claim in part five was checked against the source.
- Architecture with a long shadow. Assistants optimise for the code in front of them, not for the thing you will regret in two years.
- Security-sensitive code. Auth, session handling, anything that touches money. Assistance is fine; unreviewed output is not.
The rule we have settled on is simple: the assistant can produce anything, and a human owns everything that merges. The bottleneck was never typing. It is comprehension, and you cannot outsource the part where somebody has to understand the system when it breaks at 2am.
Operations when there is no on-call roster
Two people cannot run a twenty-four hour roster, and pretending otherwise produces a plan that fails at the first incident.
What we do instead:
- Reduce the surface that can page you. The static site cannot go down in an interesting way. That is a feature.
- Alert on symptoms, not causes. A handful of alerts that mean “customers are affected” beats fifty that mean “a number moved”.
- Make every alert actionable. An alert nobody acts on trains you to ignore alerts, including the one that mattered.
- Degrade rather than fail. A product that keeps working offline, or falls back to a cached answer, turns a 3am outage into a morning fix.
- Write the incident down afterwards. Not a ceremony. A paragraph on what happened and what changed, so the same thing does not surprise you twice.
What we deliberately do not do
The list of things a small company leaves out is part of the strategy, not a gap in it.
We do not run a staging environment that mirrors production. We do not build a microservice boundary before there is a team on each side of it. We do not maintain a design system for two products. We do not take on client work. We do not chase a feature because a competitor shipped it. We do not launch a third product while two need attention.
Every one of those is a defensible thing to do at a different size. At this size, each would consume the capacity that currently goes into supporting customers and fixing what is broken.
The point of all of it
Six pieces on building software from Australia come down to one idea: the constraints here are specific, and specific constraints are easier to design for than vague ambition.
Distance says put things close to people and stop making them wait. A small market says be profitable at a scale you can reach. Australian law says collect less, keep it where you said, and let people leave. A small team says choose things you can still understand later.
None of that is a limitation to apologise for. It is a fairly good specification for software that is still here in ten years, which is the only kind we are interested in building.
That is the end of the series. If you want the products it came from, they are BubSync and My Energy Agent. If you want to argue with any of it, we read every email.