Eventually, you can sort of just think in code. You have an idea, and sometimes, if you’re lucky, you can just write the dang thing down right away. Often it’s not quite that simple, but, it’s closer to that than explicitly reasoning through what will work and what won’t.
But you don’t start there. You start with concepts you understand so loosely you really aren’t entirely sure how they work together. This is not a result of you being bad at the thing. Programming is absolutely complex enough you just can’t get it all down intuitively right away. Don’t even bother trying. Fixed mindsets need not apply.
When you’re having trouble understanding things, replace notation with its definition. In other words, take the expression that the notation is equivalent to, and rewrite the thing that in terms of simpler expressions, that don’t use the notation.
This is an incredibly powerful technique, and there’s a few twists on it you’ll probably learn to use over time. This is most useful for getting a really deep understanding of how the new thing you’re learning interacts with the old things you know. It’s also useful in situations where there’s just too much going on, and you haven’t quite internalized the new abstraction yet. In fact, it actively helps you internalize the new abstraction.
the trick, applied to code
It’s a bit harder to do this with code, but it’s still possible! For example, take a look at this list comprehension in python. It makes the list of all three tuples without duplicates where each position takes values in the between 0–9 (inclusive). But what order does it do them in? How do we know?
Well, we can take a look at the definition, where we’ll find that:
The comprehension consists of a single expression followed by at least one for clause and zero or more for or if clauses. In this case, the elements of the new container are those that would be produced by considering each of the for or if clauses a block, nesting from left to right, and evaluating the expression to produce an element each time the innermost block is reached.
Okay, so, trying to transform the first version into a simpler version that’s still in python, we get:
This is much simpler to understand! It’s not better, but it is simpler to understand when on its own. The list comprehension is better at expressing the intent, and fading back out of the way when there’s a larger, more important story to tell.
Anyway, the point is: rewrite your program in terms you already understand if you’re having trouble with something. Figure out why it doesn’t work the way you think it should, and then translate it back into the version you get.
trickier takes on the trick
If you generally understand how a thing works, but are running into some weird edge case, try thinking through what’s happening in terms of definitions. You may or may not find it useful to rewrite the whole thing in terms of definitions, but at least think in terms of them.
Try writing your code so that, from the get go, it embodies the definition of the thing you’re trying to do. This is like, the zen of programming, or some other bullshit metaphor you like. Pick one. It’s sometimes called denotational design (there are a few talks around by Conal Elliot on this, and I can’t recommend them highly enough, if you’re up for some haskell/category theory). You don’t need to follow all the fancy math shit behind that to get a lot of the benefit of this though. Just try to make your code literally implement the definition of the thing. If you can manage to pretty much stick to that, it makes things beautifully simple.