Remedial Coding: Never. Assume. Anything.
I have friends who are great programmers, technologists, and scientists because they have a really remarkable clarity of thought and methodicity that translates well into programming. Programming is easy for them because they naturally structure problems the way programs are written. I'm not one of those people. My talents are more intuitive than analytical. I tend to think out loud. But that kind of approach can get you into trouble when you're doing a kernel project with a 20-minute compile-test cycle.
Writing code is like playing Operation. Anyone can do it. (Ok, 4 and up due to the choking hazard.) The question is, how many times are you going to get electrocuted in the process? Core dumps, compilers, stack traces, debuggers and print statements will shock you every time you make a mistake -- just like that buzzer and red light. But eventually, you'll get all the little bones out of the patient -- it just might be ugly along the way.
Anyway, in the past, my "fly by the seat of your pants" approach has made my programming a little like playing Operation while riding in the back of a truck. The process is painful, the end result can really be a mess, and it takes 10 times longer than it should. I try to be halfway methodical, but that just ends up being a waste of time. So, I'm trying to replace these sloppy habits with useful, meaningful structure.
My first hard-earned lesson is this: don't assume anything. You know how they say, "Don't assume, it makes an ass out of you and me?" Well, when you're programming, and you assume things, it only makes an ass out of you. I do this all the time though. I'll be writing some code, and I'll see a function named "does_stuff()" that gets called, and I'll think to myself "Oh, I know what that does." And then I spend an hour debugging my code before I think, "I guess I better figure out what that little function does..." I call these things "grey boxes". They're like black boxes, except less scary. You think you understand them, which gives you a comforting yet false sense of understanding.
I know what you're thinking. You tell me, "But does_stuff() could be doing anything! How can I know what it actually does?" Well, I'd like to remind you that it is a PROGRAM executing on a COMPUTER. Chances are, its behavior is totally deterministic. You could take half a day peppering your code with print statements until it narrates its behavior to you in the Queen's English, or you could take 5 minutes to actually look at the function. You'll probably learn something useful. Yes, sometimes it's not worth going down every rabbit hole until you understand the context surrounding it... but at some point, you will need to know what it's doing. Otherwise, you're betting the correctness of your program on something you assumed to be true. And the more code you write around grey boxes, the harder it will be for you to figure out later. You might even forget that you assumed things to begin with!
Assuming things is more pernicious than simply guessing about what does_stuff() does. It's not just functions that abstract away behavior. Function pointers, the meaning of variables, macros, goto labels, structures, methods -- in short anything that is not explicit or obvious -- could be playing games with your head. Functions snicker at programmers who naively assume things about their behavior. So do yourself a favor. Any time you hear the voice in your head say "I think that does/means X..." take 5 minutes and figure out what it ACTUALLY does. You'll thank yourself later.