Philip Guo (Phil Guo, Philip J. Guo, Philip Jia Guo, pgbovine)

Introductory Computer Programming Education

Summary
This article presents my opinions on some problems with how computer programming is currently taught and what types of courses I feel are good for teaching programming to advanced high school and early college students. This is not a proposal for a complete undergraduate Computer Science curriculum, only for introductory courses that teach programming and some rudimentary Computer Science concepts.

I'm not an education expert by any means; these opinions are just drawn from my own personal learning and programming experiences.

Problems with how computer programming is currently being taught

  • First and foremost, introductory courses do not present students with interesting and practical real-world problems that they care about solving. Most problems that students tackle in introductory courses, such as writing simple mathematical functions and toy programs to process user input, are small-scale and completely self-contained, without requiring any interaction with the outside world. Many of these assignments are dry, overly pedantic, and downright boring to students who don't give a damn about finding Fibonacci numbers or manually writing recursive mergesort to sort a list of their classmates' names. In most courses, all students use one IDE or programming environment and have the setup work done for them; all they need to do is to fill-in-the-blanks with some code, compile it, and see the textual output.

    Programming in these courses is done in complete isolation, without the use of libraries beyond the bare minimum or integration with the rich ecosystem of the UNIX-like command-line environment outside of an individual program. I believe that this isolation drastically limits the usefulness of programs that students can write, relegating them to writing only pedantic toy programs. For example, consider a program that downloads images off the web, automatically resizes them and moves them into the appropriate directories, and creates simple HTML web pages to showcase those photos. This could be done quite easily by leveraging the power of either libraries or command-line programs for performing HTTP web downloads, image editing, and file/directory manipulation. However, without access to these external resources, students have no clue that it's actually quite easy to have their programs interact with the outside world and cannot fathom having to write HTTP client, image editing, and filesystem-manipulation code by themselves!

    If the only exposure students get to programming is within a closed, sanitized environment where they simply 'fill-in-the-blanks', then they can never understand or appreciate how the programs they write can interact with other programs or how a software ecosystem thrives, simply because most useful real-world programming involve using specialized libraries or even scripts to chain together existing command-line programs (I didn't begin to comprehend this concept until 5 years after I learned to program in high school when I started writing Python scripts on my own during my senior year of college).

  • Furthermore, there is a huge conceptual disconnect between writing text-only toy programs in a course and creating industrial-strength GUI programs that most average computer users are familiar with (e.g., Microsoft Word, Mozilla Firefox, instant messaging clients). When I took my first programming course (AP Computer Science in high school), I was motivated by the problem-solving aspects of programming, but I could not begin to fathom how it was possible to leap from writing dinky text-only console programs to the rich GUI programs that I had been accustomed to seeing ever since I started using computers as a young child. In other words, how would I go from finding Fibonacci numbers to creating Microsoft Word? Was there something FUNDAMENTALLY different between the 50-line programs that I wrote in class and the 500,000-line GUI programs that I used everyday? I had no clue how to even begin answering these questions, and it really annoyed me that nobody around me knew either. I had hoped to crack this mystery by the time I finished that first programming course, but I did not end up figuring it out until years later when I had to build my first GUI during a summer internship. Thus, it would be great if teachers gave students a 'big picture' overview of how software development works in the real world to show that there is no fundamental magic behind the construction of large software systems - it's just layers of abstraction piled on top of one another :)

  • On a related note, it would be cool to give students an overview of how the different parts of computer software all fit together to create the user experience of a modern GUI operating system. In other words, somehow we need to convey to students that there is no magic going on 'under the hood', that everything running on the computer is simply a program written in some programming language running atop an operating system and hardware. This is an extremely ambitious goal for an introductory curriculum, because it requires some in-depth knowledge of compilers and operating systems. This 'big picture' is currently not being conveyed at the introductory level, because intro. courses are so myopic in focusing on the nitpicky details of specific programming languages and tasks. When I first learned programming in a high school class, I had such a huge mental disconnect between the programs that I was writing for class and the 'real programs' that execute on my Windows computer to provide me with the functionality to play music, chat with friends, and surf the Web. I thought that there was something FUNDAMENTALLY different about those 'real programs' versus my own programs, and I didn't learn until many years later that there was really no fundamental difference ... the only difference was one of scale (but that's a significant enough difference to create emergent properties that aren't present in small toy programs).

  • The languages being used to teach introductory programming are often not appropriate for their intentions. For example, students are going to dread writing and debugging complex algorithms in C simply because there is so much clumsiness in creating and manipulating simple data structures, keeping track of memory allocation, debugging memory errors, etc. that it all gets in the way of the true intention of teaching these algorithms. On the other hand, C is valuable for teaching certain low-level concepts such as how programs really work 'under the hood'.

  • The art and science of debugging is not adequately taught. Students resort to fending for themselves or asking their friends for help, but instructors don't really teach methodologies both for debugging and for writing cleaner and easier-to-debug code in the first place.

  • There isn't enough of a distinction being made between the technical skills of programming and the scholarly study of Computer Science (both are important in an introductory curriculum), so that students are misled into thinking that Computer Science solely consists of programming computers; thus, if they think that programming is tedious and boring, then all of Computer Science must also be tedious and boring.

My proposed introductory computer programming curriculum

Here are the courses that I would teach in an introductory programming curriculum; hopefully these courses can eliminate some of the problems I've described in the previous section.

  1. Introduction to programming concepts and applications, taught using Python immersed in a UNIX command-line environment - I strongly believe in teaching Python to students as a first language (see here for some reasons) because it's so easy to get started solving small but useful problems with minimal overhead. This course should teach the basic concepts of procedural programming (e.g., control flow, basic data structures, functions), and there should be a heavy emphasis on solving real-world problems rather than toy problems. Concurrently with learning Python, students should be learning to get comfortable in a command-line environment, familiarizing themselves with how to create and manipulate files and directories and using an assortment of standard UNIX tools. Being able to work on the command-line as well as to program in Python allows students to create applications that work on real data and produce real results rather than being confined to one program working in isolation. Students should be exposed to a wide variety of libraries for interesting tasks such as downloading content from the web, parsing HTML, manipulating images, developing simple plug-ins to instant messaging clients or social networking sites, etc.

    By the end of this course, my pipe dream is for students to be able to recognize how to solve some simple computing problems that they face every day (e.g., organizing their music collection) by writing small Python scripts. I do not expect students to be able to construct software systems of any significant size only after taking this course, but they can actually achieve a lot with a 100-line Python program :) The point of this course is to give students exposure to the power of programming and to motivate them to learn more about programming in later courses.

  2. Introduction to software engineering, taught using Java within the Eclipse IDE - After learning how to do quick-and-dirty hacks using Python to simply 'get stuff done', students should learn the concepts of how to engineer reliable medium-sized software systems, this time using a much more strict (and industry-accepted) language: Java. Students should learn about abstraction, specifications, modularity, decoupling, testing, debugging, and other software engineering concepts, with the assumption that they know how to do basic programming. This will also be the students' first exposure to object-oriented programming and some simple design patterns. Also, they will learn to work in a modern feature-rich IDE (Eclipse) and learn how to use the integrated debugger and other useful features (such as refactorings, on-the-fly compilation, jumping to identifier definitions and uses).

    This course should culminate in a team project where a group of 3-4 students work together to build a GUI program that they could be proud to show to their friends or family. After all, command-line programs just aren't very sexy, and most computer users think of GUI programs as the ONLY type of program, so by building a GUI program, students can see that they too can create something that looks and feels sort of like the programs they use in their everyday computing (thus bridging that conceptual disconnect between 'toy programs' and 'real programs'). By the end of this course, students should have some ideas of how to design and implement a medium-sized software system, rather than just hacking together spaghetti code, and be prepared to work as a software engineering intern in the industry in order to reinforce what they learned in class.

  3. Introduction to low-level systems programming, taught using C in a UNIX command-line environment - The main reason that I want to teach a programming course in C is to show students how to do low-level things that are easy in C but impossible in higher-level safe languages, most notably treating memory as untyped bytes and manipulating it at will, casting the hell out of pointers and totally abusing the (already weak) type system. One possible application is to build a simple database application that needs to divide up a chunk of memory into records, and depending on meta-data stored within each record, interprets the bytes of the records as different types (e.g., int, string, bool), and also stores/loads chunks of raw bytes to/from the hard disk.

    The general idea is to show how tricky it is to get things right when doing low-level programming, and to give students a taste of what's really going on 'under the hood', but at the same time, to also allow students to know that they really don't need to be programming in C anymore (except for low-level systems software)! Students should get familiar with using gdb to debug scary and frustrating memory corruption bugs and to build up some mental fortitude and gain a greater appreciation for modern languages.

    Another potentially interesting way to teach this class would be to use C to manually implement basic data structures and algorithms which were taken for granted when programming in Python and Java, in order for students to fully appreciate the benefits of using a higher-level language and also to learn know how to write more efficient code in higher-level languages because they know better how things are implemented 'under-the-hood' (see the article Back to Basics by Joel Spolsky).

    Programs written in C (most notably operating systems, libraries, and compilers/interpreters for higher-level languages) form the foundation of all modern software systems (in the old days, machine-specific assembly language code reigned supreme, but nowadays, C compilers can create more optimized code than humans can for most domains). Thus, even if students don't need to write C programs in the future, they should understand the foundation upon which their own software is built.

  4. (Optional) Introduction to functional programming, taught concurrently using both Scheme and Haskell - This optional course is for people who are REALLY curious about more advanced programming language concepts, most notably recursion, higher-order functions, closures, polymorphic type inference (wha???), and functional programming. Scheme and Haskell should be taught concurrently to emphasize their similarities and differences, especially the tradeoffs between dynamic and static typing. Because I don't want to bore everyone except for the math/theory geeks, this class should still at least attempt to present some halfway-practical problems. Perhaps good motivation for functional programming can come from re-implementing solutions to problems in more elegant ways than could be done in C or Java.

Programming languages to never teach to beginners

  • C++ - It's just beastly and horrendously ugly - confuses students like heck (huh? copy constructors? virtual destructors?). Java is far cleaner and can teach the same object-oriented programming and software engineering concepts. Java even has generics now, so no more arguing that C++ has templates. For teaching low-level systems programming, C is much more straightforward to deal with.

  • Perl - This is a language made to be both written by and read by 1337 hackers, not by mortal human beings. The syntax is horrendously ugly and so much of the semantics is implicit and context-dependent, which means that beginners can't easily comprehend what a program does just by reading its source code. Python is far easier on the eyes and the mind.

  • Basic - Any variant of Basic, from QBASIC to Microsoft Visual Basic, to whatever - Python is more expressive and has easier-to-understand syntax. Visual Basic might be decent to use to program GUIs or to create plug-ins for Microsoft Office applications, but office productivity software is a pretty boring domain for students to be programming for, eh?

  • Common LISP - While it may be more 'hardcore' than Scheme, it's also way too complicated for beginners. Standard library functions have a gazillion optional parameters, many constructs are far too general to be beginner-friendly, etc. Scheme was invented as a simplified dialect of LISP with friendlier syntax and semantics, so it's clearly the better teaching language.

  • Artificial, made-up 'educational' languages - These might be good for teaching young children to program, but for older adolescents and college students, I would strongly suggest teaching them using a language that's actually in widespread use in the real world. Psychologically, it's reassuring for students to know that they are learning something that's actually used by professionals in the working world rather than something that was invented by researchers just for pedagogy.

Related Work

Several other technical bloggers have written about the topic of what to teach students in an introductory programming curriculum, and there are many varied viewpoints.

A reputable hacker, Eric S. Raymond, shares my views on the Python -> Java -> C order of teaching programming to beginners, as described in a section of How To Become A Hacker.

Lots of high school and college educators now favor an all-Java curriculum (the high school AP Computer Science curriculum moved from C++ to Java a few years ago). In contrast, Joel Spolsky is 'old school' and favors the Back to Basics approach of starting low-level with C and then progressing to more theoretical Computer Science concepts with Scheme. Although I support teaching both C and Scheme, I think that they should come only after teaching so-called 'easier' languages such as Python and Java, because my #1 priority is to get students to be motivated to write interesting and useful programs, not to have them struggle to understand the intricate arcane details of pointers (taught using C) or recursion (taught using Scheme).

Acknowledgments

Thanks to Robert Ikeda for his input into this topic and for listening to my rants late one evening when I should have been working on research.

Created: 2007-05-24
Last modified: 2007-06-17