What I did:
As a part of my software engineering course for my Graduate Unschool project, I completed the fall 2005 version of MIT’s 6.042j course, Mathematics for Computer Science. This means I read all of the course materials (textbook/lecture notes/solved problems and examples), successfully solved every problem of every homework assignment (equivalent to a perfect homework score), and synthesized the course into it’s essential elements, shown below. All of my completed solutions and my course synthesis can be found here. I’ve also included a few examples of my solutions so you can see what types of problems I solved, also below. If you want to read more about the learning process, scroll past the examples of my work.
The foundational knowledge and tools I learned/used while taking 6.042j.

Examples of my work

part 1

part 2
How I did it:
The learning method I’ve paired with the software engineering course is essentially an evolution of the learning method I developed and used in college. It is heavily based off of the core concept of Deliberate (or possibly Directed in my case) Practice, and I am evolving it by trying new techniques I found from Scott H. Young’s learning methods, more literature on Deliberate Practice, and other experimental tweaks. Below is an overview of the phases I broke the course into. These phases were completed in discrete chunks:
- Coverage (5%)
- I speed read all of the textbooks, and course notes (I try to do this in ~5 hours).
- Practice (80%)
- I make each individual problem set into an adaptive set. I then attempt each problem as if it were a normal homework assignment (using the PDF version of the book and in-class examples heavily for references) and make my best attempt at a solution. After completing my solution attempt, I immediately compare it to the official course solutions. If my solution is correct, I cross the problem off my adaptive set and move to the next unsolved problem. If my solution is incorrect, I compare my attempt with the solution, identify exactly where I went wrong, circle the problem, and move forward to the next unsolved problem. When I work my way to the end of the problem set (I call this a run-through), I start again on the problem set, this time going through the circled problems in order (being careful to not attempt the same problem in the same 24 hour period to avoid memorization). I continue to run through the problem set until I have crossed off every problem, then I group my solutions together and move on to the next problem set.
- Synthesis (15%)
- I identify every major concept and problem solving tool I used while completing the problem sets and put them in a list. I prune the list so that it only contains essential information, but I also ensure that nothing essential is missing so that the list spans the course (think of it as a basis for the course). I then write this into a one-page synthesis sheet so that the entire course’s information is organized in one place (this is useful so others can learn the course faster in the future, so you can refresh yourself quickly, and it could be used as a crib sheet).
There’s a lot more that goes into finishing an MIT computer science course in your free time than a just a learning method, and how I finished this course is very different than how I started because I constantly experimented with my approach and integrated what worked into my habits. I started in September 2015 after successfully completing MIT courses 6.01 and 6.02 back to back in two weeks each, with a huge plan for how I would complete all of the MIT courses on my list in just six months. I decided to experiment with taking courses simultaneously because it was what I was familiar with (traditional school), and I thought the spacing and connections would help. I’m glad I tried that because I realized that I was very wrong.
Attempting two courses at once divided my focus, and pushed the finish line further back (it should take twice as long to finish two courses if completed simultaneously) which made it more difficult to make progress because I’d experience decision fatigue when deciding what task I was going to attempt each day, and it made what was once a bite-sized achievable short term goal, completing one course, into an overwhelming project, causing me to procrastinate, fall behind, feel guilty, and become avoidant of my work.
Eventually I stopped working on Graduate Unschool completely. I went from regularly spending 6 hours a day 6 days a week making serious progress to going months without programming, forgetting where I stood in each course, and asking myself if I was ever going to follow through on my original intentions. Sporadically I’d have short intense spurts where I’d stay up all night making a new plan and plugging away at problems, but that energy would wear off, and I’d go back to my intellectual drought of pretending Graduate Unschool either didn’t exist or was a relic of my naive past.
Then one day, somewhere between a financial rock bottom and a personal career renaissance I reconnected myself with the original intentions of the Software Engineering Course. I found myself easily slipping into flow states while programming, loving it again, and wanting to get better and pursue a career in developing software, and although I didn’t have a formal education in computer science, I believed that this would fill in the gaps in my knowledge and signal to employers that I truly did “know my stuff”. I started ramping up my skills again, challenging myself everyday, and I eventually found a job as a software developer where I have been working for around seven months now. Just as I’d hoped, my job had me programming everyday, and consistently learning all sides of software development. My secondary intentions for the software course had been fulfilled, and I hadn’t even made it very far through my personal plan. I realized that it was very likely that I could continue to have a career in software without ever completing another MIT problem set or practice interview question. I also realized that I cared deeply about developing hard skills, I loved programming, and more importantly pursuing excellence and improving my programming skills as I started developing software full time. Through some introspection I found that I truly enjoyed the MIT Computer Science courses, and that completing them was important to me even after I’d successfully completed the career transition I originally thought the courses would help me with. With purified intentions, and a serious break from any disciplined self-learning routine, I made a little bit of progress everyday, built some healthy habits, finished this course, and most importantly gained insights into the learning process. My synthesis sheet clearly shows what I learned about math while taking 6.042j. Here’s what I learned about the learning process itself.
What I learned about learning:
- Attempting two courses or topics at once divides your focus and slows progress and motivation for both. It is faster and better to start one thing, focus on it, finish it, and move forward.
- Setting up a feedback system where you can see your progress everyday is extremely helpful and motivating. Including a mechanism where your progress measures and acknowledges both efforts and results seriously accelerates your learning. You’ll know exactly what to do when starting everyday, where to go, and when something is difficult and takes multiple attempts before you make any tangible results progress, you’re rewarding the important part of learning a difficult concept, the unseen internal progress that can only be made from serious attempts (both successful and unsuccessful).
- Avoid large gaps away from your work. Taking a ~ 1 year gap in the middle of this course seriously set me back. It took tens of hours to figure out exactly where I had left off, what work I had already completed, and what remained. Even after straightening all of that out I had to complete a serious amount of redundant work, either because I’d lost a problem, or because I didn’t recognize a solution until I had already redone it.
- Make frequent progress, however small. My recommendation is to improve your desired skill 1% everyday.
- Increment the intensity of your approach to avoid overwhelming yourself and creating a system you’re likely to quit or burn out in. My recommendation is to start by completing one pomodoro (25 minute chunk of uninterrupted work) each day at the same time (since consistency matters), so it becomes a part of your lifestyle. At first you will feel resistance, but once it becomes a habit (meaning it no longer takes willpower to complete your pomodoro), you can consider adding another pomodoro, and repeating the habit incrementing process. If, after completing your regular pomodoros, you want to continue, feel free to keep working, but it’s important to realize that 25 minutes today and 25 minutes tomorrow will serve you better in the long term than 5 hours today and feeling too exhausted to start tomorrow.
- Stream of consciousness work journaling helps your progress feel more meaningful, keeps you focused, and gives you a catalogue of your actions for you to analyze and learn from in the future. At the beginning of every session I write the date, every action I’m taking as I take it, and often what’s going through my head and what I’m feeling as I work. I keep my journaling short, personal, honest, and quick (~20 words per hour, I spend about a minute an hour total on the journal, so it’s not distracting. Instead I note my distractions in the journal so that I can move forward with my work.)
-
Here’s an example of my stream of consciousness journal.
-
- Document your work and keep it well organized.
- Here’s my gitHub, where I have a directory for the work I’m currently working on, and my completed solutions.
-
Here are all of my completed 6.042j solutions in my Graduate Unschool binder. Notice how thick this is!
- Close your loops. I’ve already recognized the importance of keeping the phases of each course discreet, and in the process of completing this course I’ve realized it’s important to keep the courses discrete as well. This means finish one task before moving on to the next one. The only reason I’m typing out this synthesis (and not avoiding it like I did for the first two software engineering courses), is because I forced myself to complete all phases of 6.042j before continuing on 6.006. Once you’re finished with one task it clears up space in your mind that you can use to fully focus on the next task.
- Give yourself credit for the work that you do. If someone want to visualize what I learned while completing 6.042j I can send them this page. Find a way to demonstrate your skills so you can benefit from your work.
- Build your skills through iteration. You’ll be intimidated when approaching new things. Throw perfectionism out the window, finish something quickly, stop avoiding it, don’t fall behind on any of the pieces, get it out there, get feedback on how you can improve a few specific pieces of it (but again, just upgrade it, make it just a little better than your last one, don’t try to accomplish too much in a single iteration), and make it a little better next time. This is how you get better at anything. It takes patience and humility.
- Break very difficult problems into digestible chunks. Sometimes concepts are so complicated you won’t be able to successfully complete it in your first attempt, and you won’t be able to even completely understand or internalize the solution on your first attempt (meaning you’ll get your second attempt wrong as well). Find your saturation point, find a piece of the solution that you can easily understand today, maybe one concept, maybe one problem solving mechanism, and briefly focus on that while analyzing the solution. Quickly write how it works, then when you reattempt the problem later, start by focusing on what you’ve internalized in this process. Now that you’ve chunked out a piece of the solution, the remainder will be less intimidating. Try the now truncated problem again, and you’ll be surprised how quickly you progress. Learn the problem, solution, and problem solving mechanisms in smaller pieces so you spend less time feeling stuck.
- How to attempt problems you know you are unlikely to succeed at: Give it your best attempt. Don’t take too long (pomodoros come in handy for this). Make sure it’s a complete solution that can be turned in and be graded (you want to know where you’re going wrong, and why your assumptions were wrong). Don’t leave anything out that was asked for. Set yourself up for success, even if it is unlikely, because then you can close the gap between a successful solution and what you did more quickly/easily. It’s easy to fall into one of two unproductive traps: wasting multiple days attempting to figure something out that is too far outside of your current skill level, and giving up whenever you are uncertain and relying on the solutions to guide you. Instead of focusing on solving problems, instead focus on using problems to measure and push the limits of your skills. If you can’t solve it today, see how far you can get today, then focus on solving it tomorrow, and move forward.