Let’s begin with what I perceive to be the difference between a junior engineer and their more experienced counterparts. The biggest difference between junior and senior/staff/lead engineers is the ability to understand a problem from its highest level, break the problem down into smaller pieces, solve the smaller pieces, and then, very importantly, put the smaller pieces together again in the right way to make the final product as efficient and robust as possible.
My view of how this works in the classic tech company is that the higher-level engineer will break down the business problem into smaller problems for more junior engineers to solve. Often, in bigger projects, the lead engineer will break the problem into still-large sub-problems that need to be further broken down. The senior engineer will break that sub-problem down and assign the smaller pieces to junior engineers. The lead and senior engineers work on ensuring that all of the pieces that they broke apart will fit nicely together, and the junior engineers build the bulk of the code.
Junior engineers learn from this experience by watching how their superiors break down these problems and put them back together again. This is particularly effective in in-person environments, and less-so in remote environments (another contribution to the disappearance of junior-level roles). It has been shown over and over that people learn more effectively in in-person environments both in school and out. In conversations that I have had outside of or adjacent to the tech world, this trend towards remote environments reflects issues that are faced by junior lawyers, financiers, and nearly every other industry. If a junior engineer wants to advance their career, they must learn to do the job of their superiors. However, traditionally, the main use case for a junior engineer is to implement the code that would take too much time for a higher-level engineer to build.