The First Ten Years
A retrospective on a decade of teaching myself programming.
In 2012, I started teaching myself programming. Around this time, I read Peter Norvig’s essay, Teach Yourself Programming in Ten Years. Norvig’s thesis is that learning to program takes much more time than people like to admit. I found this a comforting voice of reason in a time when “just learn to code” felt like a cultural mantra. Norvig gave me license to slow down.
Slowing down allowed me to focus on mastery. In his talk Let’s Teach for Mastery – Not Test Scores, the educator Sal Khan compares the modern educational system to building a house even after discovering cracks in the foundation. We place artificial time-constraints on learning and measure gaps through testing and then just move on regardless of the grade. The idea of “mastery learning” is to ignore how long it takes to learn something and to focus instead on completely understanding the material. Slowing down gives you permission to honestly ask yourself if you understand.
Now over a decade later, I can say that I have learned to code. In hindsight, what surprises me the most is that the process never felt remarkable. There were no shortcuts or productivity hacks. There were only a few qualitative step changes, such as getting accepted into a computer science PhD program. For the most part, it just required mindset, habits, and hard work—maintained over years. The hard part was doing it every day.
That said, the nights and weekends learning to code were long, and the decade was also long. And I wish I could have read something that could have given me perspective on the journey. So this is my version of that, a retrospective on learning to code. It amounts to advice that worked for me, and I have organized my thoughts around a few key topics:
I feel self-conscious publishing this, but it’s something I wish I could have read. So if you want to learn to code, I hope you find something useful here.
Belief
Let’s start with the stories we tell ourselves. In the context of learning to code, perhaps the biggest useless belief people have is that they are not talented. My advice is to not worry about whether you’re talented enough to be a good programmer. Don’t worry if you’re a “computer person” or some other label. I’ll make an even stronger claim: talent is a useless concept.
To see this, imagine an adult is observing a child who is about to try a sport for the first time. If the adult thought, “This child has no talent,” we’d consider that unreasonable and judgmental. Why? Because it makes no sense to call the child untalented without any evidence. But if that is the case, we can only observe talent by observing the evidence. To quote from the excellent Mundanity of Excellence (Chambliss, 1989):
Talent is indistinguishable from its effects… Talent fails as an explanation for athletic success on conceptual grounds. It mystifies excellence, subsuming a complex set of discrete actions behind a single undifferentiated concept.
In my mind, “talented” is just a label people apply after someone starts succeeding.
You might say that unlike this child, you have evidence of your lack of ability. But that’s not the point. The point is that focusing on this descriptor is pointless because it subsumes specific actions. So just focus on improving those actions.
That said, I’d still challenge the notion of evidence. In my mind, evidence for lack of talent is typically complicated in innumerable ways. If a child suffers from poverty, hunger, and an unstable home life and also struggles on their homework, would you confidently state it was due to lack of ability? Or would you give this child some grace? If you want to learn to code but think you aren’t talented, you must learn to view yourself with the same grace. To me, this is the essence of the beginner’s mind.
The beginner’s mind is hard to achieve, especially for adults, but it is critical because judgment clouds your ability to see clearly. In the book The Inner Game of Tennis, the author and tennis instructor Timothy Gallwey tells the story of trying to get one of his students to see—not just acknowledge but actually see—, that his racket was going back too high during a swing. So Gallwey had him do the stroke in front of a reflective window. He writes:
What surprised me was Jack’s surprise. Hadn’t he said that five pros had told him his racket was too high? … But what was now clear was that he didn’t really know… Despite all those lessons, he had never directly experienced his racket going back high. His mind had been so absorbed in the process of judgment and trying to change this “bad” stroke that he had never perceived the stroke itself.
So the point of non-judgment—of avoiding internal monologues like “I have no talent”—is not to live in a fantasy world where errors are tolerated. The point is to quiet your judging mind enough that you can actually see the complex, interacting set of specific actions that go into learning something new.
A second useless belief is that one lacks intuition. This demoralizes people who believe that intuition is an innate quality rather than one that emerges through effort. My own view is that, barring outlier brains like Ramanujan, intuition is less about innate understanding and more about metabolized experience after lots of hard work. Even Terry Tao, who has deep and clearly outlier intuition for mathematics, claims that intuition can and should be developed.
Let me give an example from my career. During my first job as a programmer, I went to a hackathon with a few coworkers and friends. The two most experienced programmers sketched out what we would build and divided up the work. I did not really understand the big picture, but I just focused on the few functions I was asked to write. I remember working late into the night before finally realizing how the pieces of code came together. Was I stupid to have missed it for so long? Did I lack some essential quality to be a programmer? No. I just had very limited experience. But the more programs I have written, the better I have gotten at conceptualizing programs. Which seems obvious.
In my own life, these kinds of useless beliefs were often just an abdication of ownership. Over the years, I have often made excuses for my lack of understanding or my inability to solve a particular problem. But dwelling on external or past circumstances can turn into an abdication of personal agency. My advice: take complete responsibility for your education. No one else will do that for you.
Why am I writing all this psychological stuff in an essay about coding? Let’s get concrete. Imagine it is 7:30 in the morning. You’re sitting at your kitchen table, drinking coffee, and trying to learn to code. But you’re struggling with a particular bug. The bug seems basic, and you feel stupid, and you have to get ready for work soon. If your story of yourself is that you’re stupid or not technical in nature or can never learn to code, then your emotional response in this moment will be one of frustration and doubt that may be hard to push through. If your story of yourself is that you’re smart enough and that with hard work you can understand, then your emotional response in this moment will be one of frustration but commitment. You’ll have the motivation to go again.
So in my mind, nothing I write here will matter if on some deep emotional level you don’t think learning to code is possible for you. And my other claim is that that belief would almost certainly be wrong. I’ve had almost every kind of education—Evangelical homeschooling as a kid, state school for college, the Ivy League for graduate school—and in my experience, almost everyone was smart enough. My belief is that much of what we view as natural intelligence is an accumulation of parenting, mindset, hard work, education, and plenty of luck. I don’t think you can make sense of data such as literacy rates over time (UNESCO, 2017) without accepting that there is a lot of truth to this claim. To be clear, my claim is not so silly as that there are no differences in natural ability. My claim is that your natural limits are an unknowable distraction. Focus on specific skills, not labels.
So give yourself a little grace. With time and many small wins, you might find that your narrative about yourself can change. And after a while, people might even start saying that you’re talented.
Motivation
My second piece of advice is to accept that learning to code will require a lot of coding. This may seem obvious, but I have seen this kind of wishful thinking more times than not from people who have asked my advice about changing careers. But I think it’s important to distinguish between desiring an outcome and enjoying the process.
One way to think about this is to imagine a world in which you succeed in learning to program. What does this world look like? Why is it appealing? I might suggest that in such a world, you will be sitting at a computer and writing code; you will be frustrated by bugs; you will be wondering if you’re talented. So if you do not actually like to code now, you may not enjoy the life of a programmer.
Another way to see this distinction is to observe instances in which people achieve a goal without the process. My favorite quote on this topic is by Yvon Chouinard, the founder of the company Patagonia. In the documentary 180 Degrees South, Chouinard talks about people who pay their way to the top of Everest:
You get these high-powered plastic surgeons and CEOs, and you know, they pay $80,000 and have sherpas put the ladders in place and 8,000 feet of fixed ropes and you get to the camp and you don’t even have to lay out your sleeping bag. It’s already laid out with a chocolate mint on the top. The whole purpose of planning something like Everest is to effect some sort of spiritual and physical gain and if you compromise the process, you’re an asshole when you start out and you’re an asshole when you get back.
Chouinard’s point is that the reason mountaineers climb big mountain is to transform themselves in the process of facing the challenge. To circumvent the challenge is to circumvent the transformation. If the point were to get to the top of Everest without challenge or cost, why not just take a helicopter to the top? You know some people would if they could, and they’d take a selfie like it was an achievement.
Similarly, if your only goal is to be a software developer or a data scientist or an AI engineer—or some other fancy title—but you do not actually like the work, you’re setting yourself up for a bad time. You want the view from the mountain or the prestige of having climbed it, but most of the experience is just climbing.
To be clear, I am not gatekeeping. If programming is simply a good career choice, and having a good career is your goal, go for it! But my point is that this journey will require a lot of actual programming. You’ll be best served by accepting this fact, because motivation for the outcome without enjoying the process will only get you so far. Why? For me, there has never been a singular moment when programming became easier. (This is true for most things in my experience.) To quote Greg LeMond, an American cyclist, “It doesn’t get easier, you just go faster” (LeMond & Hom, 2014). Once you internalize this, I think you’ll realize that outcomes are often illusions—useful illusions, to be sure—and find that the process itself becomes easier precisely because you aren’t waiting for a magical moment of arrival. You’ll just slowly get better.
So find the kernel of joy in programming, and explore that. For myself, I can still remember the few-month period when programming clicked for me. I was teaching myself Python by solving Project Euler problems and the idea of computational thinking just started to click. I realized that by combining problem-solving, mathematics, and computers, I could transform seemingly impossible problems into tractable or even easy ones. It felt like magic. I don’t think I’ve ever lost a certain wonder from those early days.
So stay motivated by enjoying the process, not the outcome. As a matter of experience, this process is nearly all there is. And if you fail to climb the mountain, that’s okay. The goal was to transform yourself in the challenge.
Commitment
But enough about psychology. Let’s talk practical advice. What should you do?
For me, the single biggest reason I succeeded in learning to code is that I committed to spending a non-zero amount of time every day teaching myself to code. I call this habit “everyday.” In my case, I have found it easiest to do this first thing in the morning. So for over a decade now, the first thing I do every morning is spend a little time learning something new. It was how I first taught myself programming. It was how I took online courses to replicate a computer science curriculum while working as a software developer. It was how I taught myself remedial mathematics during graduate school. And today, I have a new project, but that’s the topic of another post. I wake up; I make coffee; I sit at my desk; and I teach myself something new. Every single day.
Just start. Start an online course or read a textbook or write code. Whatever suits your inclination, background, and skill level. And then do it every single day. If you only have time for five minutes, do five minutes. If any step seems too hard, do the smaller step. If you don’t know where to start, then finding out where to start is the thing you do that day.
In the beginning, don’t worry about the quality of your effort. The hard part is establishing the habit. As the habit becomes established and part of your identity, you can put more effort into the quality of your deliberate practice. But to start, focus on the smallest discrete win: a non-zero day.
Is this hard? In the TV show Bojack Horseman, there’s a scene I love. Bojack is trying yet again to get his life in order. He’s going for a run—Bojack is not a runner—and as he comes to a hill, he runs out of breath. As a he stops to recover, an older runner who passed him on the hill turns around, stops next to him, and says:
It gets easier. Every day it gets a little easier. But you gotta do it every day. That’s the hard part. But it does get easier.
This has been my experience. Don’t worry about talent. Don’t worry about intuition. Don’t worry about the outcome. Just do a little every day.
My second piece of practical advice is to finish what you start. If you start an online course, finish it. If you start a side project, finish it. Why? When you don’t know what you don’t know, it’s easy to give up because you think something is too hard for you now. But how can you know if something is too hard unless you try very hard? If you get in the habit of quitting too early, you will be giving up many opportunities to learn. So as a default, commit to completing what you start. By just doing this, you’ll stand out. The drop out rate for online courses is around 40-80% (Bawa, 2016). So just by finishing an online course in your free time, you’ll prove something important to yourself and others.
This is a guideline, not an absolute rule. Sometimes you can attempt something too difficult, and it’s only damaging to continue. Learning is maximized at just the right level of difficulty, where you are challenged but not too much. For example, after I completed my first online course in programming, MIT’s Introduction to Computer Science and Programming Using Python, my very next course was a Stanford course on compilers. If you know anything about computers, you know this was a bad idea. I essentially leapt from a first-semester freshman-level course to a third- or even fourth-year course. I failed spectacularly.
But if you do have to quit, try to renegotiate. Is your side project too big? Scale it down, but finish something. For example, after failing the compilers course, I decided to look at computer science curricula that universities published online and to try to follow them instead. It seemed like a common theme was to take a course in algorithms and data structures in the first year. So my third online course was Princeton’s Algorithms, Parts I and II. This was an amazing course, and it established a pattern for myself: I could get a computer science education on my own for free. If I had given up after compilers—if I had failed to renegotiate that experience into a learning opportunity—I might have lost motivation.
This ability to just finish things is a super power. Early in my PhD, I read Michael Nielsen’s Principles of Effective Research. In that essay, Nielsen argues that one way to do important work is to combine two skills. The reason this is so effective? So few people are willing to do it:
In my opinion the reason most people fail to do great research is that they are not willing to pay the price in self-development. Say some new field opens up that combines field X and field Y. Researchers from each of these fields flock to the new field. My experience is that virtually none of the researchers in either field will systematically learn the other field in any sort of depth. The few who do put in this effort often achieve spectacular results.
In my experience, this is absolutely true. Almost no working adults put any serious effort into systematically improving their skills or knowledge. So few people deliberately try to get better. If you do, you’ll find that you can very quickly close gap between you and people who have been programming for a long time.
The problem with commitment, of course, is that it is easy to have excuses for not doing the work. You need to do your laundry. You need to clip your nails. And so on. And I think the most insidious excuse is that you want to do the work but you need to improve yourself or your process first. Imagine the programmer who improves their Emacs config but does not write code or the writer who tells themselves that they must first read one more book before they’re ready to write their own. I’ve had people email me asking my advice on how to start a blog, and my advice is always the same: stop asking that question and write. This Hacker News comment put the point succinctly:
A preoccupation with the means is a lack of commitment to the end.
You don’t need to do one more thing before you’re ready. Just start.
My last piece of practical advice comes from a hard truth. If you start learning to code as an adult, and you want to get a job or do interesting work, you’ll be competing with people who started learning to code as teenagers or even younger. You’ll be competing with people with college degrees in technical disciplines like computer science, mathematics, and engineering. You’ll be competing with people whose parents are also engineers or professors or doctors and are able to give career advice or pay for remedial education. You’ll be competing with people with deep social and professional networks. In the face of this, what can you do? You’ll need to work hard.
Productivity hacks are nice sounding, but if I’m being honest, the single biggest practical reason I have any success has been my capacity to just sit down every day and do the work. It has opened so many doors. And unless you have money coming from somewhere, you can’t teach yourself to code as an adult without doing it after hours. So remember that life is short, and that work is only one dimension of the good life. But also work hard.
Don’t believe me? Then don’t take it from me. Take it from Terry Tao, John Carmack, Mindy Khaling, or Kevin Hart. “Talent,” whatever that means, only gets you so far. You have to do the work.
So that is my advice. Work hard, every day, consistently, for a long time. Start what you finish. Push through the boring parts. Don’t worry about your day-to-day fluctuations in motivation or mindset if you truly believe you want to do this.
As a final caveat, it may sound like my advice in this section amounts to: “Just have a lot of willpower.” But I don’t think so. My understanding is that modern neuroscientific models for behavioral change do not even include willpower as a variable. It’s not a knob you can turn. (I really like Judson Brewer on this topic.) This is why belief and motivation matter so much. You can’t force yourself to do this everyday. Instead, my advice would be to stay non-judgmental and curious. You’re going to fail, and when you do, be kind to yourself and then go again. Try to enjoy this process, because it never gets easier. You just go faster.
Edge
Now let’s assume you’ve learned to code, and let’s talk about how to get your first job as a programmer. My advice is to obsess over jobs or projects or skills that get your foot in the door, even if they are not exactly what you want. Be realistic about what you’re capable of and then work hard to prove that you’re capable of more. Know what your edge is and exploit it relentlessly.
Let me give an example. Around the time that I decided to learn to code, I managed to get a job in New York City that was semi-technical at best. The job title was “web production manager,” and the job was using a content management system to manage a fleet of local news websites. Basically, I was a glorified WordPress power user. This job was terribly boring, but it paid the bills and put me in day-to-day contact with developers. More importantly, it was a job I could do. And so on nights and weekends, I taught myself programming.
Roughly six months into the job, one of the web developers on my team, Scott, quit; and my boss dragged her feet on hiring his replacement. But one of the graphic designers on my team, Chris, was also trying to break into programming. And so Chris and I just started doing Scott’s work, in addition to our own. Our boss liked this because it meant she could delay hiring a replacement, and we liked it because it was real-world experience writing code.
This went on for months. Chris knew more programming than I did and eventually moved from graphic designer to web developer, taking over Scott’s role. Then Scott’s partner quit, leaving Chris—smart but inexperienced—overwhelmed. For a couple more months, I moonlit as his assistant, building widgets with HTML, CSS, and some JavaScript. It was hard work. Once, our boss promised a client that their website would be redesigned by a certain date, even though Chris and I had been overworked for weeks. The night before the deadline, Chris had a concert he’d committed to. So I stayed late, working on the site until around midnight, and then Chris returned to pull an all-nighter and finish it. It was exhausting, but thrilling—the skills I was learning in my free time were finally being used for real work.
Eventually, Chris and I each had enough web development experience that we both got other jobs. My next job was as a professional programmer, a “JavaScript engineer” for an ad tech startup. I had made the leap.
Why do I tell this story? I think it’s important to see exactly how I made the transition from non-programmer to programmer. When I tried to break into programming, I had an architecture degree and almost no technical skills. And the job of a web producer was very much not what I wanted to do. But it was the job I could do, and it put me in daily contact with programmers. So I did the work I could do, and taught myself the work I wanted to do. When an opportunity arose to do the work I really wanted to do, I pushed hard to do it.
But even web development wasn’t really what I wanted to do. If you had asked me then, I would have given the same answer as now: I am more interested in mathematics, statistics, and data. Maybe I’d like to be a data scientist or a researcher or a machine learning engineer. But given my upbringing and education, those jobs were far from me. I was stuck building JavaScript widgets. But on the spectrum between architect and machine-learning research scientist, JavaScript programmer was the right direction.
If I am honest, at the time I was often a little embarrassed for myself. I was a web developer, not a data scientist. I was a JavaScript engineer, not C++ engineer. I understood HTML, not compilers. I felt like I was at the party but standing alone in the corner. But over the years, I have come to shed this as yet another useless belief. A blog post I think a lot about it is Are You Playing to Play, or Playing to Win? The point of the post is that many people handicap themselves in competitions by adhering to self-imposed and fictitious rules in their head, rather than playing to win. In online gaming, for example, many perfectly legal playing styles are considered “cheap” despite their effectiveness. My advice: don’t build up fictitious rules in your head for what it means to be a “real programmer.” Don’t let some guy writing C code to launch rockets convince you that writing JavaScript to make drop down menus isn’t “real code.” That’s just a rule in your head that serves him but not you. The real game is to learn to write code, enjoy that process, and ultimately get paid for the pleasure of doing so. Focus on the real game. Focus on footholds that get you to the next level. This means being realistic in your assessment of yourself and your skills.
The ultimate foothold is what I call a “step change.” During the learning process, be mindful of these. These are qualitative changes in outcomes, when all the potential energy created through everyday work is finally unleashed. It’s when a poor kid from a bad part of town gets into a top-tier school. An entrepreneur sells their first company. A PhD student wins a best paper award. A self-taught programmer gets their first programming job. You cannot force step changes through incrementalism, but you can prepare yourself. When the door opens, run through.
Finally, a hard lesson of success is that once you finally achieve your goal, often after years of hard work, your skill set becomes outdated. And often what this means is that many powerful habits and mindsets that worked for a younger version of you slowly become limitations.
Let me give an example. Many junior employees rightfully recognize that they have limited experience or skills but significant downside risk—getting let go from your first job can be hard to recover from. So they become indispensable by working very hard and picking up slack across the team. I remember being an intern once and explicitly telling an older employee with a new baby that I’d stay late and do his work whenever he needed. And why not? I was young, getting paid hourly (and overtime), and gaining experience. And he got more time with his child. But this mindset doesn’t work for management. A manager who stays late when they don’t have to might guilt their employees into staying late. A manager who picks up slack for their employee might send the signal that that employee did something wrong. The mindset that makes you a good intern is not the same mindset that makes you a good manager.
In the context of your programming career, my advice is to be mindful about how the value of your skills change over time. In the beginning, any experience is better than none. Work hard and do anything that’s given to you. But as you build a career, knowing how to code won’t be enough. You’ll need to adapt and continue learning. Know your edge, and know that your edge will change.
Memory
When I started learning to code in 2012, I was trying to change careers from architecture to scientific research. It’s a long story, but I was homeschooled until college and raised with many anti-intellectual and anti-scientific beliefs. The education was impoverished, but the real problem was it lacked context. I did not know what I was good at or what I enjoyed. My world was a closed system. This is how I found myself with an architecture degree and a growing sense that I wanted to do scientific work.
My first year out of school, I worked as a research assistant in a biophysics lab, running experiments that required more care and methodicalness than understanding. However, my lack of basic scientific knowledge and poor education—I did not take a science class that taught evolution until I was twenty-three—were making the transition difficult. So I was in my twenties, making a thousand dollars a month, and carrying student loan debt. That year, I lost about thirty pounds due not having enough money for food. It was in this context that the lab’s principal investigator told me that his funding for my position was running out. Desperate for work, I applied for a job as a web producer and decided to learn to code. My thinking was that it was a stepping stone towards any sort of scientific career, and it would be possible without more formal education or debt. In hindsight, this was the right move, but at the time, it felt like the only move.
Today, with a PhD and a good job, it is easy for me to forget how desperate I used to feel. So my last piece of advice is this: once you learn to code, take a moment to remember.
Remember the world before you couldn’t code. In my mind, one of the more obnoxious things about people who work in technology is that they often do not understand how privileged they are from a market perspective. Five hundred years ago, being good at mathematics or engineering meant relatively little. You’d still be a craftsman or day laborer or farmer. Today, those skills are a stepping stone to the upper middle class. “Become a doctor or lawyer” is now “become an engineer in Big Tech.” So do not confuse the current job market for intrinsic self-worth. Don’t compare your paycheck to a school teacher’s and explain the difference with “talent.”
Remember the people who helped you along the way. In my view, my great luck was being born in a place and time when learning to code through free online resources is actually possible. You can download textbooks, take online courses, and ask questions on forums. Access to knowledge has never been better. So I will always be grateful for the people who have put their knowledge online for free. This is one reason I blog and one reason this blog will always be free.
Finally, remember that the good life is always now. Early in my career, I didn’t have money or job security, but I look back fondly on simple pleasures, like biking everywhere and attending university lectures for free food. Later, I had a stable but unfulfilling job as a web producer, but learning to code was exciting. And even when I had stabilizing successes, like getting into a PhD program, there were challenges and insecurities balanced by the satisfaction of real progress. There are always hardships, and there are always joys.
So you want to learn to code? Believe in yourself. Be motivated by the process. Commit to your work and work hard. Know what you’re good at and exploit it. But also remember just how lucky we all are. Be present. Learning to program could take ten years. I hope you enjoy it.
- Chambliss, D. F. (1989). The mundanity of excellence: An ethnographic report on stratification and Olympic swimmers. Sociological Theory, 7(1), 70–86.
- UNESCO. (2017). Reading the past, writing the future: Fifty years of promoting Literacy. Paris: Unesco. Retrieved from http://www. unesco. org.
- LeMond, G., & Hom, M. (2014). The Science of Fitness: Power, Performance, and Endurance. Academic Press.
- Bawa, P. (2016). Retention in online courses: Exploring issues and solutions—A literature review. Sage Open, 6(1), 2158244015621777.