I think about software as toolmaking because that is the comparison that keeps working.
Stone tools are millions of years old. The first toolmakers did not need a formal theory of engineering. They had a problem, a material, and a repeatable method. The evidence is the tool itself. They struck stone in a way that produced an edge, and they did it well enough for the result to be useful.
That is the basic structure of toolmaking. A person studies a material, changes it, checks the result, and changes it again. If the person does not understand the material, the tool comes out wrong.
With flint-knapping, the material is stone. It has grain, thickness, platforms, ridges, angles, and fracture patterns. The stone does not become whatever I want it to become. It breaks according to its structure.
Software also has structure. A codebase has state, dependencies, permissions, latency, naming conventions, old shortcuts, weird edge cases, and parts of the system that nobody wants to touch because too many other things depend on them. These are not details around the work. These are the material.
A beginner often acts on the version of the system that exists in his head. He knows what he wants the program to do, so he edits the code. Then the result surprises him. The surprise usually means he did not understand the actual system.
A tool is not better because it can do everything. A knife is useful because it has a specific shape. It has an edge. It has hardness. It has a grip. It has limits. If those limits disappear, the knife does not become more advanced. It becomes less useful.
Software works the same way. A program becomes useful when it has a definite shape. It accepts certain inputs and rejects others. It supports some workflows and refuses others. It makes tradeoffs instead of pretending that every use case can fit.
I trust software more when it has clear edges. If an input is invalid, the program should reject it. If a state should not exist, the system should prevent it or expose it clearly. If an action is unsupported, the interface should not imply that it is supported. Software that accepts everything usually moves the confusion somewhere else.
Failure shows where the real shape of the system differs from the imagined one. When a flake breaks in the wrong direction, it shows that I misunderstood the platform, the force, or the stone. When software fails, it gives similar evidence. A bug report can show that the interface communicated the wrong thing. A production incident can show that a dependency was more important than people thought. A user can do something that the programmer considered impossible, which usually means the programmer did not define impossible carefully enough.
Those failures are useful because they are specific. They show the part of the system that needs to be understood better.
That is the part of engineering I like. I like when the system becomes legible. I like when the constraints become clear. I like when a tool becomes sharper because I understand what it should not do.
The first toolmakers had stone. I have state, code, and machines that execute unclear thinking exactly as written.
Next