By the end, the breadth and depth of our collective knowledge was far beyond what anyone could expect from any high school course in any subject.
Education Versus Exploration
I’m a lab TA for an introductory Python programming course this semester, and it’s been…depressing. I remember my early days of programming, when the possibilities seemed endless and adding new features to my programs was exciting and gratifying, and I brimmed with pride at every detail, and I boasted to my friends of the amazing things I did, and I felt powerful. The world was literally at my fingertips. I could give substance to any idea I cared to entertain and any facet of life I wanted to explore. I had developed an insatiable thirst for programming that has lasted to this very day.
The ironic thing is that today I look back on the programs I wrote and cringe with unending embarrassment. My old code was the artistic equivalent of a finger-painting made by a kindergartener. Sure, it might look kind of like a horse, but only because the kid has no idea what he’s doing. The programs I wrote were bug-ridden, hard to read, poorly organized, and a veritable spaghetti-slop of logic.
But I can’t deny how much fun I had, and how much I learned. I will describe all of that in more detail below, but first I’d like to contrast my experience with the students I’m teaching this semester. Their labs are no more than mindlessly shuffling around data, and implementing dry, boring functions like Horner’s method for evaluating polynomials. And (amazingly) their projects are even less interesting. I think the biggest difference is that my students don’t have to actually solve any problems by writing programs. And so their experience is boiled down to following directions and pretending to be a computer in order to follow their own program’s logic.
I’m certainly not saying that following directions and simulating a computer in your head aren’t important things to be a good programmer. What I’m saying is that my students get no gratification from their work. Their results are just as dry as the problem, and the majority of the joy I see among them is when they finish a problem and don’t have to think about it anymore (even if their solution is completely wrong).
The course has other problems with it. For instance, the professor teaches the students C paradigms instead of Python paradigms (I don’t think he ever learned the right way to do things in Python), and he confuses them with talk of stack frames and registers and all sorts of irrelevant architectural details. Remember, these students have never programmed before, and some started the course just barely competent with a computer. I didn’t know what a stack frame was until I had three years of programming under my belt (two of those years were the early, experimental years).
All of this has gotten me thinking pretty regularly about how I might teach my own course, if I might ever have one. This post will roughly be an outline of how my own computer science education began. I’ll distill the most important aspects of it: the things that made me want to keep programming and the things that taught me deep ideas in natural contexts.
My First Time was with Java
My high school (Campolindo High, in Moraga, CA) was blessed with a computer science course. With my early exposure to computers (at 3 years old, by my parents’ accounts), my love of video games, and my basic grasp of HTML, it seemed inevitable that I belonged in the class. In retrospect, it was perhaps the most beneficial course I ever took, followed closely by Honors/AP English, German, and public policy. Not only did it provide me with the aforementioned thirst for programming, but it planted a mathematical seed in my mind that would flourish years later like a giant bean stalk, which I’m still in the process of climbing today.
Right off the bat the course was different. The teacher’s name was Mr. Maters, and by the end of the first week we ceased to have lectures. Mr. Maters showed us barely enough to get a simple program running with input and output, and then we were left to our own devices.
There were roughly two options for getting credit. The first was to follow an outline of exercises and small projects from a book on GUI programming in Java. Most students followed these exercises for the first two months or so, and I did at least until I had made a stupid little pizza shop program that let you order pizzas.
The second option was wide open. The students could do whatever they wanted, and Mr. Maters jokingly told us, “At the end of each quarter I’ll judge your worth, and if I deem you deserve an A, you’ll get an A, but if I deem otherwise… you’ll get an F!”
Of course, Mr. Maters was the nicest guy you ever met. He would calmly sit at his computer in the front of the lab, maintaining a help queue of students with questions. He would quietly and calmly listen to a student’s question, and then shed some insight into their problem. Mr. Maters would get a better idea of a student’s progress by how frequent and how poignant their questions were, and more often than not students who were waiting in the queue would solve their own problems before getting to the front.
Most of the students in the class chose the “wide open” route, and that meant designing games. I’m not talking about good games, mind you, I’m talking about games made by high schoolers. I made the ugliest Whack-a-Mole you ever saw, a lifeless AI for a Battleship game, and a video poker game that featured Mr. Maters’s face on the back of every card (and replacing the faces of the kings, queens, and jacks). For the last one, I even collaborated with other students who made card games by jointly designing the Maters-themed deck. Collaboration would become a bigger theme the second year I took the course (yes, I took the same course twice), but before we get there, there were some other indispensable features I want to mention.
First, the lab room was set up so that Mr. Maters could remotely control any computer in the room from his desk. The program he used was dubbed the reverent name, “Vision,” and the slackers feared its power. Vision allowed Mr. Maters to look at our code while we were asking him questions at the front, and also helped him monitor students’ progress. Second, we were allowed a shared drive on the school’s network so that we could instantly pass files back and forth between lab computers. This had a few direct learning benefits, like sharing code examples, sprites, and sound files we used in our programs. But more importantly it gave a sense of culture to the class. We occasionally had contests where every student in the class submitted a picture of Maters’s face photoshopped into some ridiculous and funny scene (really, MS-Painted into a scene). Recall, this was the early days of internet memes, and naturally we youngsters were at the forefront of it.
Third, we were given “exploration” days every so often, on which we were freed from any obligation to work. We could play games, browse around online, or just sit and talk. More often than not we ended up playing LAN Unreal Tournament, but by the end of my second year I chose to spend those days working on my programs too; my games turned out to be more fun to work on than others were to play.
All of these instilled a sense of relaxation in the course. We weren’t being taught to the midterms or the AP exam (although I did take the AP eventually, and I scored quite well considering I didn’t study at all for it). We weren’t even being told to work on some days. The course was more of a community, and even as we teased Mr. Maters we revered him as a mentor.
As I did, most students who took a first year in the course stuck around for a second year. And that was when the amazing projects started to come.
The second year in the computer science class was all games all the time. Moreover, we started by looking at real-time games, the kinds of side-scrolling platformers we loved to play ourselves (yeah, Super Mario Brothers and Donkey Kong Country). I tried my hand at one, but before long I was lost in figuring out how to make the collisions work. Making the levels and animating the character and making the screen scroll were all challenging but not beyond my reach.
But once I got fed up with getting him to jump on blocks, I found a better project: Zmob (short for Zombie Mob). It was inspired by collaboration. I helped a friend nail down how to draw two circles in a special way: one was large and fixed, and the other was smaller, always touching the first, and the line between their two centers went through the position of the mouse. In other words, the smaller circle represented the “orientation” of the pair of circles, and it was always facing toward the mouse. It was a basic bit of trigonometry, but once I figured out how to do it I decided a top-down zombie shooting game would be fun to work on. So I started to it. Here’s the opening screen of an early version (typos and errors are completely preserved):
In the game you control the black circle, and the grey circle is supposed to represent the gun. Zombies (blue circles) are spawned regularly at random positions and they travel at varying speeds directly toward your character. You can run around and if you hold down Shift you can run faster than them for a time. And of course, you shoot them until they die, and the game ends when you die. The number of zombies spawned increases as you go on, and your ammunition is limited (although you can pick up more ammo after you get a certain number of kills) so you will eventually die. To goal is to get a high score.
The game plays more like reverse-shepherding than a shooter, and while it might be hard, I don’t think anyone but me would play it for more than ten minutes at a time.
The important part was that I had a lot of ideas, and I needed to figure out how to make those ideas a reality. I wanted the zombies to not be able to overlap each other. I wanted a gun that poisoned zombies and when a poisoned zombie touched a healthy zombie, the healthy one became poisoned. I wanted all sorts of things to happen, and the solutions naturally became language features of Java that I ended up using.
For instance, at first I just represented the zombies as circles. There was no information that made any two zombies different, so I could store them as a list of x,y coordinates. Once I wanted to give them a health bar, and give them variable speeds, and poison them, I was forced to design a zombie class, so that I could give each zombie an internal state (poisoned or not, fast or slow, etc.). I followed this up by making a player class, an item class, and a bullet class.
And the bullets turned out to be my favorite part. I wanted every bullet on the screen to be updated just by me calling an “update()” function. It turns out this was the equivalent of making a bullet into an interface which each specialized bullet class inherited from. Already I saw the need and elegance behind object oriented programming, something that was totally lost on me when I made those stupid “Shape” interfaces they have you do in basic tutorials. I was solving a problem I actually needed to solve, and an understanding of inheritance was forever branded into my mind.
And the bullet logic was a joy in itself. The first three guns I made were boring: a pistol, a machine gun, and a shotgun. Each sprayed little black circles in the expected way. I wanted to break out and make a cool gun. I called my first idea the wave beam.
The idea behind the wave beam is that the bullets travel along a sinusoidal curve in the direction they were shot. This left me with a huge difficulty, though: how does one rotate a sine wave by an arbitrary angle? I had x and y coordinates for the bullets, but all of the convoluted formulas I randomly tried using sines and cosines and tangents ended up failing miserably. The best I could get was a sort of awkwardly-stretched sideways sine.
After about a week of trying with no success, I finally went to my statistics teacher at the time (whom I still keep in touch with) and I asked him if he knew of any sort of witchcraft mathemagic that would do what I wanted.
After a moment’s thought, he pulled out a textbook and showed me a page on rotation matrices. To my seventeen-year-old eyes, the formula was as mysterious as an ancient rune:
My particular code ended up looking like:
x += frame*Math.cos(angle) + Math.sin(frame)*Math.sin(angle) y += frame*Math.sin(angle) + Math.sin(frame)*Math.cos(angle)
When I ran the code, it worked so perfectly I shouted out loud. After my week of struggle and botched attempts to figure this out, this solution was elegant and beautiful and miraculous. After that, I turned to calculus to make jumping look more natural in my Fox side-scroller. I experimented with other matrix operations like shearing and stretching. By the end of that year, I had a better understanding of a “change of basis” (though I didn’t know the words for it) than most of the students I took linear algebra with in college! It was just a different coordinate system for space; there were rotated coordinates, fat and stretchy coordinates, along with skinny and backward coordinates. I tried all sorts of things in search of fun gameplay.
And it wasn’t just mathematics that I learned ahead of my time. By the end of the year I had “finished” the game. I designed a chain gun that set off chain reactions when it hit zombies, I had given it a face lift with new graphics for the player and zombies. I even designed a smart tile-layout system to measure the size of the screen and display the background appropriately. I had gotten tired of measuring the sizes by hand, so I wrote a program to measure it for me. That sounds trivial, but it’s really the heart of problem solving in computer science.
The whole class “beta tested” it, i.e., spent a few days of class just playing it to have fun and find bugs. And they found lots of bugs. Overt ones (divide by zero errors making bullets go crazy) and subtler ones (if you time everything right, the zombies can’t get close enough to hurt you, and just keep bumping into each other).
One pretty important issue that came up was speed. Once I added images, I decided to use a Java library to rotate the images on every frame so they were pointing in the right direction. Now some people say Java is slow, but this part was really slow, especially when it got up to a hundred or more zombies. My solution, it just so happened, was a paradigm in computer science called caching. You pre-compute all of the rotations you’ll ever need ahead of time, and then store them somewhere. In fact, what I really did was called lazy-loading, a slightly more sophisticated technique that involved only storing the computed rotations once they were needed.
And I never learned the name of this technique until I got to a third-year college course in dynamic web programming when we discussed the Hibernate object-relational mapping for databases! Just like with linear algebra, my personalized problems resulted in me reinventing or rediscovering important concepts far earlier than I would have learned them otherwise. I was giving myself a deep understanding of the concepts and what sorts of problems they could solve. This is distinctly different from the sort of studying that goes on in college: students memorize the name of a concept and what it means, but only the top students get a feel for why it’s important and when to use it.
An Honest Evaluation in Retrospect
I’ll admit it, I was more dedicated to my work than the average kid. A small portion of the class was only engaged in the silly stuff. Some students didn’t have a goal in mind, or they did but didn’t pursue the issue with my kind of vigor. We didn’t have access to many good examples outside of our own web browsing and the mediocre quality of the books Mr. Maters had on hand. The choice of Java was perhaps a steep learning curve for some, but I think in the end it did more good than harm.
But on the other hand, those of us that did work well got to explore, and absorb the material at our own pace. We got to struggle with problems we actually wanted to solve, leading to new insights. One of my classmates made a chat client and a networked version of Tron, while others made role-playing games, musical applications, encryption algorithms, painting programs, and much more. By the end, the breadth and depth of our collective knowledge was far beyond what anyone could expect from any high school course in any subject. I don’t say that lightly; I spent a lot of time analyzing literature and debating contemporary issues and memorizing German vocabulary and fine-tuning essays and doing biology experiments, but programming was different. It was engaging and artistic and technical and logical and visceral. Moreover, it was a skill that makes me marketable. I could drop out of graduate school today and find a comfortable job as a software engineer in any major city and probably in any industry that makes software. That class was truly what set me on the path to where I am today.
And worst of all, it absolutely breaks my heart to hear my students say “I didn’t think programming would be like this. I’m just not cut out for it.” The best response I can muster is “Don’t judge programming by this class. It can be fun, truly.”
What They Need
It’s become woefully clear to me that to keep students interested in programming, they need a couple of things:
1. Instant gratification
My students spend way too much time confused about their code. They need have some way to make a change and see the effects immediately. They need teaching tools designed by Bret Victor (skip ahead to 10:30 in the video to see what I mean, but the whole thing is worth watching). And they need to work on visual programs. Drawing programs and games and music. Programs whose effects they can experience in a non-intellectual way, as opposed to checking whether they’re computing polynomial derivatives correctly.
2. Projects that are relevant, or at least fun.
Just like when I was learning, student need to be able to explore. Let them work on their own projects, and have enough knowledge as a teacher to instruct them when they get stuck (or better yet, brainstorm with them). If everyone having a customized project is out of the question, at least have them work on something relevant. The last two projects in the class I teach were regrettably based on file input/output and matrix sums. Why not let them work on a video game, or a search engine (it might sound complicated, but that’s the introductory course over at udacity), or some drawing/animation, a chat client, solve Sudoku puzzles, or even show them how to get data from Facebook with the Graph API. All of these things can be sufficiently abstracted so that a student at any level can handle it, and each requires the ability to use certain constructs (basic networking for a chat client, matrix work for a sudoku, file I/O in parts of a search engine, etc.). Despite the wealth of interesting things they could have students do, it seems to me that the teachers just don’t want to come up with interesting projects, so they just have their students compute matrix sums over and over and over again.
3. The ability to read others’ code.
This is an integral part of learning. Not only should students be able to write code, but they must be able to read foreign code. They have to be able to look at examples and extract the important parts to use in their own original work. They have to be able to collaborate with their classmates, work on a shared project, and brainstorm new ideas while discussing bugs. They have to be able to criticize code as they might criticize a movie or a restaurant. Students should be very opinionated about software, and they should strive to find the right way to do things, openly lampooning pieces of code that are bloated or disorganized (okay, maybe not too harshly, but they should be mentally aware).
These three things lie at the heart of computer science and software development, and all of the other crap (the stack frames and lazy-loading and linux shells) can wait until students are already hooked and eager to learn more. Moreover, it can wait until they have a choice to pursue the area that requires knowledge of the linux shell or web frameworks or networking security or graphics processing. I learned all of the basics and then some without ever touching a linux terminal or knowing what a bit was. I don’t doubt my current students could do the same.
And once students get neck deep in code (after spending a year or two writing spaghetti code programs like I did), they can start to see beauty in the elegant ways one can organize things, and the expressive power one has to write useful programs. In some sense programming is like architecture: a good program has beauty in form and function. That’s the time when they should start thinking about systems programming and networking, because that’s the time when they can weigh the new paradigms against their own. They can criticize and discuss and innovate, or at least appreciate how nice it is and apply the ideas to whatever zombie-related project they might be working on at the time.
I hold the contention that every computer science curriculum should have multiple courses that function as blank canvases, and they should have one early on in the pipeline (if not for part of the very first course). I think that the reason classes aren’t taught this way is the same reason that mathematics education is what it is: teaching things right is hard work! As sad as it sounds, professors (especially at a research institution) don’t have time to design elaborate projects for their students.
And as long as I’m in the business of teaching, I’ll work to change that. I’ll design courses to be fun, and help my future coworkers who fail to do so. Even in highly structured courses, I’ll give students an open-ended project.
So add that onto my wish list as a high school teacher: next to “Math Soup for the Teenage Soul,” a class called “Finger-paint Programming.” (or “Programming on a Canvas”? “How to Kill Zombies”? Other suggested titles are welcome in the comments :))
Here’s a project from the University of Kent for entry-level instant gratification: http://www.greenfoot.org/
Is it actually instant? I’m thinking of an editor that compiles and runs your code as you type it, so that you can see the game updates live, and not have to recompile each time you make a change.
However, I’m really impressed by the Kinect support: http://www.greenfoot.org/doc/kinect That would make a wonderful programming experience.
There’s no REPL to modify the program at runtime, but there is a lot of overlap with your comments. The creator has a demo at http://www.youtube.com/watch?v=Tcwx-I6Arwk . At time 23:23 he talks about the fun of customized pictures and sounds. If memory serves, there’s even a one-click post-to-web feature to share your programs with friends outside the classroom.
A similar project is http://www.alice.org from CMU, but it uses a nonstandard language.
Well… I think Greenfoot would be a better teaching tool for a younger audience. This falls in with my opinion of Scratch as well: I couldn’t see a high school student using the framework for a long time without feeling constrained or getting bored. Moreover, my experience was no duller for the details I endured in doing it all by hand. I wanted to know the details of the graphics. I wanted to have complete control over every aspect, to make the application full-screened, and to design my own welcome menu and handle all of the key- and mouse-event logic. Even the details of code organization became an enjoyable task, because it was all mine. Perhaps one might call this the “boyish tinkering” that drive so many boys to build bridges and work on cars and play with legos, but this programming class was distinctly separated in my mind from the sort of Logo turtle graphics I had used in middle school (which were relatively fun at the time, but in a completely different league; turtle graphics never awed me).
I know a number of teachers who adhere to the idea that a student should gradually be introduced to a language, literally by removing the language features that are more advanced (this was how the beginning of my first course in university was taught, to my dismay). Personally I think it’s a flaw in the language if the more advanced features get in the way of basic programs so adversely that a newbie can’t overcome them. Further, a student will effortlessly learn to ignore and accept the more advanced details until he’s ready to understand them. For instance, I had committed “public static void main(String args)” to memory long before I knew what all the parts meant, and how they changed the meaning of a function.
I actually learnt from this. Greenfoot was the IDE in which I first programmed. I can’t recommend it enough. Everything in this article I remember; the instant gratification, the zombies (I made about three zombies games), the visual aspects. I then moved on to creating programs (i.e. not games) in the BlueJ IDE – which is actually made by the same people! I also highly recommend BlueJ. Of course, now when I’m using Java I use things such as IntelliJ IDEA (not that I use Java too much anymore except for college work, which requires it to be completed in Java), but both BlueJ and Greenfoot are _fantastic_ for hooking you into programming.
I’ve heard lots of people say that Java shouldn’t be taught as a first language because it protects you from all the ‘hardcore’ low-level stuff, and because of this everyone should be taught C/C++ from the start, and pointers, and memory management, and so on and so forth. These people say that if you start with Java, you’ll struggle to get to grips with the hard stuff. In my experience, this isn’t true at all. I started with Java (a la Greenfoot), and have had no trouble with things such as pointers in C++.
Interesting first. Nitty-gritty later.
Ha! That’s funny, because I would say that Java is still too low-level for a first programming language. At the least, there’s far too much typing and room for awkward warnings and syntax errors. But I’m sure the IDE makes a big difference in that (I learned in Eclipse, and it does most of the scaffolding-typing needed for classes and such).
Hi, Hope you will share your experiences teaching introductory programming more often.
I really liked the way Udacity’s CS101 went. It takes some amount of thought in to the teaching process, to come up with such a curriculum. I am one of the victims of a bad (read: mechanical) introduction to programming several years ago…and would love to see a different way of introducing!! 🙂
Very interesting post! Thanks for sharing.
Great blog post! 🙂
I came across your blog from google+. My wife and I worked on a project intended to help beginners learn C++:
It has problems. It needs a way to share code, and we don’t have the time to do that. It’s in C++ which is unfashionable with a lot of people today. It also uses the Qt SDK which lots of people seem to not want to download and try. It also uses an IDE, a fact which we’ve gotten some justifiable criticism over, as IDEs can be overwhelming to beginners. We’ve shown it to a couple of teachers who liked it, but they are pretty insistent that it “be on the web” by which they mean it must run in the browser. We posted a link to it on the official Qt forums, and it got a couple of upvotes, but no one ever commented in the thread.
In short, we’ve had a hard time getting anyone to actually *try it*! lol Every single person we’ve shown it to or talked to about it has immediately started telling us how we must change it, but to our knowledge, not one person has actually sat down and spent any time trying to work through the exercises we wrote, using our documentation.
We spent a lot of time on it, and although it is unfinished, it demonstrates our idea of a way to provide students with something a little more fun, with immediate graphical feedback, etc. But we gave up working on it. I don’t know that it is a good solution, and honestly, I don’t know that we are the right people to try to create it. But you can’t tell what it is trying to teach without *working the exercises*, and we can’t get anyone to do that, so we just don’t know. 🙂
You seem to share some of the same ideas, and have had a similar experience getting started in programming as I did on the TRS-80 back around 1982. I thought you might find it interesting. I like some of the ideas we had about trying to make programming a little more engaging, but I’m not sure it will actually be useful, and we have other priorities now. But I thought you might be interested and might enjoy checking it out. 🙂 Maybe an implementation of something like FeetWetCoding would be helpful to beginning students in some other language like Java or Python.
Unfortunately I simply don’t have the time to work on large software projects of my own these days, but from my experiences teaching it seems there are three main barriers to learning programming:
1. Most people don’t know how to use computers to begin with, and doing any programming outside of the browser requires some maturity there.
2. Most people don’t have an interesting project to motivate them to continue.
3. Programming itself is hard to learn.
In particular, setting up a development environment is often prohibitively difficult. I (a more or less experienced programmer) still struggle every now and then to install libraries (cygwin is particularly notorious in my mind). You’d be really lucky if you can guarantee your users get past step 1 of installing Qt.
Of course, #3 is what most teachers want to focus on when teaching programming. The sad fact is that without a human there to help with clear explanations of what the student is doing wrong, the student is bound to mess things up. Part of this is in maturity in deciphering error messages, but it often loops back to #1 as well: what should the user do if they’re trying to open a file and get a “file not found” error? Without competency in #1, you wouldn’t even think that file-paths are relative, you just expect it to find your file.
For instance, on your website you say that if they mangle their exercise and can’t get the code to build, then they (provided they haven’t exited the program yet) can use the undo function to revert to the original state of the exercise. This raises some obvious questions, because a beginner is guaranteed to mangle it. Why isn’t there a “reset exercise” button? Why should they have to struggle to build their code, when instead there should be instructive explanations of error messages which occur before a student even tries to build? Why isn’t there a way for them to figure out why they mangled it in the first place, so that they can improve later? All of these would help them get past problem #1 and be able to focus on #3.
I think all of these things are driving learning environments to the browser. They are simply more homogeneous than raw operating systems, and they can strip out the issues with #1 to focus on #3. I also think part of the problem is that people want to learn to program because now it’s established as a cool, high-paying, acceptably degreeless profession. In other words, they want to learn it for reasons other than enjoyment or interest, so patience is in less supply.
Title suggestions? Maybe “50 Shades Of Zombies” would get the female future programmers interested! 🙂
Awww I feel like I read your journal. So dedicated 🙂
I discovered your blog a week ago and didn’t even realize that it was yours until today (I am not very observant). Anyway, you should contact Mr. Maters and show him your blog. I bet he’d love to read it.
Funny coincidence, I actually saw him in person the other day. I mentioned the blog and I’m sure he found it, but I don’t know if he read this post.
Reblogged this on The Order of SQL.