6 "Things-I-Wish-I-Knew" After 15 Years As A Programmer
1. Fundamentals are key
Everyone loves to try new and fancy technology and just start hacking. Even today I am still guilty of this. However, it's been proven over and over that learning fundamentals is far more productive. This applies both to general programming concepts and specific frameworks and libraries. If you understand how something works conceptually not only can you deduce how 80% of the thing works, but also fix issues far faster than desperately searching StackOverflow.
The thing about learning fundamentals is that it is ultimately boring. Often you have nothing to show for it for the longest time, and some concepts will be completely alien and useless to you. The important thing is to know what you don't know. All you need to do is remember where to go back to for information once you really need to. You are severely limiting yourself if you "don't know what you don't know".
Once you hit that "oh I know the perfect solution for this, I've read about it here and here!" there will be no further need to prove that fundamentals are key.
2. Know your own and your code's limits
Perfect code doesn't exist, just like perfect software doesn't exist. Software is never finished and neither is code. Therefore it's imperative to understand why your code is imperfect, how it could be better, and, most importantly, what the limits are. What can your code do really well and when does it perform poorly?
It is hard to predict how your code is going to be used in the future, but if you analyzed the corner cases it will be much easier to estimate how difficult future changes are going to be.
On the other hand don't be afraid to admin defeat when you don't know something well enough to implement in a reasonable time frame. When I was just starting out I tried building an interactive HTML tree for constructing a multi level menu of a website (that was before jQuery was a thing and IE had 90% browser share). It took me a week just to build the basic tree with drag-and-drop and one more week to realize the whole thing is way too brittle and will take months to bug fix. It would have save 2 weeks of my life to just admit that the task is too difficult and a simpler solution (such as limiting tree depth to 2 or 3) would be more appropriate.
3. Mature, respected libraries have bugs
Just recently I have encountered a bug in .Net tooling for web services that has existed for half a decade and is likely never going to be fixed. There are bugs in MySQL that still have not been fixed after more than a decade. Some bugs exist for 25 years before getting fixed.
These bugs are the most frustrating of all - you never suspect a mature library, you suspect your own code. These occurrences are not as frequent these days as 10 years ago, but they still happen and you should be ready for them. Sometimes the library doesn't work and you need to re-implement the same thing yourself (ideally make a pull request and fix the bug in the source).
Funnily enough, StackOverflow is littered with answers that recommend a library, when the original question originated because of bugs in said library. I have at least one such question.
4. Code quality is in the eyes of the beholder
No code is perfect. Some is really good, but perfection is too subjective. There is no point in blaming yourself or, worse, your colleagues for poor quality, unless you can produce a measurably better solution. And no, just because something looks better does not mean that it's a better solution.
Chasing perfection often leads to unnecessary abstractions that end up never being used by any other code, but the original use case it was designed for. Writing code that is easy to refactor later on is far more important. Follow a consistent style with your teammates, use abstractions already available in your framework of choice and stick to SOLID and in most cases that will be enough.
5. Complex subjects are easier than you think
We all learn "divide and conquer" as a strategy for solving programming tasks. This strategy works equally well for learning new things. A new tech or idea looks overwhelmingly complex? Split it into smaller chunks of unknowns. Make a list of small things that you need to learn first, before getting to bigger concepts. Each point should take no more than a day to learn.
These days it's a lot easier to do, with websites like Pluralsight, where content is broken down into streams, which are broken down into courses, which are further broken down into modules and chapters.
This might seem obvious - indeed educational content has been done like this since the beginning of time. However, it is too easy to overlook when you are educating yourself and not consuming a prepared course. Not every course or book is structured well, and they cannot know what your level of knowledge is, so be prepared to go on a search of your own - see point # 1.
6. Setup CI/CD first
Automated build process saves everyone so much time it should always be the first step in setting up a project. Even if it saves you just 5 minutes a day over a year it's about 3 working days saved every year. Reality is that not only it saves you 5 minutes doing the same thing over and over, but it also eliminates a lot of human errors and inevitable deployment bugs.
Automated deployment is even better, especially if you have multiple environments and automated verification process. OctopusDeploy is the best solution in this category. It's so good it should really be in its own category.
And don't forget that everything should be automated, including database deployment. Here my favorite solution is DbUp, it's quite specific to .Net world, but the general concept is amazing in its simplicity. I have tried several DB migration and change tracking tools over the years (DataGrip is one of the best for developers), but DbUp really nails the concept as it creates easy to follow, repeatable process that is completely transparent to everyone - developers, IT, DBAs.