Long answer incoming:

I've always maintained that you really learn how to write code for a particular problem when you've done it three times. The first is usually to get working code, the second is to redesign it to improve some aspect (adding features, improving performance, code maintainability, etc.), and the third is when you really get it "right". Using this approach, it takes time and deliberate practice (borrowing the term from Geoff Colvin's "Talent is Overrated").

From my point of view, the core question that you're asking is "How do I get deliberate practice with coding?" There is no easy answer for this, but here are some ways to break things down so that you can come up with a plan for yourself.

1) "Clean Code"

This was brought up in another comment, but the book does an excellent job of covering many of the techniques for keeping a code base in good shape over time.

What can be trickier is 1) there are a lot of techniques, and 2) you may have a hard time taking the techniques from the book and applying them to your situation. While the examples in the book are universal, I think it's easy to get lost applying the technique to a more complex problem.

2) Applying One Technique to a Known Situation

Either start with a smaller piece of code or a medium piece of code that you know really well. Ideally, use some advanced piece of source code control that allows multiple branches. Then, start doing the practices from "Clean Code" one a time with a goal in mind, such as eliminated duplicated code, limiting scope, making functions more configurable via parameters, etc.

The real trick? Just do one of these improvement areas at a time. Do it a few times in different parts of the code. Eventually, the patterns start to emerge as to why it improves and where else it can be applied.

3) Recognizing Patterns

You may or may not have read articles/books about API design, coding style guidelines, etc. While many of those rules may seem arbitrary, as you get more practice, you'll start to see why such guidelines exist.

There will be similar patterns for how your code refactoring can improve modularity, debuggability, development time, testing, error handling, etc. Each code change may not improve all those things simultaneously, but you'll eventually start making improvements that cover more than one area of improvement.

4) Improving Your Learning Acceleration

There's a point you'll reach where you can keep refactoring, but you may not learn much more. At that point, try adding a feature. It can be a minor feature or a major one, but you'll start to see where the new feature forces your code to be refactored again. You can write a bunch of code or you can go through the "what do I need to change in the existing code base to add feature X?" as a mental exercise. In either case, you'll start to see what prior decisions may not have worked since they only considered one aspect of code improvement instead of three or four.

5) Putting All of the Above into Context

Another aspect of being a "Senior" engineer means that you recognize the problems really being solved by your software and what value it has. As you get more senior, you can answer all sorts of questions such as:

- How does the customer use this software? What pieces do they pay for? What features save them the most time/money/suffering?
- What should the next features be?
- What parts can be improved? Speed? Usability? Data Input? Connectivity/compatibility with other software?
- What sort of documentation does the user need?
- Is this software getting dated? What parts need replacing? Does there need to be a new solution (rewrite vs. continued refactoring)?

Not all of the above questions are applicable (e.g. tools used inside a company vs. a tool customers buy and use on their own computers), but being able to put the software usage into context (sort of the PM area you were talking about) and breaking down the technical context (the "senior" engineer role) are both useful skills.

You may need to go back and forth a bit between coding and reading books like "Clean Code" before the lessons really get internalized. Using the above guidelines and knowing what sort of learning process works best for yourself, hopefully you can start taking steps to improving in ways that help your career.