When I started, I thought getting good at this job meant knowing more APIs, more frameworks, more syntax. Five years and a stretch of enterprise, banking-sector, and freelance work later, almost none of the hard-won lessons were about syntax. They were about judgment — knowing when to be careful, when to move fast, and when the "correct" solution is actually the wrong one for the situation.
The bug that changes how you write code
Early in a banking-adjacent project, I shipped a change that looked completely correct in review — a currency formatting fix — and it quietly rounded a display value incorrectly for a specific locale. It never touched the actual transaction data, but it was still the kind of mistake that gets escalated fast in that industry. Nothing catastrophic happened, but the review process that followed changed how I write financial and numeric code permanently: assume the edge case exists until you've proven it doesn't, not the other way around.
Most bugs aren't technical — they're communication gaps
A large share of the "bugs" I've been asked to fix over the years turned out to be the application doing exactly what it was told to do, just not what anyone actually wanted. The fix wasn't in the code; it was in a conversation that should have happened during planning. This reshaped how I handle ambiguous tickets now — I ask clarifying questions before writing code far more often than I used to, even when it feels like it's slowing things down. It almost always saves time overall.
The best debugging skill isn't reading stack traces faster. It's asking "what did the user actually expect to happen?" before touching the code.
Consistency beats cleverness
I used to be proud of elegant, compact solutions. Working on a codebase with other engineers — and returning to my own code eight months later — taught me that a slightly more verbose, obviously-correct pattern almost always beats a clever one-liner. The clever version saves five minutes to write and costs everyone (including future you) twenty minutes to understand later.
A small example
// Clever, but makes the next reader stop and think
const total = items.reduce((a,b)=>a+b.qty*b.price,0);
// Obvious, and just as fast to write once you stop optimizing for brevity
function calculateOrderTotal(items: OrderItem[]) {
let total = 0;
for (const item of items) {
total += item.quantity * item.price;
}
return total;
}
Estimates are a negotiation, not a prediction
I don't think anyone gets good at estimating in the sense of predicting the future accurately — the work is too variable. What I did get better at is treating an estimate as the start of a conversation about scope, not a number carved in stone. "This will take two weeks if we cut X" is a far more useful sentence than a confident, unqualified "two weeks."
Ownership changes everything
The clearest before/after in my career was the shift from writing code someone else specified, to owning a feature or product end-to-end — as happened once I started taking on freelance clients directly. When you own the outcome, not just the ticket, you start asking different questions before you build anything: does this need to exist at all? Is there a simpler version that gets 80% of the value? That instinct doesn't show up on a resume, but it's probably the single most valuable thing I've picked up.
What I'd tell someone starting out
- Optimize for code that's easy to delete, not just easy to write.
- Ask the "obvious" clarifying question — it's rarely as obvious as it feels.
- Treat every production incident as free education, not just stress.
- Learn to say "I don't know yet, let me check" — it builds more trust than guessing.
None of this is groundbreaking advice. It's also not the kind of thing you can learn from documentation — it only sticks after you've lived through the version where you didn't know it yet.