Learning to do test-driven design requires a big shift in mindset for many developers. I was impressed with how well the students in my recent agile development course took to the idea of writing tests first. Even the most skeptical students seemed willing to go along with the group in using tests to specify the code they needed to write. Other agile practices, such as pair programming and communal development, helped to support all of the students, willing or skeptical, to move in the right direction.
My friend Steve Berczuk suggests another way to support the change in habit:
Rather than frame the testing challenge with the default being the old way of not testing:
Write a test when it makes sense.
Change your perspective to the default being to test:
Write a test unless you can explain why you did not.
I like how Steve shifts the focus onto default actions. The actions we take by default arise when our mental habits come into contact with the world. Some of my students prefer to talk about their "instincts", but the principle is the same: When things get hard -- or easy -- what will you do?
We can change our habits. We can develop our instincts. Yes, it is hard to do. However we make the change, we have to change the individual actions we take at each moment of choice.
The way to turn running into a habit is to run. When I have a run planned for a morning but wake up feeling rotten, my default has to be to run. I need to have a really good reason not to run, a reason I am willing to tell my family and running friends without shame. This is another example of using positive peer pressure to help myself act in a desired way.
There are good reasons not to run some days. However, when I am creating a new habit, I have to place the burden of proof on the old habit: Why not?
When I follow this discipline, there is a risk of overusing the technique I am learning. If my default answer is to just keep running, I will run on some mornings when I really should take a break. I may find out during the run, in which case I need to listen to my body immediately and adapt. Or I may find out later, when I see that my times from the workout were substandard or when I am sore or fatigued beyond reason later in the day. Whenever I recognize the problem, I can examine the outcome and try to learn the reason why I should not have run. This will allow me to make a sound exception to my default in the future.
The same risk comes when we try this technique while learning test-driven design or any new programming practice. I may write a test I don't have to write. I may write code that is too simple. I may need it after all. This risk is an integral part of learning. I must learn when not to do something just as much as I need to learn when to do it. The risk of running when I ought not run carries a greater potential cost than writing a test when I need not write, because physical injury may result. The only real cost of writing an unnecessary test or taking too small a step forward in my code is the time lost.
As a runner, the way I minimize the risk of injury or other significant cost I have to listen to my body. As a programmer, I still also have to listen to my code, and keep it clean through refactoring. Done steadily and faithfully, the side effect is a new habit, better instincts.
The key to Steve's suggestion is that changing practice isn't just about habit and instinct, as important as they are. It's also about attitude. There are times when my surface attitude is compliant with a picking up a new practice, but my ingrained attitude gets in the way. My conscious mind may say, "I want to learn how to do TDD", while subconsciously I react as if "I don't need to write a test here". Taking the initiative to change my default action consciously helps me to bridge the gap. I think that's why I find Steve's idea so helpful.
As department head, I teach only one course each semester, not the three that is standard for our faculty. The past academic year has been a bit unusual, though, with a load that kept me busy, busy, busy. In the fall, I taught Software Engineering, a course I had never taught before. My spring load included not only Programming Languages but also ten weeks of teaching Cobol two hours a week. I've taught Cobol many times before, but not for over fifteen years. Then, to top it all off, I taught my first May term course, an agile software dev course that met two hours every day for four weeks.
All of this was fun for the teacher in me, but no one turned down the spigot of administrative work pouring into my office. As a result, the year felt something like a treadmill. Then I spent a week digging out of a pile of undone work, eight days on vacation, and another week plus digging out of a new pile of undone work. With the delivery of faculty salary letters, I am ready to begin summer.
Fall classes begin in eight weeks -- a blink of an eye.
I love the opening of Research Through Development of Installed Tools, a short section in the conclusion of Richard Stallman's 1979 memo, EMACS: The Extensible, Customizable Display Editor:
The conventional wisdom has it that when a program intended for multiple users is to be written, specifications should be designed in advance. It this is not done, the result will be inferior. The place to try anything new is in a research project which users will not see.
Some people know better than this, but they have been silenced.
If only it were so. The section explains why incremental design was essential to the creation of Emacs:
EMACS could not have been reached by a process of careful design, because such processes arrive only at goals which are visible at the outset, and whose desirability is established on the bottom line at the outset. Neither I nor anyone else visualized an extensible editor until I had made one, nor appreciated its value until he had experienced it. EMACS exists because I felt free to make individually useful small improvements on a path whose end was not in sight.
Agile development teams also like to learn from the act of creating software, allowing goals for the software to emerge as the software grows and allowing the value of features to be assessed in the context of the overall system.
Of course, design still mattered to Stallman and the other developers of Emacs:
While there was no overall goal, each small change had a specific purpose in terms of improving the text editor in general use, and each step had to be individually well designed and reliable.
The resulting design was, according to Hal Ableson (quoted here), good enough to support a new kind of software development community: "Its structure was robust enough that you'd have people all over the world who were loosely collaborating [and] contributing to it. I don't know if that had been done before."
Agile teams use test-driven design, refactoring, and metaphor to keep the quality of their designs on track. Like the Emacs project, agile projects take advantage of real users to keep the usability of their systems on track.
Raymond talks about the value of experimentation in writing new software, relegating upfront design to implementing new versions of existing features. These new implementations can take "advantage of hindsight". I'm often pleasantly reminded just how often an experimental mindset can benefit me as a software developer, even when implementing systems in domains where I have some experience.
This is the quote of the day from my reading, drawn from a relatively old blog by Giles Bowkett:
YAGNI and "scratch your own itch" don't just keep code clean, elegant, and succinct, they also keep it honest. The worst code you will ever encounter in your career will contain program logic which does something completely different than it claims to, either in its comments or its method, variable, and object names. Programmers spend more time talking about good and evil than priests or preachers do. The reason is simple: bad code is nothing but lies.
I think beginning programmers don't often realize how many different ways that code can lie to us. Moreover, their lack of experience building large programs and living with programs over time usually means that they have no clue at all why this matters, or how important it is.
I am also surprised that so many experienced programmers seem not to grok this yet, especially the role played by doing the simplest thing you can to implement a feature. YAGNI helps us to write more honest code because it helps us to be more honest to ourselves.
All those things for which
we have no words are lost.
-- Annie Dillard, "Total Eclipse"
My family and I spent eight days on the road last week, with a couple of days in the Jamestown/Williamsburg area of Virginia and then a few days in Washington, D.C. I'd never spent more than a couple of hours in my nation's capital and enjoyed seeing the classic buildings in which our leaders work and the many monuments to past leaders and conflicts.
The Korean War Veterans Memorial caught me by surprise. No one ever talks about this memorial, much like the war itself. I had no idea what it looked liked, no expectations. When we came upon it from the rear, I became curious. Standing amid the soldiers trudging through a field, I was unnerved. They look over their shoulders, or they make eye contact with one another, or they stare ahead, blankly. This is no sterile monument of while limestone. It is alive, even as it reminds us of men who no longer are. When we reached the front of the memorial, we saw a wreath with a note of thanks from the Korean people. It brought tears to my eyes, and to my daughter's.
As touched as I was by the National Mall, most of my memories of the trip are of artwork we saw in the several galleries and gardens. I came to remember how much I like the paintings of Monet. This time, it was his "The Seine at Giverny" that gave me the most joy. I learned how much I enjoy the work of Camille Pissarro, another of the French impressionists who redefined what a painting could be and say in the 1800s. I even saw a few abstract pieces by Josef Albers, whom I quoted not long ago. That quote came back to me as I walked amid the creations of men, oblivious to computer programming and the debits and credits of department budgets. What happens happens mostly without me. Indeed.
I left Washington with a new inspiration, Hiroshi Sugimoto. My daughter and I entered one of the gallery rooms to find a bunch of canvasses filled with blacks, grays, and whites. "More modern nothingness," I thought at first. As we absorbed the images, though, one of us said out loud, "These look like pictures of the ocean. See here...?" We looked closer and saw different times of day, different clouds and fog, horizons crisp and horizons that were no more than imperceptible points on a continuum from dark ocean to light sky. Only upon leaving the room did we learn that these images were in fact seascapes. "This is modern art that works for me," said my daughter. I nodded agreement.
Sugimoto's seascapes are only one element of his work. I have many more of his images to discover.
I did not get through my eight days away without any thoughts of computer science. In the National Gallery of Art, we ran across this piece by Edward Ruscha, featured here:
I vaguely recall seeing this image many years ago in a blog post at Lemonodor, but this time it grabbed me. My inner programmer was probably feeling the itch of a few days away from the keyboard. Perhaps Ruscha has an his own inner programmer. When I did a Google Image search to find the link above, I found that he had also created works from the words 'self' and 'ruby'. We programmers can make our own art using Lisp, Self, and Ruby. Our art, like that of Monet, Pissarro, Sugimoto, and Ruscha, sustains us.
Last time I mentioned that students in my agile software development course found several of the reading assignments to be valuable. For what it's worth, here is at least a relatively complete list of the readings I assigned in the course of four weeks, in no particular order.
When I still thought we might use a distributed version control system, I asked students to read Hg Init and then a few items on git, including Git for the Lazy, the official git tutorial man page, and Everyday Git. Then, when I decided to show discretion in at least one part of the project and use centralized version control, I asked the class to read several items on Subversion
I'm under no illusion that every student read every page of every reading. This is an ambitious list for agile beginners to tackle in four weeks while also working on a big project. Still, it's clear from discussion that many students read a lot of this material, had questions, and even had comments for me.
As always, I'd love to hear from you any comments you have about this list, or any additions you can suggest for future offerings.
Today I finished reviewing materials from my agile software development course so that I could assign final grades. The last thing that students wrote for me was a short evaluation of the course and some of the things we did. Their comments are definitely valuable to me as I think about offering the course again.
What worked best this semester? I was surprised that the class was nearly unanimous in saying pair programming. Their reasons were varied but consistent. By pairing, they faced less down time because one of the two developers usually had an idea for how to proceed. Talking about the roadblock helped them get around it. Several students commented that programming was more fun when working with someone. One student said that he "learned how to explain things better". I imagine that part of this was that, as the team came to build up trust and respect for each other, he wanted to be more patient and helpful when responding to questions. Another part was probably simply practice; pair programming increases communication bandwidth.
The students who did not answer "pair programming" said "learning Ruby". I was hopeful that students would enjoy Ruby and pick it up quickly. But my hope was not grounded in much empirical evidence, so I was a little worried. The language was no worse than a break even proposition for some students, and it was a big win for most. And as a result I had more fun!
I asked students, Who were the best partners you worked with? The best part of these answers is that they were not dominated by a few names, as I thought they might be. There was a pretty good spread of names listed, with only a couple of students mentioned repeatedly. I take this to mean that many students contributed relatively well to the experience of their teammates, which is the sort of horizontal distribution of knowledge that is desired for XP teams. I did note an interesting distinction made by one student between the partner I learned the most from and the partner with whom I felt the most productive.
Which of the assigned readings was most valuable? I include a list of most of the readings I assigned over the course of the four weeks. Of those, two were identified most frequently by students as being valuable: Bill Wake's The Test-First Stoplight, which was assigned early in the semester to give students a sense of the rhythm of test-driven design before diving in as a team, and What is Software Design? by Jack Reeves, which was assigned late in the semester after the team had worked collaboratively for a couple of weeks on a system that had no upfront design and very little documentation. In class discussion, a couple of students disagreed with a few of Reeves's points, but even in those cases the paper engaged and challenged the reader. That's about all I can ask from a paper.
When asked, What did you learn best in the courses?, the answers fell into roughly two groups: TDD and the value of tests and Ruby and OOP. The flip side is that many students also said, "I wish I could have learned more!" Again, that's about all I can ask from any course, that it leave students both happy to have learned something and eager to learn more.
I don't mind that Ruby and object-oriented programming were the prized learning outcomes of a course on agile software development. A couple of times during the course I noted that a good project course is by its nature about everything. We can design courses in neat little bundles, but the work of making anything -- and perhaps especially software -- is a tangled weave of knowledge and discipline and habit that comes together in the act of creating something real. If Ruby and OOP were what some students most needed to learn in May, I'm glad that's what they learned. I will trust that the agile ideas will take root when they are most Needed.
How could I improve the course? This is always a tough question to ask students in an named setting, because no matter how much they might trust me I know that some will be reluctant to say what they really think. Still, I received some answers that will help me design the next iteration of this course. Several times expressed a desire for more time -- to write tests, to pair program, to work on the system outside of class, to practice refactoring, .... It seems that desire for more time is a constant in human experience. (At least they weren't so tired of the course that they all said, "Good riddance!"!) Clearly, there is a trade-off between a four-week semester focused on one course and a fifteen-week semester given over to four or five courses. The loss of absolute number of hours available is a cost of the former. I'll have to think about whether that cost is outweighed by its benefits.
One of the more mature and experienced team members offered an interesting comment: Having an instructor who is a programmer act as client and explain the project requirements to the developers affected a lot of things. He wondered what it would be like to have a non-technical client with the CS instructor acting solely as agile coach. An insightful observation and question!
All in all, this was valuable feedback. The students came through again, as they did throughout the course.
If you are a university professor, read this short piece by John Cook and try not to think about teaching and educational research! It doesn't take long for a CS professor interested in thinking about his teaching methods scientifically to bump into the seeming double standard that Mosteller identifies between practicing doctors and medical researchers. Wanna teach object-oriented programming beginning on Day 1 of CS 1? No problem. Wanna collect some data and compare the results to results from a control group? Hold on there... You'll need to fill out a few forms, get permission from a review board, and make sure your research methodology satisfies the scrutiny of colleagues from across campus.
I understand Cook's reasoning that review boards add necessary value to the enterprise of medical research, and I suppose they add some of the same value to educational research. But many of the things we try in our classrooms are more experimental than routine. Even with grounding in educational psychology, a lot of our teaching is done under conditions we don't understand completely and with techniques that are outside the range of past education research.
Formally vetting education research does achieve one worthwhile goal: It reduces the chances that a poorly conceived experiment can be used to draw broad conclusions beyond its reach. On the other end of the spectrum, though, it likely reduces the number of formal experiments that are done, which contributes to a culture of sharing anecdotal results and "Look what I did..." experience reports. When that happens, you have to study the community closely to learn whose results to pay attention to and learn from. Vetting informal experiments becomes an informal process.
After going through a couple of rounds of trying to get teaching experiments approved early in my career, I have even greater admiration and appreciation for the work people like Mark Guzdial do. We educators need educators to do formal experiments and to report the results. We also need to learn as much about psychology (of all sorts, including behavioral), biology, and even sociology if we hope to teach more effectively, more reliably.
I promise I won't try to learn anything when I teach compilers this fall. If I do, though, I may tell people about it.
Recently I shared my fixation with a tweet from Kevin Rutherford about replacing unnecessary weekly meetings with ad-hoc stand-up meetings and a status wall. I have been imagining how I might do something similar with faculty meetings. Most CS faculty prefer to work on the courses, their research, and their outreach activities than to go to business meetings. Perhaps there is a better way.
Not all meetings are created equal. What sort of meetings are there, and which could be replaced with other mechanisms? I like Keith Ray's taxonomy from a message to the XP list nearly a year ago (June 15, 2009):
The first two types are the kind of meetings worth holding. Sure, some decisions can be made by e-mail or some other technology, with a little straightforward discussion followed by the sending of votes. We can also share a lot of information, using e-mail, wikis, and various collaboration tools. Whenever I can, I ask the faculty to make decisions via e-mail. This works well for decisions with a narrow range of well-understood alternatives, where the decision does not require the group to find any new common ground. I also try to disseminate as much information from outside the department as I can via e-mail, mostly where the new information isn't likely to lead to a wider discussion that we might want or need to have. Finally, I try to collect as much information from the faculty as I can without holding meetings. Sometimes this data can be assembled and either used by me or passed on to outside agents without need for us to meet.
But even agile software developers recognize the need for meetings to make decisions and to gather or disperse information. We hold collaborative meetings for tasks such as release planning and iteration planning, in which we make decisions about the work we want and need to do in the short term. We hold short daily stand-ups meetings and conduct periodic retrospectives of our work, in which we share information about our progress and gather communal information that helps us to improve how we work individually and as a team.
All of these meetings are valuable, even necessary, and sometimes enjoyable. The key distinction between these meetings and the meetings that people usually complain about is that they are collaborative, with everyone participating in the conversation, with decisions actually being made (not just handed down from above), and with everyone's learning being fed back into the system to make it work better.
I do my best to limit most of the meetings I call to these two categories, and try never to call a meeting with no decisions, no information to share that requires discussion, and no collaboration. I am pretty confident that our meetings aren't legacies that originally had a purpose but now only fill a time slot. In fact, we don't meet weekly any more because we all could see that there was not enough business to keep us busy every week. Of course, if we all got excited about long-term planning, we could meet and talk every week but that is not the nature of our faculty.
All that said, I still wonder whether we could be more productive and happier with something like what Rutherford describes. Focus whenever possible on small, frequent releases, and track progress in as visible and obvious a way as possible. Meet briefly and collaboratively when maximum value comes from meeting, not not meeting.
There are limits to how far one can take this approach in a large bureaucracy like a university. Mandates come from above that require collective discussion and decision, even when we in the department have little interest in the issue, perhaps even an active disinterest. But that should not distract us from doing the best we can with what we do control. As legendary teacher and coach John Wooden used to say, there is no point in worrying about what you don't control; that only steals energy and time from accomplishing as much as you can with what you do control. So, we should try to be as agile as we can!
The mafioso over at PLT have done it. They have renamed their suite of languages, editors, tools, and teaching materials from the collective "PLT Scheme" to Racket. As they explain on their web site, the Scheme part of the old name had grown increasingly less useful for identifying their work and the languages they had created. As a result, they had to continually distinguish their work from the work of the mainstream Scheme community, which was similarly disadvantaged by having the PLT community's work consume much of the mindshare around the name Scheme. A name change solves both problems... with the new cost of having to re-brand PLT Scheme, Dr. Scheme, Mr. Ed, mzscheme, and the rest of their suite.
This is the first time I have ever seen a programming language community try to rebrand its language, so this will be fun. I suspect the PLT folks will be just fine. They are both great researchers and tool builders, and they are remarkably energetic in disseminating their ideas. I don't think there is any danger they will lose many of their current devotees, if any. The real question is whether the new name will make it easier or harder to bring new users on board. Because "Racket is a Scheme" and Racket is not called Scheme, I figure the name change is at worst a wash and at best a perceptive move. Over the next couple of years, we'll see which is true.
If nothing else, this is likely to force me out of my holding pattern with PLT Scheme 4.2.0 and into the modern era of PLT, er, Racket 5.0. The #lang construct is such a nice solution to several different issues in creating languages and building large systems. Now I'll have to try it out for real! I'll also have to convert all of the code for my programming languages course, which will give me a little more incentive to make some larger changes in what I teach in the course and how I teach it. That's good news, too -- an extra shot of energy.
Early this morning, @kevinrutherford tweeted:
Permanently replaced a 10-person 1-hour weekly meeting with an ad-hoc stand-up and a status wall #win
I couldn't get this message out of my mind all day. On a very small scale, daily stand-up meetings did just what they needed to do in my recent agile development course: keep team members apprised of what everyone was accomplishing and, occasionally, what stood in peoples' way. A story board and a somewhat lame burndown chart put the daily reports in context. I can see how this approach works so well with agile teams, especially ones with open, steady communication channels and a lot of trust.
Many people like to group meetings into categories, but my experience as university department head says that, when it comes to meetings, there are also two kinds of people: those who like them and get things done via them, and those who don't. My snarkiest faculty colleagues and software developer friends will label these groups "those who waste time" and "those who get real work done", but that's not fair. I've had to participate in a lot of meetings on campus over the last five years, with faculty, staff, and administrators at various levels of the organization chart. I've had to chair my my fair share as well. I bump into many of the same people over and over again on different committees, and it's clear that many of those people have found ways to make such meetings productive, even helpful checkpoints in the doing of real work. Some even enjoy the meetings.
How can this be? Are my snarky colleagues right, and the people who enjoy meetings are simply killing time that could be spent better elsewhere? I don't think so. Different people in the organization are assigned different tasks. When the task requires the synthesis of an understanding or a plan that will be held in common across a large organization, it requires collaboration. When it requires compromise among strongly-held competing viewpoints of the world, it requires conversation -- and sometimes lots of it. I have found that it is a good thing that different people in the organization have different working styles, because without them a lot of this sort of work would never get done. In the end, this comes down to more than different working styles. Different people have different values, and different ways they hope to contribute to the group's success.
Paul Graham commented in one of his essays that meetings are easy work compared to the challenge of making things. As a creator of programs and a writer of blog entries, I know what he means. I rarely exercise my intellect in a meeting the way I exercise it while trying to figure out if the accounts in my bookkeeping software should be composite objects, or if composite debit/credit values will suffice. But sometimes a meeting can prepare me to tackle a tough writing task, such as drafting a report to the president that conveys a strong plan for moving his IT operation forward. When a task falls inside the bounds of a group's common values and beliefs, meetings often are not necessary. When they fall outside the intersection, people need to talk to one another. The conversation is itself part of the work that needs to be done.
The reason Rutherford's tweet won't leave me alone is that I keep fantasizing what it would be like to replace all or even some of my department's faculty meetings with something more agile. Our nine-person faculty usually meets bi-weekly for an hour or so. Most everyone grumbles at having to meet, even when we have decisions to make. If we could replace even half our meetings with an occasional ad-hoc stand-up meeting and a status wall that kept us all on track doing what we needed to do, what a glorious #win that be!
Now, to figure out which meetings to replace, and how...
Teaching a 3-credit semester course in one month feels like running on a treadmill that speeds up every morning but never stops. Then the course is over, almost without warning. It reminds me a little bit of how the Christmas season felt to me when I was a child. (I should be honest with myself. That's still how the Christmas season feels to me, because I am still a kid.)
I've blogged about the course only twice since we dove head-long into software development, on TDD and incremental design and on the rare pleasure of pair programming with students. Today, we wrapped up the second of two week-long iterations. Here are the numbers, after we originally estimated 60 units of work for the ideal week:
I was reasonably happy with the amount of software the team was able to deliver, given all the factors at play, among them never having done XP beyond single practices in small exercises, never having worked together before, learning a new programming language, work schedules that hampered the developers' ability to pair program outside our scheduled class time, and working a domain far beyond most of their experiences.
And that doesn't even mention what was perhaps the team's biggest obstacle: me. I had never coached an undergrad team in such an intense, focused setting before. In three weeks, I learned a lot about how to write better stories for a student team and how to coach students better when they ran into problems in the trenches. I hope that I would do a much better job as coach if we were to start working on a second release on Monday. As my good friend Joe Bergin told me in e-mail today, "Just being agile."
We did short retrospectives at the end of both iterations, with the second melting into a retrospective on the project as a whole. In general, the students seemed satisfied with the progress they made in each iteration, even when they still felt uncomfortable with some of the P practices (or remained downright skeptical). Most thought that the foundation practices -- story selection, pair programming, test-first programming, and continuous integration -- worked well in both iterations.
When asked, "What could we improve?", many students gave the same answers, because they recognized we were all still learning. After the first iteration, several team members were still uncomfortable with no Big Design Up Front (BDUF), and a couple thought that we might have avoided needing to devote a day to refactoring if only we had done more design at the beginning. I was skeptical, though, and said so. If we had tried to design the system at the beginning of the project, knowing what we knew then, would we have had as good and as complete a system as we had at the end of the iteration? No way. We learned a lot building our first week's system, and it prepared us for the design we did while refactoring. I could be wrong, but I don't think so.
Most of the developers agreed that the team could be more productive if they were not required to do all of their programming in pairs. With a little guidance from me as the coach, the team decided to loosen the restriction on pairing as follows:
After the second iteration, the team was happy with this adaptation. Only three of the ten developers had worked alone during the iteration, all doing work well within the letter of the new rules and, even more important, well within the spirit of the new rules, too. The rest of the team was happy with the story wrap-up, the refactoring, and the experimentation that had been done. This seemed like a nice win for the group, as it was the one chance to adapt the XP practices to its own needs, and the modification worked well for them. Just being agile!
As part of the course retrospective, I asked the students whether they would have preferred working in a domain they understood better, perhaps allowing them to focus better on the new practices and new programming language. Here are the notes I had made for myself before class, to be shared after they had a chance to answer:
My thoughts on the domain:
I think it was essential that we work in a domain that pushed you out of your comfort zone.
- It is hard enough to break habits at all, let alone working on problem you already understand well -- or think you do.
- The benefits of agile approaches come in helping the team to learn and to incorporate that learning into the system.
- Not knowing the domain forced you to ask lots of questions. That's how real projects work. That's also the best way to work on any system, even ones you think you already understand.
- There is a realness to reality. Choices matter. When the user is a real person and is passionate about the product, choices matter.
I was so impressed with the answers the students gave. They covered nearly all of my points, sometimes better than I did. One student identified the trade-off between working in familiar and unfamiliar domains. Another student pointed out that not knowing the domain made the team slow down and think, which helped them design better tests and code. Yet another remarked that there probably was no domain that they all knew equally well anyway. The comment that struck me as most insightful was, roughly, "If we worked in a domain we all understand, then we would probably all understand it differently." That captures the problem of requirements analysis as well as anything I've ever read in a software engineering textbook.
It occurred to me while writing this that I should share the list of readings I asked students to study. It's nothing special, papers most people know about about, but it might be a subset of all possible readings that others might find useful. Sharing the list will also make it possible for you to help me make the it better for the next time I offer the course! I'll gather up all of my links and post the list soon.
A few days ago, alumnus Wade Arnold tweeted:
Sad to see May graduates in Computer Science applying to do website design and updates. Seriously where did the art of programming go?
The small and largely disconnected programming problems that we assign students in most courses may engage some students in the joy of programming, but I suspect that these problems do not engage enough students deeply enough. I remain convinced that courses like this one, with a real problem explored more deeply and more broadly, with the student developers more in control of what they do and how, is one of the few things we can do in a traditional university setting to help students grok what software development is all about -- and why it can satisfy in ways that other activities often cannot.
Next up on the teaching front: the compilers course. But first, I turn my thoughts to sustainable pace and look forward to breathing free for a few days.
When my student team began the second iteration of our project this morning, one member had no partner. So I jumped in to fill the void for half an hour or so. When the missing student arrived, he joined us and we worked as a trio.
What a treat! Most of the time, I code alone, and far too often the only code I work closely with is code I have written. Working with another programmer was a lot of fun. We talked as we studied and tried our ideas about how the code worked out on one another. Studying someone's code added a second dimension to the fun, because neither of us brought much experience with this part of the system to our pairing session. That meant real study, and real learning.
Our programming session reminded me just how valuable tests can be. We bounced back and forth between the class we needed to extend and its tests. We would study the code, come up with an idea of how it worked, and then inspected the tests to confirm or disconfirm our idea. At one point, we were studying a particularly confusing method. We finally figured out what it was doing and went to the tests to check our understanding. But there was no test to help us... and there should have been. So we wrote a test that embodied what we thought should happen, ran it, and -- voilé -- it passed. That felt good.
The story my partner and I picked out turned out to be effectively solved by the existing code. Rather than taking the easy way out, mark the story as done, and grab another story card, we decided to clean up the code a bit. We were a bit disturbed at having to study that confusing method so long and generally at having to work so hard to understand the class as a whole. So we refactored the class to express the code's intent more clearly. The biggest product of our clean-up was a helper class to structure the parts of a journal entry and name them. This meant taking a horizontal slice of data that was originally sliced vertically. This clarified several of the internal interfaces and even simplified one hairy loop with an if statement that selected on value type into two simpler loops. Ahh.
Is the new code better than the previous version? We think so, but we won't know until the next developers to touch it, whether others our ourselves, bump into it again. I will say that it is at least more explicit. The intention of the code shows up in the names of local variables and formal parameters; it shows up in blocks that send messages to objects of the same kind. It shows up in tests that make finer-grained assertions about the behavior of the system. That feels like an improvement to me and, hopefully, my partners. We'll know more soon enough.
I would love to have more chances like the one I had today.