The biggest thing I've learned is how software changes over time. Previously I thought of a good program as like a crystal. Write it correctly the first time, and it doesn't need to change much, if at all. Sure you might want to add features or fix bugs, but otherwise you write it once and it's good forever, right?
Most programs are closer to organisms than crystals. They're adapted to specific niche, and when the world changes around them they need to either adapt or die. This is the biggest gap I notice between senior devs and strong junior devs. Junior devs have mostly built things, released them or turned them in for credit, and then moved on. It's rare to find a junior dev that has maintained an app for the long haul, and it accounts for so much of the advice that I found strange when I was junior:
* code is read many more times than it is written
* be very mindful of your dependencies, if push comes to shove you could end up owning them
* commenting why is often more important than commenting how. it's good to comment about both, but it's much more important to comment why. the source code explains how, but it has nothing about why
* it's ok to love your code, but delete it the first chance you get. it will save you and the people around you a lot of time
Do you think I'm overselling things? Maybe that's a problem for people who write code in $cursed_language on $bad_platform but your tools favor writing True and Long Lasting Solutions That Stand the Test of Time!
Let me give a handful of real world examples:
* A major security vulnerability is found in an API that previously seemed safe. Refactor your code not to use it.
* Turning on a certain compiler optimization breaks a pattern common in your code. After some experimentation, you discover an ugly fix that you then need to apply to everywhere in the code that uses that pattern.
* A compiler/browser/hardware update adds support for a feature that your code has been using a bunch of ugly workarounds to cope without. Do you use the new feature? Do you still support running without the feature? Is it a breaking change to users downstream of your code? How much does it improve things, how hard will it be for your users to update, and how many users do you have?
* You need to migrate to the new version of one of your dependencies. Maybe this is just a version bump, maybe you end up needing to review every single line of your app.
* Your app for phones and desktops is working great! We want to run it on TVs as well! This means a brand new (and super weird) matrix of features that are and aren't supported, from hardware to OS to UI.
In short, the only constant is change. Write your code so that you can go away for six months and forget just about everything about it and still be able to maintain it. Lots of tests, document why decisions were made, and it's like Bruce Lee said, be like water.