How to Study for This Course
Chapter structure
Every chapter has two sibling chapters: a review chapter and a practice chapter. The main chapter teaches the material. The review chapter is a set of short questions that test whether you can recall and apply what the chapter taught. The practice chapter is a set of problems that ask you to apply the chapter’s ideas in a new context.
The main chapter and the practice chapter each come with starter code and solution code you can download (there are a few exceptions, chapters that cover purely theoretical material with no code to write). The starter code has the missing pieces for you to implement; the solution code is a reference implementation.
The order to work through a chapter
I recommend working through each chapter in this order:
- Read the lecture notes. This is the main chapter.
- Download the starter code, and try to implement the missing pieces yourself, using the notes as a guide.
- You can also download the solution code as a reference, and revisit the lecture notes if something is not clicking.
- Work through the review questions.
- Work through the practice problems.
How to use the review chapter
Every review chapter opens with this advice:
These questions cover the chapter’s material. Try each one on your own before opening the sample answer. Compare your answer to the sample. If they line up, move on; if they do not, or the sample answer is not clear, it may help to revisit the chapter notes.
Each review question’s solution is hidden behind a click, on purpose. Effortfully trying to answer a question from memory, even when you are not sure, builds a stronger and longer-lasting memory than reading the answer straight away. This is called active recall, and the practice of testing yourself this way is called retrieval practice. It is one of the most well-established findings in the science of learning: the act of retrieving information strengthens your memory of it far more than passively re-reading it does.
So when you hit a review question and do not know the answer, it is still better to spend real effort trying to recall or work it out yourself than to read the solution right away. The solution is there to help you check your own answer against a canonical one, not to be your first move.
How to use the practice chapter
Every practice chapter opens with this advice:
These problems take the ideas and techniques you learned in the chapter and apply them to new contexts. It is best to attempt these after reading the chapter and feeling confident with the material. For each problem, write your own solution before reading the provided one. Give yourself more time to work through the challenge.
This is a different kind of learning than the review chapter, and it deserves a different kind of solution. A practice solution reads more like a tutor working through the problem with you: it often starts from an incorrect or inefficient approach and builds up from there to the intended solution, the same way you would if you worked through it yourself.
Generally speaking, it is better to attempt the problem yourself before reading the solution, even if you are not confident you will get it right. This is the difference between generative learning and passive learning. In generative learning, you generate a solution yourself, from scratch, before seeing the answer. Problem solving is a skill, and skills are built by doing, not by memorizing. Generating an answer yourself, even a wrong one, leads to better learning than reading the correct answer directly.
How long to struggle before checking a solution
There is a popular saying, misattributed to Einstein: “It’s not that I’m so smart, it’s just that I stay with problems longer.” Einstein did not say it, but the quote points at something real about how hard problems get solved: through patience and sustained effort. Researchers who make real discoveries rarely do so overnight; they often spend months or years on a single stubborn question. The problems you will face in this course are not open research problems, they are well-known problems with well-known solutions, but the mental discipline you build by staying with them before looking up the answer is the same discipline researchers rely on. Struggling with a hard problem before you see the answer builds the debugging intuition and the persistence you need to become a good problem solver.
When you rush to the solution, you skip the most useful part of learning: the struggle itself. Most of the growth happens in the space between not understanding a problem and eventually working it out on your own. Knowing exactly how long to persist before you look for help is not something a single rule can settle. It depends on your own goals, how much time you can spend, and how you are doing on that particular problem. Here are some practical guidelines:
- Set a minimum timer. Five minutes is almost certainly too little. I would commit to somewhere between 15 and 45 minutes, depending on whether I am making progress.
- Refine incrementally. Start with a small input before you generalize. Start with a brute-force solution before you make it efficient. The solutions in this coursebook often work the same way.
- Watch for the illusion of competence. If you do end up reading the solution, that is fine, but be aware that reading a solved problem can trick you into thinking you know how to do it, when you would not have been able to produce it yourself. I use a few techniques to counter this:
- I might use the Feynman Technique: explain the concept out loud, in simple words, as if I were teaching it to a beginner.
- I might try to solve a variant of the problem: change it a little (if it asked for the minimum, find the maximum instead) and solve that version without looking at the solution.
- I would take a break of days, not hours, and come back to the original problem later. Then I try to solve it again without looking at the solution. At that point, I am doing retrieval practice, not just problem-solving.
Using AI
We know from surveying students that the first thing many of you turn to, whether you have a question or you run into a problem, is AI. AI can help you learn, but it is a slippery slope.
Let’s take a concrete example. Suppose you open the starter code for a practice problem and are about to start working. Your IDE is not working correctly: it does not recognize the project’s files or dependencies, and it will not run the code. This is a real obstacle, but it has nothing to do with what the chapter is trying to teach you. The learning outcome of the course is not about getting your IDE configured. This kind of difficulty is extraneous to the task, and struggling with it does not help your learning. This is exactly the kind of case where I highly recommend using AI. You would want to get past obstacles like this as soon as you can, and AI is probably even better at this than your instructor or your TAs. So by all means, use AI to remove this kind of unproductive struggle.
In contrast, there is productive struggle, the kind that does contribute to learning. Solving a problem takes mental effort, and that effort, because it is intrinsic to the task, is a desirable difficulty. Now consider a spectrum: on one end, you do all the cognitive work yourself; on the other, you outsource that work to someone or something else. You cannot always do the entire cognitive work on your own, especially when you are learning something for the first time, and sometimes you need help. But broadly speaking, the closer you stay to doing it yourself, the more you get out of the experience. The more you outsource, the less you learn and the weaker the skill you end up building.
Education has a concept called scaffolding: the support given to a learner, especially when a concept is new. Good scaffolding gives the learner the minimum help they need and then lets them do the rest on their own. Just as important, good scaffolding fades: the support is gradually withdrawn so the learner builds confidence and independence and eventually does the task unaided. Skip the fading, and you get overscaffolding, giving so much assistance that you cause harmful cognitive offloading without meaning to. Cognitive work is the mental effort it takes to do something; cognitive offloading is outsourcing that effort to something else. Offload too much of it, especially the parts that matter, and you weaken your own learning.
AI is designed to be a helpful assistant. It was not designed to be an effective tutor. A good tutor gives you the minimum help you need and makes you do the rest. AI lets you skip that far too easily. This is the slippery slope: when you rely on AI for help, you may think you are still close to the “doing the cognitive work yourself” end of the spectrum, when you are actually much closer to the other end.
I would recommend erring on the side of caution and minimizing your use of AI. Many generations of students have learned without it, and they turned out fine. If you do use AI for help, use Socratic prompting: tell it not to give you the final answer, and ask it for a small hint or a guiding question instead. Try the next step yourself before asking for more. When you hit a bug, ask AI to explain why the bug happened, not just how to fix it. Done well, this can be a genuinely useful middle ground before you give up and look at the provided solution.
Is not “minimize AI” out of step with how the industry actually works?
I think it helps to see the difference between product and process. At work, when your employer asks you to implement something, they care about the product: the thing that gets built. At university, when I ask you to do the same, I care about the process. When I give you a programming problem, it is not because I need the solution. I already have it. I give it to you because the process of solving it, the effort and growth it takes to arrive at the solution, is where the learning happens.
Underneath this is a conflict between two different ideas of progress. At work, progress usually means faster, easier, cheaper, more automatic. In learning, progress usually means the opposite: slowness, effort, memory, feedback, and being willing to get confused and correct yourself. AI is very good at the first kind of progress, which is exactly why it is so useful at work. But it can undermine the second kind, and the second kind is exactly what you need while you are still learning.
Do I even need this material if AI can write the code?
Here I want to be honest and candid rather than assertive, because I do not know what foundational skills will remain essential as AI keeps improving.
At the time of writing, the reasonable position still seems to be that AI has not replaced all cognitive work, and that the arrangement we actually want is for AI to do the work where it can, with a human still in the loop to direct and audit that work. Directing and auditing AI’s work, as a computer scientist, requires the same problem-solving foundations the field has always needed, AI or no AI. At least, that seems to be the case for now. On that basis, my best judgment is to keep teaching you the same material we taught computer science students before AI entered the picture.
Pacing yourself
How you schedule all of this (notes, starter code, review, practice) for a given chapter is up to you. My recommendation is to not do it all in one sitting. It is fine to do so, but a better strategy is to space these steps out over a few days: work through part of it, let some time pass, allow yourself to forget a little, and then come back and recall what you learned. This is the same idea behind retrieval practice, applied to your whole study schedule instead of just one set of questions. Spacing out your study, with some forgetting in between, produces stronger long-term learning than cramming it all at once.
That said, it is best not to let the material sit too long. Each chapter’s coursebook notes are released at about the same time as the lecture that introduces it. It is best that you read the notes within 24 hours of that lecture, and ideally, you finish the rest (the starter code, review, and practice) within a week of that lecture. You should try to keep up with this pace, because every chapter builds on the ones before it, and falling behind on one chapter makes the next one harder to follow.