I shipped my first paid website in 2001. I won't tell you what it looked like, because it's still archived somewhere and I have my pride. But it worked, it loaded on dial-up, and a small business in suburban Charlotte made money off it for the better part of a decade.
I've shipped a lot of things since then. Some of them mattered. Some of them I'm proud of. Some of them I would build very differently today. After twenty-five years, the things that turned out to be true are not the things I thought were going to be true at the start.
Here are the ones that held up.
The stack barely matters compared to the people
I came up in the era where the right answer to "what framework should I use" was a thirty-minute argument. I have lost more hours to those arguments than I care to think about.
What I know now is that a competent team using a boring framework will out-ship a brilliant team using a fashionable framework, every time. The stack is the smallest input. The team's ability to communicate, ship small, fix what breaks, and not lie to each other about progress — that's the whole game.
Pick a stack you can hire for, that has a real ecosystem, that does the obvious things obviously. Then never think about it again.
"We'll clean it up later" means "this is now permanent"
The single most reliable predictor of whether a piece of code will survive in production for five years is whether someone said "this is temporary" while writing it.
Cron jobs nobody documented. CSS hacks for "just this one launch." Hard-coded credentials "for testing." Schemas that "we'll migrate before the next release." All of it is still there, in production, ten years later, being inherited by someone who has no idea why.
Write the boring version the first time. The boring version is almost always good enough, and you can ship it without leaving a note.
Shipping is a different skill than building
Some of the best builders I know are not good shippers. They can write a beautiful module. They cannot decide when the module is done. They cannot push something out the door with rough edges and let real users tell them what to fix.
Shipping is its own discipline. It requires being okay with imperfection, being honest about what's actually a problem versus what's a craftsperson's discomfort, and being willing to put your name on something that you can already see how you'd improve.
I've watched more good projects die from over-polishing than from any technical problem. If you can ship something usable in two weeks that's 80% as good as the four-month version, ship the two-week version. You will learn more from one real user than from two more months in your own head.
You will rebuild it. Plan accordingly.
Anything that matters, you will rebuild at least once. Probably twice. Maybe three times if it really matters.
When I was younger I thought this was a failure. Now I think it's just how it works. The thing you build first is the way you discover what the thing should actually be. The second version is the thing.
Plan for it. Don't put off architectural changes forever, but don't pretend the first version is the last version either. Make it easy to rip out. Don't tangle the parts that change quickly with the parts that don't. Decouple where you can afford to decouple.
The codebases I'm most proud of are the ones I could throw away cleanly. The codebases I most regret are the ones where every part of the system knew about every other part.
The boring problems are the real problems
Authentication. Permissions. Background jobs. Idempotency. Migrations. Error reporting. Backups. Email deliverability. Date and time zones. Money math. File uploads. Logging.
Every one of these is unsexy. Every one of these is where systems actually fail. The interesting features get written quickly because they're interesting; the boring infrastructure gets written badly because nobody wanted to do it.
If you're new to this trade, the highest-leverage thing you can do is get good at the boring problems. Anyone can ship a CRUD form. The people who keep getting hired are the ones who can ship a CRUD form that handles edge cases, doesn't lose data, and doesn't bring down the rest of the system when it's under load.
Most "best practices" are local truths
The reason "best practices" are best practices is because somewhere, at some point, somebody made a costly mistake and wrote it down. That's valuable.
What's less valuable is treating those best practices as universal. The right architecture for a Fortune 500 with 200 engineers is not the right architecture for a three-person team. The right testing strategy for a payment system is not the right testing strategy for a marketing site. The right security posture for healthcare is overkill for a recipe blog.
I have watched a lot of small teams break themselves trying to do what Google does. Google has problems you do not have. Solve your problems with tools sized for your problems.
Saying "I don't know" is a senior move
The longer I do this, the more comfortable I am saying "I don't know yet, give me a day." Junior engineers think this makes you look incompetent. It does not. Lying about what you know does.
The senior people in this trade say "I don't know" more often than the junior ones do — and it's almost always followed by an honest plan to find out. That's it. That's the whole secret. Knowing the limits of what you know, being willing to say it out loud, and being willing to go figure it out is most of what makes a real engineer.
What I'm still figuring out
I don't know what AI does to this trade over the next ten years. I have opinions, I have working hypotheses, I have a practice that's currently weighted heavily toward AI integration work because that's where the demand is. But anyone who tells you they know how this plays out is selling something.
What I'm fairly sure of: the craft of understanding a real business problem, designing a system that fits, and shipping it with someone's name on it doesn't go away. The tools change. The work doesn't.
Twenty-five years in, that's the part I still like.
Got a project in mind?
If something here sounds like the work you need done, drop me a line.
Get in Touch