Development Philosophy

We treat a repository as data. Its structure and rules are written down, checked and used to generate code, so people and AI agents work from facts that have been verified rather than from habit. The principles below follow from that, and from one fact about cost.

Code is cheap; maintenance is expensive

Writing code costs little and maintaining it costs a lot, so we judge a choice by what it leaves us to look after. Anything that costs more to keep than it is worth is deleted, however much work went into it. This matters more when an AI writes the code: writing becomes nearly free, and code that nobody fully understands becomes harder to maintain.

Dependencies are obligations

Third-party code is code we do not control: it can have security holes, it can break, and it has to be upgraded on someone else's schedule. Before adding a dependency we ask whether we would be willing to maintain it in five years. Usually we build the part we need ourselves, and it is smaller than it looked.

We rely on open standards, not on one company's product. Git, HTTP, containers and SQL have several implementations and nobody can take them away, so we use them directly. A product from a single vendor is kept behind an interface, so that we can replace it with another.

An abstraction has to earn its place

We keep our code direct. We add a layer in two cases only: to wrap another company's product so that it can be replaced, or when we already have several kinds of the same thing, such as several programming languages to support. We do not add one because it looks tidy, or because we might need it one day.

Declare before you code

Before writing code by hand, we ask whether a declaration would do: a schema, a rule or a configuration entry. A declaration can be checked automatically and used to generate code. Generated files are never edited by hand, and generating them again gives the same result, so we can check that they are up to date.

A failing check is information

A failing check tells us something is wrong with how the code is organised. We fix that cause, and we do not weaken the check to get past it. If the check itself is wrong, we change it on purpose and write down why.

Names are kept accurate

When a name no longer matches what a thing does, we rename it straight away, everywhere it is used. We do not keep the old name working beside the new one, because a misleading name leads to misleading design.

One owner, one writer

Every file and every piece of data has exactly one owner. Only the owner changes it; everyone else reads it, and makes changes by asking the owner. There is never a second copy to disagree with the first.

Nothing general without a user

We add a capability only when something needs it, and the two ship in the same change. A capability that nothing uses is removed. Our tools are built with our own tools, so each capability is in use from the day it exists.

The record keeps pace with the work

Documentation is updated in the same change as the thing it describes. Decisions, and work we chose to leave for later, are written down where the next person will look. Nothing important lives only in someone's memory.