For a different point of view on things, do not focus on writing clean code in front.
Enter the code that first solves the problem, then go back and clear it. The only caveat is not too long without returning to clean it. And having cleared it, I want to go back and implement the SOLID principles only for abstractions that determine the intent of your system.
Most of the code that I read, from professionals or not, you can pretty much tell when they tried to write perfect code on the first pass, because they always do not, and they never come back to clear it. Actual clean code is the result of repeating drafts of a block of code that only the original writer can understand, until it becomes clear to anyone familiar with the patterns used.
Premature optimization is trying to write perfect code up.
First, you get a ton of abstractions that you really don't need to satisfy some dogmas about supported code. Often you will spend so much time on the structure of the solution that the actual problem you are trying to solve is lost in translation. In fact, the smaller the number of abstractions, the easier it is to understand the primary responsibility for a piece of code. Itโs best to build abstractions only at the highest level, and only build more when absolutely necessary.
Secondly, you often develop incorrect abstractions based on the parameters of the current requirements, than if you knew exactly how the requirements would change in the future.
I have a great example from a project looking for candidates for a career. Initially, our requirements were to load search results and candy data (statistical maps, etc.) for a set of results. We implemented this all using sql and outlined the process of downloading sweets and receiving search results in different services. After a couple of years, we decided to try all this using elastic search. As it turned out, our idea of โโseparating the generation of chocolates from search results is not needed using elastic search, because it can do both at the same time.
What happened to us was that we were zealous in the fact that classes are responsible for one thing. In fact, we had different services for generating sql for searching and saving search parameters in the database. When we implemented this using elastic search, the parameter save service came in handy, but still the unnecessary work was done. In fact, most of the extra time that we set ourselves for maintainability was largely a waste, because we had to redo the kernel abstraction error to work with the new requirements.
If we could do all this, we would instead write all the code in one giant search service. During our project, we would break down each of these other services that we implemented into the simple private functions of this service. When we created the second search implementation in 2 years, all we would have to do to break up the common services is copy private functions to a new service and publish common functions to the new service. This is a much more pragmatic approach than trying to write the โperfectโ code out of the gate.
Do not get me wrong, clean code will make your code more elegant and maintainable; you just can't record it on the first pass. You cannot really understand what makes code clean just by reading a book or looking at the source code that people think is clean. In addition to this material, it takes a lot of time for errors like those described above to realize the full intent of the principles that make up pure code. Do not be afraid to make these mistakes, they will make you a better programmer. Fearing mistakes, this is an intolerable opinion that makes you a worse programmer.