An Engineering Philosophy
Principles I've converged on after building production software solo and watching them play out across multiple codebases.
These are some principles I’ve converged on after a few years of building production software, mostly solo, mostly with AI as a collaborator. They’re not in priority order, and they’re not exhaustive — they’re the ones that come up most often when I’m making a call about what to do next.
Code is debt
The less code you own that produces the same output, the better. Code is a maintenance liability, not an asset. Every line you write is a line someone has to read, refactor, test, port, and eventually delete. The right instinct on a new feature is not “how do I add this” but “how do I add this with the smallest possible footprint of new lines.”
Features are assets or debts — there is no in-between
Either a feature makes the company money or it makes the company spend money. If you can’t articulate which one your latest feature is, you’re guessing. The corollary: most features quietly cost more than they earn over their lifetime, and the discipline of regularly killing features is as important as the discipline of building them.
DRY is an iterative, endless process
Keeping a codebase DRY isn’t a one-time refactor. It’s a habit you practice every time you write something new and notice it looks like something you wrote three months ago. The duplication shows up the moment you stop looking for it.
Constantly challenge your own premises
Most engineering disasters start when an assumption hardens into a fact without anyone noticing. The discipline is to keep asking “is the thing I just assumed actually true?” — even, especially, about decisions that were yours.
Avoid the sunk-cost fallacy on tools
The mere fact that you already have a thing built with X is not a reason to keep building with X. The cost is everywhere in the future; the only honest question is “if I were starting today, would I pick X?” And if the answer is no, the next question is “what would it take to switch?” — not whether to.
Most feedback isn’t real
The only real feedback is from people who actually use what you’re building and care enough to tell you about it. Everything else — internal opinions, hypothetical user research, “I would never use this” — is noise. Build for the people who’ll tell you it’s broken.
The UI/UX is the product
You can have the most elegant backend in the world. If the user interface or experience is bad, the user will never know or care about your backend. The thing they touch is the thing they judge. Treat the interface as the product, because to the user, it is the product.
Think long-term, avoid quick fixes
“Quick fixes” almost always become permanent. The temporary CSS rule, the hacky script, the if-statement to handle “just this one case” — these stay in the codebase for years and accumulate. The honest move is to either fix it properly or document the debt explicitly and put it on the schedule.
Good software is intuitive
If your product needs a manual, you’ve already lost most of your users. The interface should answer “what do I do next” without anyone having to explain. This sets an enormously high bar, but it’s the right bar to aim at.
Care about the small details
The difference between a product that feels good and one that feels off is almost always the small things — the alignment, the loading state, the empty state, the keyboard shortcut that works in seven places but not the eighth. The user can’t articulate why one feels better, but they can tell.
Persist everything the user did
If the user did work, you should remember it. Their scroll position, their open tabs, their unsaved draft, their last selection. Anything they actively interacted with that you don’t preserve feels like a betrayal — a small one, but they accumulate. The default should be “everything persists” and you should have a specific reason for anything that doesn’t.
Good code is self-documenting
Comments age badly; code doesn’t lie. The variable names, function names, and structure should make the intent obvious. When you reach for a comment, ask first whether a rename would do the work. The best comments explain why, not what.