When you are programming, you can have two types of understanding about what the code is doing. The first is the understanding in your head. This is fragile and temporary, residing in your short-term memory. You read code, you understand, but you forget about it a few days later. When you come back to the same code after some time, you have to again make an effort to understand the code.

The second type is the long term understanding, and it's not in your head. You persist the first type of understanding by moving it from your head to the codebase itself. By doing this, the code tells you what it's doing, and you don't have to figure it out again.

The way you do this is by extracting that piece of code into a new function and give it an intention revealing name. A name that tells you what it's doing. You can always go to that function to see how it's doing it, but you don't have to. You just abstracted that code to something simpler. The next time you are reading the code, you just need to glance at the name to understand what it's doing. 

Put everything you should remember back into the code, so you don't have to remember it. 
For example, consider this piece of code from Martin Fowler's Refactoring. The code on the left does everything in a single method. As you can see, it's complicated, and needs comments in some places to explain what it's doing. The code on the right does exactly the same thing, but has well-named functions abstracting the tasks they are doing. It also needs fewer comments to explain its purpose.
Usually, after you move your understanding of the program into the code itself, the code becomes clearer to read and easy to understand and hence to modify. That in turn leads to deeper understanding and makes way for better refactoring to improve the structure and performance.