Two years in, some lessons I keep re-learning.
The infrastructure trap
My worst habit: when a deadline gets scary, I add infrastructure instead of shipping. A new config system, a better folder structure, a task runner — anything that feels like progress but defers the first real step. Naming the pattern helps. If I'm building scaffolding the week something is due, that's not engineering, that's hiding.
Scope is the product
Every project that shipped on time was one where I cut scope early and publicly. Every project that dragged was one where I kept the full scope in my head and quietly slipped. The feature you cut is not a failure; it's the reason the rest exists.
Boring beats clever
The clever solution is the one you decode at 3am six months later. The boring solution is the one that was never in the incident channel to begin with. I now treat "I can't wait to show someone this code" as a warning sign.
Ship the ugly version
The prototype with hardcoded values that real users touched taught me more than the perfect architecture that never left my machine. Feedback compounds; polish doesn't.
What's next
Deeper systems work — networking, signal processing, the stuff under the abstractions. And answering the only question that matters: what do you want to create?