Judging code quality is hard. Often, the quality function is aligned to short term goals, ignoring longer term ones.
If you're building a house, the quality function takes into account several factors. Does the the house stand up? Does it look good? Does it last? The foundations are important, the pipes, the wiring. These all tend to be carefully planned. When they are are not, you notice. Your client notices. The quality function reveals, very quickly, that it is not up to standard.
With code, many people miss key factors in their quality function. This needs to go far beyond "does it work". It's common to learn to (and rightly so) run before you walk. There's two issues here. The first: it's very easy to forget to invest time in learning how to walk, especially after you've been running for a while. Lessons from walking are some of the softer skills here; structuring your code neatly, avoiding code smells, refactoring, abstractions, debugging effectively. Also, several -able's: understandable, testable, and observable.
The second, is that a lot of issues can be ignored, usually until it's too late. Work can be reused, modified, with zero marginal cost. Technical details can be hidden, swept under the rug. This works in harmony with the first point, creating traps that sit and wait patiently for your project to grow larger, users to increase, features to come and go.
In a world that doesn't enforce strict rules, and that hides it's flaws, how do you even know what you're not doing well?
Going back differences between crafts, code has one enormous asset: open source. Tech has a huge community that gives open software away for free. This is a great but unusual quirk. What other industry is so heavily populated by open work, done by experienced engineers and shared freely and openly?
For anyone wanting to push themselves, leaving this advantage on the table is insane. Moving past the feeling of wanting to "run" all of the time, i.e. not only being able to answer "how" to build something, but also why something is a certain way (versus another), and who will be working on it. Combing through open source gives you access to new ways of thinking. It gives you language techniques, a window into how problems are solved, and an unrestricted view into every inner working of another machine.
Talking to other developers, the biggest gripe that comes up is the size and the complexity of a lot of open source projects. This tends to be a result of the obvious: there's likely been many people working on it for many hours, solving many problems, with many iterations over many years.
I'll tend to block out at least a half hour to sit with it. Read over it, and repeat as many times as needed. "Reading" a codebase is a misnomer. It's really learning a codebase, and should be treated thus. Pick small sections to consume. Read over it many times. Sleep on it. On a non-trivially sized codebase, it's not likely you're going to understand everything after a day. Give it a week, a month, before deciding you've bit off more than you can chew.
It also helps if you have context. A clearly marked problem that a codebase is supposed to solve is key. It helps put guide rails on your thinking whilst you're working through the codebase. Implement it in a branch of your app, spin up a test app implementing the code. Insert breakpoints or log messages at key points to understand the state as it works through the application. Anything that helps give you a clearer mental map of how the system is functioning throughout it's lifecycle is hugely helpful.
I could not recommend investing your time here more. Stick with it, take your time, note the details, apply them yourself, and give back.