Wednesday, May 8, 2013

CS222: A Year in Review

Introduction

Three years ago, my department introduced a new required sophomore-level course, CS222: Advanced Programming. I was on the committee that designed the course, which was created to help students bridge between introductory programming and data structures courses to project-oriented, upper-division courses. I have taught the course several times, and this post is a reflection on my experience with two sections this past academic year.

Course Structure

Students come into this course having taken a course on discrete structures and two introductory programming courses—one on the fundamentals of programming and one on basic data structures. I also convinced the department to add freshmen composition as a prerequisite, in order to stave off the worst of the bad writing. This was the first year that the writing prerequisite was in effect, and I don't remember many times when I wanted to throw my monitor out my window at reading students' writing, so I suppose that was a success.

I used the same course structure in both the Spring and Fall semesters. We began with seven weeks of structured daily exercises, generally emphasizing reflective practice. For example, several assignments involved reading an article or expert programming tip then comparing and contrasting programs that follow and don't follow the tip. Frequently, I asked students to look at their own programs from the first two programming courses and evaluate them for phenomena such as naming convention violations or comment rot. The next two weeks were devoted to a two-week project undertaken in pairs, with a formative evaluation after the first week. In the Fall, I used Mad Libs, and in the Spring, an RSS analyzer that counted frequencies of words. The latter was inspired in part by my experience working on WikiBeat.

The last six weeks of the semester were devoted to the final project, which I described as a six week final exam. Teams of four pitched project ideas, which I vetted before approval. They had to set milestone objectives for themselves for three- and six-week milestones. Once again, the mid-project milestone was a formative evaluation (that is, it did not count in the final grade). Each team had to produce a technical whitepaper in addition to the working software. I provided a rubric before the pitches indicating how the project would be evaluated, and it contained the following categories: in the whitepaper, requirements analysis, acceptance testing, architecture articulation; in the software implementation, coding conventions, distributed version control, object orientation, and logging.

The logging requirement is unique in the list. I purposefully do not design interventions about logging, neither what it is or how to do it. Instead, I put it in the requirements as a litmus test of sorts, to see if the student teams can collectively identify an unknown, research it, and integrate it. If someone asks me for help, I will certainly point them in a fruitful direction.

A Year of Clean Code

When we designed CS222, I chose Joshua Bloch's Effective Java as a recommended text for instructors who wanted to use Java. One of the goals of the class is to teach students how to learn advanced programming concepts themselves, and Effective Java is a treasure trove. The book is full of general advice (such as preferring immutable objects) along with tips on how to implement these in Java (such as details on how to use final fields and prevent subclassing). In my three semesters teaching with this book, I used assignments to scaffold the process, starting with assignments in which I directly told students how to read and apply a section, moving toward assignments in which the student had to find, understand, and integrate new tips. I was happy with this approach, but each semester, there was a vocal, significant minority who complained that the book was impenetrable. There are many reasons that sophomore undergraduates may have had trouble with these exercises, but I don't have the luxury of addressing the root causes: I had to deal with the fact that some students were having real trouble with this.

Around this time, I read Robert C. Martin's Clean Code and began using it in my projects. I adopted Clean Code this past academic year, using it in the Fall and Spring offerings of CS222. My hope was that it would be more accessible to the students because it is easily readable; my fear was that they would not be able to apply the principles because it was separated from the specifics of a programming language. I was also inspired by the success of using Clean Code in my VBC project last Spring: for all of the complex and difficult work that team did, Clean Code still came up as one of the most important things they learned.

Different Semesters, Different Populations

I had the smaller of two sections in the Fall, only around a dozen students. Their abilities generally met my expectations, and we had an excellent semester. There were four project teams, and each decided to create a game: one infinite side-scroller, one tower defense game, and one platform game. I had told them that they could make games, but that despite the fact that I teach game programming as well, I discouraged them from trying to make a game in six weeks. Once they made their pitches, though, it became a very comfortable teaching and learning experience, since I am intimately familiar with game programming and, specifically, the kinds of problems that novices have as they start making their own games. I was quite proud of the students' work, and so I allowed them to vote on whether the final presentations should be open to the public or not. They voted unanimously for it, and it was great to see how proud they were of their accomplishments and how impressed the audience was in their products.

Unfortunately, the story does not end there. When I evaluated their three-week milestones, I noticed several significant violations of coding conventions (Clean Code in particular), problems of object-orientation, and a failure to do appropriate logging. The final projects had more features, more polish, but still contained significant flaws. We had a good discussion about this after the final presentations, about how the teams had gotten caught up with features—as I had warned them might happen—and had neglected the actual purpose of the project, which was to incorporate the semester's lessons into an interesting and personally meaningful project.

In the Spring, I had the only section, about two dozen students. I started the semester by showing two of the games from the Fall, explaining to the new group that they would be able to make their own projects too—but that it was important that they learn how to do it right. I used FizzBuzz both semesters early in the semester as an introduction to test-driven development and object-orientation; I noticed that the Spring group had trouble getting just the basic FizzBuzz to work, but at the time I dismissed it as a fluke of having to meet at 8AM.

Turns out, it was not a fluke. The Spring class' technical abilities were far below the Fall's, a contrast between semesters unlike any I had seen before even in teaching "off-sequence" introductory programming courses. The first several weeks of the class involve reading, reflecting, and analyzing previously-written code; the students don't start writing significant programs until several weeks into the semester. As a result, it took me almost half the semester to recognize that many of the students in my class—even of those left after the drop deadline—could neither read nor write rudimentary Java programs. This means that they didn't really get much from the first several weeks either: while they could identify naming conventions violations, many didn't have any mental model for what the program was actually doing.

I did a bit of detective work in an attempt to root out the problem, and I found two contributing factors. First, several of my students had taken their two introductory programming courses in the 2012 Summer sessions, and had no prior programming experience. This means that they did programming for only ten weeks of their lives, and then didn't touch it for half a year, and then showed up in my advanced programming class. These students simply lacked the experience required to have any sense of the craft (although, I should note, one particular student recognized this failing and, laudably, demonstrated significant growth by undertaking out-of-class projects and befriending a community of practitioners). The second factor is that, according to the word on the street, some teaching in the introductory sequence only use fill-in-the-blank programming assignments. I have heard this rumor intermittently, and one must always take such rumors with a grain of salt. This semester, however, a trusted student in my game studio told me how he had never written an application before CS222. 

Consider the implications! A student with no prior programming experience decides she wants to take introductory programming, spends fifteen weeks struggling with concepts like iteration, sequencing, and selection, and after fifteen weeks... still has no idea how to write a program to actually do something she wants to do! I am sure that the professors are doing what they think is best, but they are wrong. Although I cannot do anything about this in CS222, I am a perennial member of our foundations curriculum committee, and so I have some agency in how this can be addressed as a curricular issue.

By the time I had all the pieces together, it was too late. In fact, I designed some in-class exercises to help catch people up on nomenclature they consistently got wrong—a student pointed to a parameter and called it a "constructor" in the last three weeks of the semester, for example. However, at that point, the people who needed help most had stopped attending class. Indeed, attendance plummeted in the penultimate week of the semester, although I never got a good reason for this besides students' being overwhelmed by undergraduate life.

In any case, there were five project teams in the Spring, and they developed: a Bomberman clone; a 2D maze; a financial calculator for expense-sharing flatmates (originally through Google Apps Scripts, then transitioned to GWT); a Reddit client for iPhone; and a point-and-click adventure game. All were able to get their applications running, although with less elegance than Fall's teams. Several of the teams incorporated appropriate logging frameworks, even if their use of these was less than ideal—but, I recognize that it's hard to understand logging before you've had to debug an application that's too big to fit in your head, so they got credit for their efforts. Unfortunately, these groups fell into the same trap as in the Fall: they added a lot of features in the second iteration, but they failed to address the comments in my three-week evaluation, resulting in many significant methodology violations. Some of the teams dropped the ball on the whitepaper, which I suspect is also because they focused on features (such as running without crashing!) rather than stepping back to ensure that requirements were met.

Tardiness and Donuts

There was one other significant difference between the Fall and the Spring semesters. I have always been offended by students who come to class late, and I decided to add a new policy to the Spring course: no late admittance without donuts for the whole class. I borrowed this from my game development studios, where we begin each meeting with an important stand-up meeting. Putting this policy in place ensures that team members show up on time. My intention for the Spring, which I explained, was that we are a learning community, a team of learners, and that coming in late is unprofessional and disrespectful. Some students immediately pushed back against this, falling back on predictable consumerist notions that they have paid to learn and will soak up what they can no matter when they show up. Indeed, I think that the group as a whole misunderstood the donuts as punitive rather than apologetic. This was exacerbated by the fact that there were some big personalities in the class who tended to drive the discussion towards dissension rather than critical analysis.

It bears repeating that the Spring class met at 8AM&mdash. It is not a time I would choose, but because of this, there was no excuse for coming in late: no previous professor could hold a student back, no class-across-campus would give them an impossible commute. Instead, all one has to do to show up on time to an 8AM class is wake up early enough. In fact, I explained to the students my belief that if you are not there five minutes early, you are already late: we begin at 8AM, which means everyone should already be seated, laptops and pens ready. 

I don't think they got it. Several students complained that my no-late-entry policy was preventing them from learning, instead of recognizing that this was a learning opportunity in itself. I still struggle with this: how do I encourage students to act professionally when the unprepared ones—the ones who really need the lessons—are the same ones least ready to accept it?

What We Learned

As part of the final exam, both groups generated a list of about a hundred things that they learned. As I have described before, this is done in two phases: first, anyone can contribute anything that they learned, and it is recorded; then, everyone is given a small number of votes to select those items that are most important. Taking the top ten percent or so gives the items that are most important to the collective learning community.

The top items for Fall were:
  • Clean code
  • Teamwork
  • Single-responsibility principle
  • Class design
The top items for Spring were:
  • Don't Repeat Yourself (DRY)
  • Clean Code
  • Build one to throw away because you're going to anyway
  • Refactoring
  • Naming conventions
  • Research before acting
Keeping in mind that we followed essentially the same course structure, it's interesting to me how these two lists compare. Clean Code shows up in both, which is to be expected since it was a major theme of the class. Indeed, many elements on both lists are principles that are upheld in Clean Code, and so it's no surprise that students would put their vote into the bigger category to capture them all. "Teamwork" only shows up in the Fall, where the teams worked very well and fruitfully together; it does not show up in the Spring, where many teams experienced difficulty coordinating their efforts. By contrast, the Spring team shows explicit reflection on process, recognizing that the first try is almost always wrong: several Spring teams had to throw one away in order to succeed, whereas the Fall teams kept patching their existing code. The Spring team also recognized what I usually articulate as the human failure mode, "Inventing rather than researching." I think this group became more aware that they had to learn in order to succeed, whereas the Fall group—with their generally higher aptitude—perhaps had already experienced this. Perhaps, in comparing the two semesters, I can hope that CS222 was able to fulfill its role in the curriculum in that both groups are prepared to do larger project-oriented work in upper-division courses. The course cannot magically grant months or years of additional experience to those with little, but it can provide them with the intellectual tools necessary to proceed. I will be eager to see how these students turn out in their senior capstone teams.

One of the students in the Spring mentioned something that I have never heard before during this final exam exercise. He tentatively raised his hand and asked whether we could put anything that we learned this semester or not. I confirmed, and he said, with some exasperation, "I need more practice!" This is a great insight, and although it did not receive many votes, I think it is one of the keenest observations a student has made in my class.

Closing Thoughts

CS222 is an enjoyable but sometimes frustrating course to teach. It is designed to build a level of competency in the students, but it suffers from highly variable inputs, as illustrated above. I am considering adding a programming assignment to the first week of class, an entrance exam of sorts that would allow me to better gauge the collective abilities of the students: if I cannot influence the prerequisite courses, at least I can get a better idea of where the students are, so I can meet them there.

I have written before about my idea to restructure the course more explicitly around essential questions. Reflecting on this past year, I think this is a good idea. In the Spring, one of the learning articulations from the final was, "A working program is not necessarily a good program." It only got three votes, but it cuts to the heart of what I want the course to teach. Perhaps by articulating this as an essential question and giving it privileged status on the course description, the Web site, and in exercises, more students will think more deeply about it. It would be a success if students could go beyond valuing Clean Code in itself to building a deeper understanding about what it means in practice and why it exists.

Speaking of Clean Code, I am generally happy with the transition to Martin's book. However, when I made the switch, I still kept my teaching approach from Effective Java: carve out bite-size pieces and put them into daily reading and analysis problems. Evidence from the six-week project shows that even very strong teams have trouble remembering these lessons and contextualizing them in their work. This may be because Clean Code tells a bigger story than Effective Java: it's not a bundle of tips so much as a way of thinking. I am considering, then, a pedagogic change: if I have the students read the whole book very early in the semester, will this give them a mental map of its contents sufficiently that we can then draw upon both the holistic sense of Clean Code and reinforce specific items that I find important? For example, if we read through the book in the first four weeks, could we then spend the next three weeks focusing on critical issues such as DRY, immutability, and SRP?

Regardless of the book being used, I have had essentially the same six-week project format since the first time teaching CS222. However, students struggle with the requirements analysis and making realistic pitches. This is partially because we do it on such a tight timeline that there is almost no time for me to reject proposals. I am considering the implications of turning the six-week project into a nine-week project, where we spend the first iteration on concepts such as user stories, risk management, and prototyping. We already do cover these, but in a separate context from the students' projects. An alternative approach would be for me to more rigorously specify what must be delivered at each milestone. In my current approach, I evaluate Milestone 1 as if it were final, but the grade does not "count" for the students; it is designed to presage the evaluation I will give on Milestone 2. I suspect that one result is that students simply don't prioritize the things that sound difficult, such as code review and a whitepaper. I could mandate that these be done, and use the old carrot-and-stick of grades to enforce it. I don't like leaning on extrinsic motivation in this way, but perhaps it would help scaffold students more effectively toward strong Milestone 2 submissions.

I enjoy teaching this course. I think that it suits my teaching style and my emphasis on reflective practice, even though the course structure, format, and even its content is a work-in-progress. The content of this course is still not well referenced by the rest of the curriculum, even those classes that have it is a prerequisite. The students, without prompting from their instructors, fall back into bad habits. For example, I saw a graduate student give a presentation on an project that, from a cursory glance at Eclipse, was rife with compiler warnings, object-oriented design flaws, and violations of coding conventions. I would like to see students emerge from CS222 not just empowered to program well, but passionate about it to the point that they see the value in practicing good programming in all of their work—whether it is graded that way or not.

2 comments:

  1. I would like to confirm the rumor of fill-in-the-blank programming assignments. And as a result I arrived in CS 121 with no knowledge of how to write a program. The first assignment to write a program from scratch would have stumped me if not for the kind and generous help of the grad student in charge of our lab time.

    As for understanding the value of writing clean code, would it be possible to give the students a terribly-written and bug-filled program that they have to refine? Working on other people's code always made me value clean code. And as a bonus they could learn debugging skills.

    ReplyDelete
    Replies
    1. I have thought about what you mention here, that students would have an easier time seeing the necessity of professional attitudes if they had to deal with large and/or bugridden code. There's a challenge in picking what they should do, though. It cannot be a project that requires a complicated build process, so that knocks out anything of significant size. I think something like Morgan's Raid is just too big: too many moving parts. The “right” size is probably the six-week projects they complete at the end of the semesters, but I feel a bit guilty about using a previous team's project as an example to future students of bad code. I also don't want to invent something “fake”. Long story short, I'm open to suggestions!

      My wife asked if EEClone might fill the bill. It was the last pure Java game that I wrote, and although it is designed to exemplify design patterns, I wrote it before have read Clean Code or TDD and incorporated those practices. I will take a closer look at that one and see if it's something they can use.

      Delete