articlesabout

Programming might not be about programming

A few of the earliest students I mentored at Bloc.io struggled with the "Developer Mindset".

In particular, they needed help scanning and understanding code, sometimes even chunks explicitly covered in the course. They were missing the inner logic of a program, and as frustration built up, the less combative amongst them would start falling behind.

I'm convinced that anyone can learn just about anything given a sufficient amount of time and effort, and most importantly, the proper environment, so it got me thinking about what exactly was preventing those students from succeeding in learning how to code and how I could best help them overcome that overwhelming feeling.

What I've come to realize over time is that all my struggling students had a common weak spot, one unrelated to programming.

First Sight & Second Thoughts

One ability that is very useful for programming but is certainly not limited to it is metacognition.

Metacognition is often defined as the act of "thinking about thinking". The concept is helpful to make a distinction between cognition, the act of knowing or learning, and what is about cognition, for instance, assessing what would be the best strategy to learn something. Cognitive skills are necessary to perform a task, while metacognition is necessary to understand how the task was or should be performed. It's clear already that some degree of metacognition is required to be efficient with cognitive tasks. For example, one may copy-paste a program from StackOverflow and get a general feeling of how it functions. Still, to customize the program or use it as boilerplate code, one needs to understand something more than the program itself.

The above may sound obvious, but I had to accept that metacognition in practice is far from spontaneous behavior. For instance, truly understanding a program is a challenging task for many newcomers, and when you look at what "truly" means in that context, it's expected it would be. It means that nothing feels magical, for each statement of the program is conceptually under control and makes sense as a purposeful technicality part of a bigger picture, the very program — the latter eventually making sense as a whole as well. Building that level of awareness is a skill on its own! One metacognition can tremendously help with this. As a mentor, though, I had to empathize more than I was used to, to understand why it was so hard for some students to seize on metacognitive skills. For one thing, many students first had to take cognizance of metacognition itself!

It's one of the reasons why I started speaking explicitly about metacognition with my students. But instead of describing it as "thoughts about thoughts", I prefer to speak of a process, that of "building self-awareness". It's a twofold process, for deep comprehension is orthogonal. Let me explain what I mean by that with an idiom by Terry Pratchett:

First Sight and Second Thoughts, that’s what a witch had to rely on: First Sight to see what’s really there, and Second Thoughts to watch the First Thoughts to check that they were thinking right.Terry Pratchett, The Wee Free Men

First Sight is all about untainted perception. So when presented with a bunch of code lines, it would mean reading those lines as they are and not as you wish they were, avoiding skipping or mentally editing characters out of inattention or because it's "easier" (the latter would be Second Sight, "wishful perception" if you want).

Second Thoughts acts as an insurance policy or a fact checker. By limiting yourself to the implied First Thoughts, you would understand what the code lines you carefully read do, but would remain unable to assess whether they are efficiently solving a problem, or solving a problem at all for that matter.

First Sight is when you can see what’s really there, not what your heid tells you ought to be there. Ye saw Jenny, ye saw the horseman, ye saw them as real thingies. Second sight is dull sight, it’s seeing only what you expect to see. […] Thoughts are the everyday thoughts. Everyone has those. Second Thoughts are the thoughts you think about the way you think. People who enjoy thinking have those.Terry Pratchett, The Wee Free Men

Many if not most individuals live a credulous life, fed by Second Sight and First Thoughts: they see what they want to see, they can only ever think what fits those biases. I know I do… from time to time! Problems arise when someone makes a habit of that, consciously or not, or when someone relies on that "strategy" to cope with difficulties, like… learning a challenging skill, say, programming.

When I said that deep comprehension is an orthogonal dual process, I meant that you first have to reach out to reality (leveraging First Sight & First Thoughts) and then, must cross-check that perception to dismiss any self-imposed misconception you might not be aware of (using Second Thoughts vs Second Sight). It's quite demanding for some people to do that, but for it's a skill, it can be trained, and the more you practice it, the easier it gets and the more sturdy it becomes as a learning tool.

A strategy for code analysis: Double Top-Down

Amongst the different "tricks" I share with students, there is one I especially like: it recommends scanning new code twice before doing anything bold. In order, one would:

  1. scan pattern-wise
  2. scan line-wise

"Line-wise" is what most new developers feel natural doing: "start with line #1, read it, try to understand what it does, move on to the next line, repeat". The problem is, diving into code like this without any sort of context makes it very hard to understand it. Many students who pretty much skip the big picture and go straight for analyzing (or so they think!) the statements they can see on their screen, quickly freeze up with confusion. That's what the first step, "pattern-wise", is designed to help them overcome.

Let me share a real-world example. Pick that piece of spec which is testing validations for a Vote model in a Ruby on Rails app:

describe Vote do
   describe "validations" do
     describe "value validation" do
       it "only allows -1 or 1 as values" do
         up_vote = Vote.new(value: 1)
         expect(up_vote.valid?).to eq(true)
 
         down_vote = Vote.new(value: -1)
         expect(down_vote.valid?).to eq(true)
 
         invalid_vote = Vote.new(value: 2)
         expect(invalid_vote.valid?).to eq(false)
       end
     end
   end
 end

From time to time, I have a student struggling with that spec. I shall ask him to "explain the code to me", and it usually goes something like this:

Student: Ok, so it reads "up_vote equal vote dot new with an argument, value is 1", ok, and then there is "expect" and… and… and I don't know what this is. What's "expect"?

Good question indeed! But it's the wrong question to ask oneself 🐱

At this point, my goal is anything but answering that question. Instead, I want the student to know why this question is irrelevant and what would be more interesting to inspect. To do so, I will be fully explicit about how I would answer the same question, "explain the code". I usually say something like this:

Mentor: That's a good question, but I'm not going to give you the answer right away, because knowing what "expect" is, well it's not going to help you that much at this point. What about "to", or "eq"? What about the rest of the code? "expect" is very specific, maybe it's important, maybe not, the thing is, you can't tell for now. So I'm going to describe my thought process as if I was answering the same question I asked you, "explain the code", and I'm pretty sure you will see how my strategy is going to help you answer your question, eventually. Ok?

Student: Ok!

Notice how:

  • I promptly gave a hint as to why the initial question was irrelevant;
  • I introduced my forthcoming demonstration as a "strategy", which will eventually empower the student.

Mentor: First, instead of diving into the code itself, as you did, I'd rather breathe and step back. I actually step back from my screen! And I look at this piece of code I'm discovering. My initial goal is to get a feeling for it, before actually reading it thoroughly. What I see—and again, I'm not reading the code right now—well, I see independent groups of lines. Did you notice the blank lines, demarcating three groups?

Student: No, I didn't.

Mentor: I don't know whether it's been organized this specific way on purpose and whether it's important, but hey, I noticed… It's purely visual analysis at this stage. I also notice that the three groups are very similar in shape. They consist of a couple of lines, the first line is an assignment, and there is this "equal" sign in it. The second line, well, it's filled with keywords I don't know about yet, like "expect", but reading in one go, as simplified English, it's saying: "Expect 'whatever' to equal true or false". Ok… Sounds like some kind of boolean test. Oh, and the three groups do look the same: same kind of assignment, and then the same kind of test. So the fact they've been separated by blank lines, I'm pretty sure it was intentional now, to help me get started I guess, and to make it obvious there are three tests in here.

Notice how:

  • I'm dead explicit about everything that would come to my mind while I'm discovering the code;
  • I'm using a fairly relaxed, inner tone, but am also pretty persnickety about my wording, using the proper technical vocabulary;
  • I highlight the existence of patterns, using successive abstractions: visual pattern (the three groups), then logical pattern (assignment + test-case);
  • I make sure to introduce statements about my self-awareness ("My initial goal is…", "I'm pretty sure now that…", etc.)

I keep going:

Mentor: So, there is a pattern. Do you see the pattern now?

Student: Yes. Pretty obvious now that you showed it…

Mentor: Three times in a row, I'm going to create a variable, and test something about that variable. So, it probably means I could review only the first occurrence of that pattern, the first couple of lines, and pretty much understand the whole spec in the process. At this point, I already have the feeling that I could rely on the English semantics of those new keywords, like "expect", to understand what's going on here, even though I don't know what they do. Do you agree with me?

Student: Yes, I do.

Mentor: Ok, so far it has been me looking at the code, literally looking at the code, to get some kind of "big picture". Now, I can proceed with reviewing the very first line in more detail, just like you did. Remember how you jumped for it though, and got confused?

Student: [usually nods or laughs]

Mentor: The difference is that I have some context about the code now, so I'm less stressed about analyzing it, it's not overwhelming anymore. That first line, it's assigning a value to a variable named "up_vote". The value is an instance of the "Vote" model, with a specific value of 1 for the attribute named "value". I'm in a spec file, but there's been no "testing" so far, so I can expect it will come later. Moving on to reviewing the second line, I encounter that new keyword, "expect". Cool, sounds a lot like a test being prepared. It looks like a method call, by the way. The argument is referencing the variable that was assigned in the previous line. Only, a method is called on that variable, "valid?", which I don't know about. But it looks like a question, something one could answer to with "Yes" or "No". So I'm not really surprised it's compared to "true" or "false", and I was already aware the tests are "boolean tests", it's something I noticed earlier on: I said it was "comparing 'whatever' to 'true' or 'false'". The "expect" keyword, I'm not sure exactly where it's coming from, but I can understand what role it's playing, so I'm good with that. I look into it later on, it's not that important after all.

Notice how:

  • I keep involving the student, comparing what I do and think, with what he did and thought;
  • I avoid reading the code, rather leveraging what I can recognize in terms of structure (variable assignment, method call, etc.) to describe what the code is most likely doing;
  • I make sure to use words with a metacognitive semantic, such as "realize", "aware", and "notice";
  • I stress that I'm not surprised by the code ahead of me.

I eventually formalize the method I've just demonstrated, so the student can reuse it:

Mentor: Alright! So let me quickly summarize what I just did. I followed a two-step process: first I described the code "top-view", looking for patterns; then I logically analyzed the code, line-by-line. With the first step, I tried to be as "neutral" as possible. Sure I made some assumptions about what the code was doing, and what it meant, but I knew I wasn't going to be adamant about it. I merely gathered facts and "questionable insights". The second step was more sequential, and it allowed me to check whether I was right with those insights. It boils down to "first scan pattern-wise, then line-wise". Does it make sense? Do you feel like you could apply the same strategy next time you read some new code?

Student: Yeah it makes sense, "pattern-wise then line-wise"… Let me try actually!

Mentor: [discreetly delighted] 🎆

That, was Double Top-Down.

Behind the scenes

The fictional conversation above is pretty close to what could happen during one of my mentoring sessions. It was a singular example, of a spec; when analyzing a complex method, for instance, the "pattern" to mark off could be the "Four parts of a method". The point is, no matter what the code under review is going to be, a student will know about a strategy to handle it, and that strategy builds upon self-awareness.

As a mentor, I'm not trying to teach people what to think, but rather how to think. Students sure spend an awful amount of time taking in new concepts, and they would often complain about the fact they "don't even have time to think". But from my perspective, the fact that a student will learn useful web development chops along the way is almost incidental: they could have learned something else than programming entirely and nonetheless have trained this same ability, metacognition, which is precisely about thinking! Students sensible of that fact are usually the most successful ones.

Once they are aware of metacognition, to help my students get the most out of it, I regularly provide them with the kind of micro-strategy I exemplified above. In addition, I review scientific studies to borrow or adapt generic metacognitive tools, tweaking them into "dumbed down" techniques suited to learning programming.

For instance, "Double Top-Down" is nothing genius. I bet most seasoned developers do something very similar naturally but the main point is that it's liberating for students, because it gives them control over their learning process. Formalizing it into something a newcomer could easily seize upon was challenging for me though. I don't want to improvise, I don't want to get inconsistent results, and I don't have much time to digress. The strategy itself, and the way I could best demonstrate it to my students, I designed using a metacognition framework called strategy evaluation matrix (SEM). It's an instructional aid for developing awareness of metacognition among students. One SEM1 I especially like is the following one:

StrategyHow to UseWhen to UseWhy to Use
SkimSearch for headings, highlighted words, previews, summaries.Before reading an extended text.Provides a conceptual overview, which helps to focus one's attention.
Slow downStop, read, and think about the information.When information seems especially important.Enhances focus of one's attention.
Activate prior knowledgePause and think about what you already know. Ask what you don't know.Before reading or an unfamiliar task.Makes new information easier to learn and remember.
Mental integrationRelate main ideas, use these to construct a theme or conclusion.When learning complex information or a deeper understanding is needed.Reduces memory load. Promotes a deeper level of understanding.
DiagramsIdentify main ideas, connect them, list supporting details under main ideas, connect supporting details.When there is a lot of interrelated factual info.Helps identify main ideas, organizes them into categories. Reduces memory load.

Each row is a generic metacognitive strategy.

I usually start with the third column, which is about conditional knowledge, ie. understanding when a strategy fits given specific constraints. Is my student going to discover new code? Or is he going to try to fully understand code he's already familiar with? Or maybe to write code on his own? In the example above, the student had to explain code he wasn't familiar with, so good matches in the SEM matrix would be "Before reading an extended text" and "Before reading or an unfamiliar task".

The second column is about procedural knowledge, ie. understanding how the strategy works. It gives actionable steps. Instead of searching for "headings, highlighted words, previews, summaries", a student learning how to program would look for "patterns, keywords", etc. but it's pretty close. "Pause and think about…" is always relevant. You can see how it shaped the "pattern-wise" aspect of Double Top-Down.

The fourth and last column is about conditional knowledge once again, ie. understanding why using that strategy is relevant given specific constraints. It's the kind of insight I try to share throughout my demonstration with my students. For instance, "Provides conceptual overview" I often rephrase as "getting the big picture"; "Makes new information easier to learn and remember" I sometimes translate as "not being surprised".

In the end:

  • for the first step of Double Top-Down, "pattern-wise", I relied on the strategies named Skim and Activate prior knowledge;
  • for the second step, "line-wise", I relied on Slow down and Mental integration.

The result is a custom, efficient strategy tailored to my student's needs, with which I've been experiencing very good results. It's a good mix of First Sight and Second Thoughts, and it has helped my student become more aware of metacognition. I created a series of such strategies using the matrix above, some formal and some informal. More generally, the matrix I use as a guideline to assess whether the explanations I give are helpful and whether a student is self-aware enough. I can always work on a specific aspect if need be.

Another significant takeaway from this is that those metacognitive skills are domain-general, meaning that if a student becomes handy at leveraging them for programming, he will likely be able to use them for tackling a wide range of disciplines. So at the end of the day, programming might not be (solely) about programming.

Beyond metacognition

Metacognitive skills and strategies I've been talking about help a lot, but they are barely tangent to an essential aspect of learning: monitoring the learning experience itself. A student may ask herself: what is my goal? What are my expectations? Do I understand what I am doing? How good am I doing? What didn't work? What could I improve? As a mentor, I also need to be aware of where the student is, and what momentum I can rely on, to best adapt my methods. That question is about regulating metacognition, controlling one's learning experience.

The happiest and usually most efficient programmers are self-regulated learners, something I shall discuss in a follow-up post.

Footnotes

  1. From Promoting general metacognitive awareness, Instructional Science, March 1998, Volume 26, Issue 1, pp 113-125

Nov 6, 20159 min readSummary: A case study of empowering students with an explicit metacognitive strategy, allowing them to become self-aware, effective learners.