Maintainability
Software does not wear out, but it needs constant care.
Maintenance is the main cost
Requirements change, the platform under the code changes, and bugs need fixing. So most of software's cost comes after launch. Old (legacy) systems are hard: the tech is dated, and the people who knew why it was built that way have left. Any system that is useful for long enough becomes legacy. So design for three things: operability, simplicity, and evolvability.
Operability features
Operability means the team can keep the system running smoothly. Good operations can make up for weak software, but not the other way round. A data system helps with metrics to monitor, no outage when one machine is taken down, clear docs ("if I do X, Y happens"), defaults you can override, self-healing with manual control, and few surprises.
Automation cuts both ways
With thousands of machines, doing everything by hand is too costly. But automation leaves the rare, odd cases to people, and those are the hardest ones. So more automation needs a more skilled team, and a broken automated system is harder to debug. The right amount depends on your app and team.
Simplicity and abstraction
Big projects tend to become a "big ball of mud", which slows everyone and hides bugs. What counts as simple is partly a matter of taste. Some split complexity into essential (from the problem) and accidental (from our tools), but that line moves as tools improve. Abstraction is a strong tool: SQL hides storage layouts, other clients, and crash recovery behind one clean interface, and every fix helps all its users. Apps build their own abstractions with design patterns and domain-driven design, on top of general ones like transactions and indexes.
Evolvability and irreversibility
Needs keep changing: new laws, features, platforms, growth. Simple, loosely coupled systems are easier to change. Steps you cannot undo, like a database move with no way back, raise the stakes. Keep a way back when you can.
Remember
- Most cost is maintenance, not the first build.
- Make routine operations easy and behavior predictable.
- More automation is not always better.
- Good abstractions hide detail and get reused.
- Avoid steps you cannot undo.