Stop Teaching Code.

Solicit Predictions Instead.

I’m serious — stop it with the gatekeeper introductions to content.

In theory, it makes sense to start with the core building blocks and work your way up. That’s why so many CS lessons on functions start with “Today we’ll be learning about functions. Functions are a repeatable, customizable blocks of code which can be defined with the keyword def and which can …”

Before you know it, you’ve wound up explaining the difference between a reference type and a value type to someone before they could possibly give a hoot about the difference.

If your students are already 100% on board with CS, then at best they’ll have already mined this information on their own on YouTube or reddit. At worst, they’ll endure the lecture while you slog your way through. But the students who weren’t sure if this was the right class for them? You’ve just confirmed what they feared — that this class only welcomes those who could already pass it.

To be honest, starting in the world of the theoretical isn’t inherently flawed. David Malan does a really great job of this bottom-up approach to code without accidentally screening for prior knowledge in Harvard’s CS50 course. Whenever he calls on a student, he celebrates the reasonable misconceptions of beginners, and acknowledges but does not glorify the more advanced students’ out-of-scope questions (which can alienate those with less experience).

But odds are, you aren’t David Malan. So I want to propose an inversion of the lesson structure that lets you welcome beginners with the same enthusiasm that he does.

Here goes.

DON’T: The Academic Model

Teachers feel the need to be the source of truth. I say this from a place of humility, because I was perhaps the worst offender at my school in my first year of teaching English. I was 23, only 5 years older than my high school seniors. It didn’t feel like enough of an age gap to inherently demand respect, and so tried to command respect in two other ways: I wore a tie, and I pretended to know everything.

To be honest, it almost worked, but it also meant that a lot of my lessons involved throwing up a slide with a definition of a poetic or rhetorical device on it, having students write down the definition, showing an example, and then being disappointed when they couldn’t identify another example of that device later on in the lesson.

If we were to break that into steps, it looked something like this:

  1. Identify academic vocabulary word students should know.
  2. Show definition. Insist that this definition is important.
  3. Show example.
  4. Ask students to “create” own example.
  5. Repeat 1–3 for about five (5) total terms.
  6. Give a multiple choice quiz with *other* examples and ask students to identify which definitions mapped onto which examples. In other words, ask them to solve a puzzle (for a grade!).
  7. Despair as only my most privileged and well-read students succeed.

This approach wasn’t my own invention — I was following the “I do, we do, you do, assess” instructional model that had been handed to me. I was doing my level best, and I was following all the rules, but it just wasn’t working.

The problem with this model in any discipline, but especially in a coding classroom, is that it asks students to memorize nuanced differences between different situations without ever putting them in a situation where knowing those nuanced differences is helpful.

DO: The Prediction-First Method

So my proposal is simple. Just take those steps up there and reverse the model.

  1. Present a puzzle for students to solve. The puzzle is complicated, and involves all of the composing pieces you intend to teach for the day.
  2. Ask students to create something based on that puzzle.
  3. Show your own example of what the thing we’re doing looks like, ideally in its most perfect form.
  4. Take the mental model students have built, and paste the academic vocabulary and definitions on last.

In other words, find a way for students to sense deep within themselves what a line of code does before ever asking them to learn the name for the individual parts of that line of code.

Code for Predictions

When I say puzzle, I don’t mean skipping right to “Let’s build bubble sort from scratch!” on the day 1.

In fact, you should NEVER have students guess new elements of a language’s syntax, and you should also refrain from teaching new syntax and new algorithms at the same time.

What I do mean is that you should simply present finished programs and then ask students what these programs do. In other words, have students read and understand code before you ask them to write it.

Here’s an example — the second thing I ever show students, right after print("hello world") , is this right here:

name = "Tamara"print("Hello" + name)

And then I ask one simple question:

Don’t answer out loud — just think. What will happen when you run this program?
Don’t answer. Just think. What will happen?
Turn and ask your neighbor for their prediction.

Literally every student intuits that this program is going to greet Tamara.

And then after that, we run the program, find out that it prints HelloTamara without a space, and we also do our first round of debugging. High fives all around!

You’ll notice that we haven’t yet said the words stringconcatenate, or variable, but now we have enough context that I could dive in and have students handle whichever of those is most critical for the day’s lesson. But we truly don’t have to cover any of those before asking students to write a program that greets their favorite fictional character, historical figure, or classmate. We’re ready to build.

I recognize that in this form, it’s a coding class and not a computer science classroom. But I also think that’s okay. You don’t need to know the difference between value and reference types to build a cool project on your first day. And once you believe that computer science allows you to build cool projects, you’re much more likely to be hungry for the theoretical complexity underlying it.

What I’m driving at here is that cooking is to chemistry as coding is to computer science.

You don’t need to know that Python comes with strings and C doesn’t in order to write your own silly version of The Oregon Trail, any more that you’d need to understand the chemical changes that take place when cooking an egg in order to make an omelette.

That’s not to say that underlying complexity (of both programming and omelette-making) isn’t interesting. In fact, if some of your students ultimately want to be thought leaders in the discipline, the formal definitions and best practices are critically important. But if your goal is to pass your enthusiasm about computer science on to your students, these details are not the right entry point to the discipline.

And if you’ll allow me to push this food metaphor to its breaking point, no one has ever applied to the culinary institute because they wanted to be a chemist — it’s because consuming great food is fun, and they want to create great food. Our students will love these granular, academic details in the same way we do later, after they’ve seen how powerful their code can be. So let’s stop with the hour-long lectures about ingredients, and start giving our students playtime in the kitchen.

Bad Examples Made Good

Let’s take a quick look at some old coding standbys that we should really retire. We’ll convert each one into a great prediction challenge by making a few small tweaks.

Example 1: Conditionals

Here’s the most common example I see when an academic teaches conditionals in any language.

if 1 > 2:
print("1 is greater than 2")
else:
print("1 is not greater than 2")

Yikes.

1 is not greater than 2. I know that’s the point, but this is an example that requires students to see code in its least authentic form. This example asks students approach conditionals from the never-useful case where you compare two literals.

Also, bonus bad-example points when a text or tutorial relies on the inherently meaningless print("foo") and print("bar") as outcomes — they tell you nothing about the program.

How about this as an alternative?

age = 15if age >= 18:
print("You're old enough to buy a ticket for an R-rated film.")
else:
print(f"You'll be old enough in {18 - age} years")

As it’s written right now, what’s going to print out in the terminal?
3 isn’t anywhere on screen. How did you know it would say “3 years”?
How could we change the program to make the other statement display instead?
What does indentation appear to do to our program?

Even with the out-of-nowhere addition of string formatting, students can immediately figure out what’s happening here. Use guided questions to probe about syntax after students have already intuited how the indentations work. And of course, you can change the indentation question to be about curly braces or do and end depending on what language you’re teaching — it always works.

After going over these questions, you can brainstorm a dozen other age breakpoints (driver’s permit, license, car rental, double digits, triple digits, oldest person alive, etc.) for the create task. Ask if they need any additional help before getting started, give them whatever they ask for (even if they don’t know the exact names like elif or and), and then watch them go!

Example 2: Collections

The bad example used for collections has to be my absolute favorite:

a = [3, 2, 7, 5, 3, 9]print(a[2]) 
# returns 7

Stop using numerical indexing to organize numerical information — as written, this example is at best inscrutable (“Why would 2 return 7?”), and at worst implies incorrect understanding (“Oh, does it just return the next number?”), but it doesn’t have to be this way.

Here’s my revision:

favorite_foods = ["Sushi", "Tamales", "Mofongo", "Pizza", "Chicken Tikka"]print(favorite_foods[2])

Which food will print out?
[After testing]: How could I print out the first food?
How could I print out the last food?
Why would favorite_foods[10] cause a problem?

That first question in particular is loaded with assumptions, and that’s on purpose. I’m directing my students’ focus to the number 2, and using that as the key to understanding the purpose of structuring the list in the way I have.

After this single example, students understand listsindexes and even index out of range errors. For the create task, they can start by declaring their own list, and if they’ve also already got for loops under their belts, they can use this as the starting point for a CLI to-do-list generator.

Example 3: Objects

Objects is another one where it’s tempting to start as small as possible in order to tackle this pretty giant topic. Here’s what I usually see:

Class Dog:
def __init__(self):
pass

def speak(self):
print("woof!)
Fido = Dog()
Fido.speak()

I love the intention behind this. It’s so much better than “foo” and “bar” that I almost want to give it a pass.

But I have some complaints:

  • Dogs don’t exist in cyberspace.
  • You’ve used nine lines to create and call a function. I can write code that does the exact same thing in three lines without objects.
  • At this point, 100% of dogs created are the same, so why make a class?

We can do better. How about this:

user1 = User("Ty", "[email protected]", 16, "Pa$$word")
user2 = User("Sarai", "[email protected]", 15, "Kangaroo!")
user3 = User("Osu", "[email protected]", 12, "12345abcde")print(user2.age)

What’s the first user’s email?
Which user has the weakest password? (I don’t care about a right answer, but forming an opinion about it requires understanding how these objects will be used)
What will the last line print out?

Depending on their comfort level with risk-taking, I can even sometimes get students to correctly answer this one:

“Can anyone guess how I can get the string Osu to be printed?”

And to be honest, I praise the risk-taking necessary to make that guess more than I do the thought behind it — you get from students who are willing to try things.

After students have bought in to the purpose of OOP (“So each user has their own name, email, age, and password, and you can instantly create lots of different users?”) then you can sell them on the importance of knowing the syntax used to create the class (“Yup! Want to see how to set that up?”).

An important thing to notice here is that this code isn’t simple — it’s actually pretty complex. But it is clear, and I’m starting to realize that clarity matters more than simplicity.

Let’s Zoom Out

Here’s my thesis: If you write good code examples, students will infer the purpose and master the content.

So here are the criteria I use to determine whether a code example counts as “good”:

  • Can students who don’t know this syntax yet still figure out what this code does?
  • Is the code reasonably similar to what one might see “in the wild” later on?
  • Are my literals meaningful (no foos and bars) and are my variables well named (no str1 and myarray)?
  • Do my questions allow students to focus in on the most important part of what’s happening here?
  • Do my examples rely on prior experience marketed to just one subset of my students (e.g. video games) or am I digging for examples that nearly everyone can relate to (e.g favorite foods)?

When I’m confident that the puzzles which open my lesson stand up to the scrutiny of these five questions, I know I’m making inroads instead of gatekeeping.

The Takeaway

Write readable code, and then let your students form their own understanding. Definitions are important in computer science, but the love of definitions never made anyone a computer scientist.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *