My divide between dead text files and living code
Have you ever really stopped to think about the difference between a configuration file and a program? It's a little bit like the difference between a cow grazing in a field and a burger (or a carrot in the ground vs. one on your plate, if that's how you roll). One of them is an animal or plant, and the other is food. It has to cross a certain divide to get there.
I used to have this big invisible divide in my internal understanding of how things worked on my computers. There were config files which looked like this:
BN=My Test BBS PH=408-555-1234 D1=tw2002 D2=usurper D3=lord
Meanwhile, there were programs which generally looked like this:
program logthingy; var log: text; begin assign(log, 'test.log'); append(log); writeln(log, 'some event happened at 12:34:56 01-02-91'); close(log); end.
I knew that I could hand a properly-formatted program to a compiler and it would give me a nice .EXE (DOS), or later a nice a.out or ELF binary (Linux). What I couldn't figure out is how to do things like that myself.
This meant my programs were relatively limited in what they could do. Even the ones which needed to have some kind of "programmability" really weren't that flexible. The most they could do was start at the top of a config file and process the directives in order. You couldn't do any sort of loops, subroutine calls, or jumps, but there was one even bigger stumbling block I had beyond all of that. I couldn't wrap my head around variables.
My reasoning at the time was something like this: in code, when you say "A = 1" in a program, that actually goes off and does something. A config file, however, can't just go and do that. The program is already "baked" and you can't just make up variables on the spot. The config file can't "reach into" the realm of the programming language to create "real" variables, in other words. The concept of "eval" didn't exist in the languages I had been using, so that option was out, too.
I simply had no idea that you could make up your own "bookkeeping" to track such things. I could have used some kind of data structure to say "okay, I have a new numeric type, and it'll be called A, and will now be set equal to 1", but I didn't. That kind of behavior in a program just didn't occur to me.
Even if I had gotten that far, the notion of turning around and using a variable as part of another statement would have been yet another stumbling block. Imagine trying to handle conditionals like "if" when mere variable assignment might as well be magic to you. It just wouldn't happen.
This was the case for longer than I'd like to admit, but it changed. I happened to take a compilers class as an elective, and it introduced me to a whole bunch of interesting new ways to look at problems. I didn't realize it at the time, but it was actually showing me how I could deal with things like variables, conditionals, and subroutine calls. When it was all over, I discovered it had effectively patched over that hole in my understanding. I could now see a way from one to the other.
The thing is, if someone had come up to me early on and insisted that I learn about scanners, parsers, tokens, lexers, symbol tables, immediate code and interpreters all by themselves, I wouldn't have gotten anywhere with it. It took a complete class with a clear goal of creating a compiler which did something useful to grab my interest. Encountering the individual parts as part of some huge checklist would not have worked for me.
Show me a problem first (like how to breathe life into a "dead" text file) and encountering a solution (like compiling it) will then seem useful. Otherwise, it's just yet another thing which happens to exist.