How to teach code to beginners

Miguel Jimenez
The Startup
Published in
13 min readMay 23, 2020

A few lessons learned from teaching how to code to people who just got started.

Teaching how to code is one of the most rewarding things I get to do. Psychology and technology are two things that I hold close to my heart. As a psychologist, I get a kick out of helping people learn and refine their own thinking. Doing so in the context of tech translates into empowerment, I can help others realise what they are able to build.

This article is about teaching code to adult learners who just got started. In case you have never tried, learning software development is damn hard. I’m always struck in awe by people who take up bootcamps like ours or go into extremely demanding self-study programs.

Picking up a new syntax to express your thoughts might be the lesser problem. Personally, I find one of the biggest barriers for newbies is the difficulty to keep oversight of so many different contexts. Before coding, your computer is a perfectly predictable black box. Two days into software development, the complexity seems endless.

New contexts, rules and relationships appear as you build your first project. Languages, databases and frameworks create a mesh that extends unpredictably across your computer and the internet. At the beginning, patterns are hard to grasp and understanding the simplest actions takes most of your mental resources.

Learning to program will also test your patience. You need to manage the constant friction between the idea you want to express, and what an unforgiving computer will actually understand. Thousands of errors appear in obscure terminal logs, while searching on StackOverflow only takes you deeper down the rabbit hole. Nobody will blame you if you think of setting your computer on fire.

An amazing reward waits at the end of the process. Learning how to use code to bring your ideas into reality is a truly empowering feeling.

Now, If learning how to program from scratch is this challenging, helping other people reason and learn during the act of programming while enjoying the process inevitably requires some finesse. Being a good programmer does not cut it. You need to be able to keep a beginner’s mindset, be good at reading and motivating people, and transform dry computer science abstractions into relatable stories. You need to gently steer others’ thoughts and promote good programming habits. That’s an awful lot.

In this article, I’ve gathered a few principles and tips rooted on cognitive science, learning psychology and my own experience (aka mistakes). Most pointers are tie in with the three contexts I usually find myself in: lecturing, live coding and helping students get unstuck.

Foster Collaborative Coding

The benefits of learning in collaborative environments are well researched. Working in collaboration equates to higher brain activation. In an activity as high-bandwidth and close to semantics as coding is, I think collaboration is crucial when you are starting out.

Coding with others forces learners to verbalise their thought processes. It pushes them to expose their assumptions on how code works. Pairing with more experienced developers helps them see how others might break down a problem in a different way. Encouraging students to take up pair programming soon enough contributes massively to creating better learning outcomes.

Group live codes can also be very effective as long as they are time-boxed and they build on concepts that the learners are already familiar with. Doing code peer-reviews and refactoring sessions also encourage great conversations about higher level concepts like code quality, choices and trade-offs.

Build the Basics with Repetition and Active Recall

Any new task that takes a toll on your executive function will require most of your attention. After enough repetitions, everything starts to ‘click’ and you can do them almost effortlessly (i.e. think of your first day at driving, or your first day playing an instrument).

This is one of the main contributions of Daniel Kahneman and Amos Tversky to cognitive psychology. A constant challenge in the journey of learning complex skills is chunking new tasks and moving them from system 1 (limited, conscious, verbal) to system 2 (automated, semi-unconscious, non-verbal).

Coding is no different. You want your learners to automate basic procedures as soon as possible so they can put their attention on more difficult matters. Resources like flash cards, focused coding coding drills and rapid fire trivia questions are very valuable to become fluid on the fundamentals.

Promote a Growth Mindset

The term growth mindset was coined by Carol Dweck. It has become part of the pop psychology compendium and has been very successful in the corporate world. The core idea boils down to this: 1) People who are better at learning tend to see failures as opportunities and put more focus on the learning journey than on the result. 2) It’s not a stable personality construct, anyone can develop a growth mindset — as a coach you can model it and help others develop it.

This is particularly relevant in a bootcamp context. Students eager to land a job in the tech market have committed a considerable amount of time and money, so the outcomes count. Competitive dynamics can quickly arise in the cohort and negative self-talk happens frequently in students that do not perform up to their own expectations or feel they are lagging behind. They stop focusing on learning and only pay attention to their definition of success. Your job as a dev coach goes beyond transmitting knowledge. You must foster growth mindset and self-efficacy.

An example:

- student: Oh dear, I will never understand closures.- terrible_coach: Ah, this is nothing. Wait until you see recursion.- another_terrible_coach: It's ok. Keep applying yourself and you will get better.- better_coach: You have not gotten them *yet*, but you will for sure. Closures are hard and they take a bit to sink in. I remember having difficulties with them too. Let's go through an example together.

A few tips: Promote positive self-talk. Incentivise learners to break their programs and understand why things stopped working. Congratulate your learners when they make efforts to understand how something works beyond what a test expects. Declare errors as something to be glad about (as long as you are getting different errors). Encourage students to have fun coding and believe in them. Show vulnerability and acknowledge the ups and downs of your own journey in software development.

To Keep Learners Motivated, Understand What Drives Them

If you did not develop an affinity to formal languages, coding in a vacuum can be quite dry. Your average learner cares most about what software development makes possible, not about what software development is. Learning to code is an intellectual journey, but most people who seek it later in life see programming as a means to an end.

If you keep a keen ear, you’ll quickly notice what make your learners tick. Some dream of starting their own companies. Others want a life of nomadic work. There’s people, companies, communities and icons in the tech world they admire already. A cunning coach is attentive to these cues, and uses them to decorate technical explanations with the right examples and stories.

You have a bunch of tech entrepreneurs in the room and you happen to be teaching Ruby on Rails — you can be much more impactful if you mention what some companies like Twitter and Shopify have achieved with it. If you are running a live code to solve a Fizz-Buzz kind of exercise for students who want to be software developers, they’re more likely to be engaged if you mention that the kata was (sadly) popular in technical interviews. The point is to craft narratives that bind the content to a frame that matters to them.

First Tap on Analogies, then on Previous Knowledge

The expressiveness of programming languages is part of what makes software so powerful and magical — we can compose abstractions on top of one another with words as soon as we understand the moving pieces of a language.

However, most of the terminology you need to start ‘thinking in code’ is opaque for the uninitiated: “What does it mean to yield a block? What is a promise? What is encapsulation? ”. When we are unable to connect a new abstraction to something that we knew already, or when there is not enough context to guess what the idea could be about, the process of understanding stops.

Associating abstract concepts to relatable objects in the physical world can make all the difference. Kids start counting with their fingers to compensate for the fact that their abstract reasoning is still underway. Adults in indigenous tribes struggle to learn to count ‘in their minds’, but are more likely to learn numeracy if the teacher uses everyday objects like machetes or arrows. Building the right representations and analogies goes a long way.

If you think these examples are contrived, check out how the ruby core library describes Enumerable#map:

# Map → an_enumerator
# -------------------
# Returns a new array with the results of running block once for
# every element in enum. If no block is given,
# an enumerator is returned instead.
# Example(1..4).map { |i| i*i } #=> [1, 4, 9, 16]

To catch this on the fly, your mind needs to have a solid grasp of ranges, enumerators and blocks . A beginner’s brain might already be itching on the second line. Wouldn’t it be much easier to say that #map is something like a factory?

You can extend this to any other language or CS concept. A JavaScript promise is like a plane ticket — most likely you will get a seat (success) but your flight might also get cancelled (error). This strategy can be even more effective if you nudge learners to create their own analogies. Illustrated.dev has great examples and resources about using visual thinking and metaphors in web development.

Eventually, your learners grow their own mental models and you can refer to them as you introduce new concepts. For instance, understanding JavaScript prototypes is a bit easier if you have seen classes in other language. Once you have understood the HTTP protocol, wrapping your head around FTP or SMTP is way easier. By highlighting similarities and differences, you can ease the friction of gaining understanding.

Eliminate Sources of Cognitive Noise

Cognitive noise is any input that stands in the way of understanding an idea. Check this out for example:

// Noise on['foo', 'bar', 'baz'].forEach((x) => { 
console.log(x)
}
// Noise off['banana', 'apple', 'pear'].forEach((fruit) => {
console.log(fruit)
}

When we process written text, our brains will automatically try to find meaning in it. If a learner finds three unfamiliar words without clear meaning, his/her brain will continue do its best to figure them out while other processes go on in parallel. We humans suck at parallel processing, this is a situation you want to avoid as a dev coach.

The second example takes less time to process because most English speakers can relate to fruits, and most English speakers are not used to reading examples with metasyntax like ‘foo-bar-baz’.

Hick’s Law reminds us that every piece of added information matters. As developers, we go at great lengths to make our code easy to understand and discover to others — As dev coaches, we should put the same amount of care into ‘refactoring’ our explanations and examples so underlying principles can be understood as easily as possible.

The tips: Avoid metasyntactic variables. If possible, start coding your examples on clean files and build them up gradually. Try to not mention concepts or acronyms that clearly sit outside of the learning context. Don’t mention CSS-in-JS when people just got started with CSS. Don’t mention everything you know about the JavaScript ecosystem when your learners just got started with their first framework. Whether lecturing or clarifying doubts 1:1, dev coaches who are truly savvy know how to convey an idea really clearly without overwhelming their audience.

Show code in the wild

Like so many other people, I did not enjoy math at school. I remember doing exercises about algebra and calculus like they pertained to a different world that only cared about the correctness — learning in an isolated bubble did not help me solve for anything beyond the exercises themselves, and only exerted pressure on me to get the ‘right’ answer. It made whatever skills I gained untransferable to other contexts, and going through it was painstakingly boring. The approach to teaching the subject was broken.

A similar pattern can emerge with coding as you move into topics like metaprogramming, algorithm complexity and concurrency. As a dev coach, you add a ton more context if you show how the techniques you cover are used in real codebases, and hopefully solve real problems.

For instance, Rails is constantly using Ruby’s introspective capabilities to pull metaprogramming tricks that power all the ‘magic’ that the framework makes happen under the hood. Showing the lifecycle of a request, or going through the work done by a controller creates a lot of ‘aha’ moments. Resources like, DHH’s video series on how to write good software or some sections of Ruby Design Patterns showcase this principle really well.

Keep a beginner’s mind

It’s widely agreed that the brains of experts and beginners work differently. Experts, over time, learn to apply strategies and heuristics that optimize their cognitive resources. Their approach when they engage with the act of programming, from touching the keyboard to editing code to tackling bugs it’s very, very different from someone who just got started.

If you are a teacher with a few years of experience in software development, it’s easy to start taking things for granted. While you can quickly decompose a problem and visualize how files in different contexts are connected, your learner still gets stuck on a line of code has not quite fully made sense yet. If you don’t keep your beginner’s mind, you risk eroding your patience and jumping to prescriptions, impairing your learner from discovering a solution autonomously.

Many, many students are brilliant and absorb concepts very quickly — Their struggle is transforming their intent into code, simply because conceptualising problems and keeping track of your own language plus the machine language is a lot of work for the beginner’s mind.

Visualising the problem on a whiteboard and breaking down its parts can work really well, even if the problem seems trivial. Using and modelling REPL-driven development is also very helpful to help students get quick feedback on their approach to a problem, without having the direct intervention of the coach. When pairing with learners, I also find it useful to ask refocusing questions that allow me to access their perspective of the problem in the moment, and start working from there:

  • What is your next immediate goal?
  • What are you trying to solve for right now? What is your approach?
  • What is not fully making sense at the moment?
  • Which part of the problem seems most difficult or unclear right now?

Model reflection and adoption of powerful strategies

People learning to code are no different from your average user. A learner’s brain will do its best to satisfice when overwhelmed with information. After being confronted with an error log, learners may try to ‘catch the scent’ of the log , and then randomly change a line of code here or there without stopping to understand the behavior of the code.

It’s important to detect these signs early — In my experience, students who resort to ‘satisficing’ and rely on information foraging strategies by default tend to experience overflow more often, get frustrated more and stay focused for shorter periods of time.

For instance, nudging learners to pseudo code early and often goes a really long way in the future. Pseudo code is the tool that helps your learner clarify their intention before writing code, and will help them detect the gaps on the things that they know, and the things that they know are possible but they don’t know how to implement yet.

Precisely because pseudo code forces you to think deeply about a problem, telling people to simply do it has not worked too well for me yet (sastisficing means people will take the route of least resistance). Modelling, inquiring, coaching and solving problems with pseudo code tend to be more effective.

In consonance with this, it’s also important to let your learners do the work, read errors and actively figure out the behavior of the code they write. When you do it for them, you prevent them from elaborating and interiorising key concepts. An example:

An ok’ish learning intervention

**student and coach work on a script** - student: I need help, I am not sure why my program crashed.- coach: Ok, show me your editor.** coach takes control of the keyboard **- coach: You see what is going on? You called the method 'parse'. However, you did not define parse anywhere else in your program. - student: Oh, I did not realise.- coach: You need to define the parse method in your scraper class, if you want it to work. Does that make sense? - student: Yep, makes sense. I'll give it a try.

A better learning intervention

**student and coach work on a script** - student: I need help, I am not sure why my program crashed.- coach: Ok, run your code and let's look at the error.- coach: Let's see what's going on. The error says 'undefined method
parse for scraper'. Can you see at which line?
- student: Yeah, it says line 16 in the parser.rb file- coach: what do you think that could mean?- student: I have don't have a method called 'parse',
but I thought Ruby included it by default? I guess it doesn't.
- coach: So what could you do about it?- student: I could define it as a method, but where?- coach: Let's check the error again. Who is asked to run the parse method?- student: Ah now I see! I need to go to the Scraper class and write it there. I'll start off with some pseudocode. - coach: Great stuff, go give it a try.

The example might seem trivial, but the principle underneath is powerful. The first example points to situation-specific errors without helping the student reason about the code. The coach missed the chance to tap on the student’s metacognition. In the second example, the coach nudges the student to reason autonomously, fostering troubleshooting habits that transfer well across many different situations.

Wrapping up

Coaching people from zero to building their own products requires more than technical knowledge. The key points are:

  • Automate the boring stuff. Use routines and drills that help learners cement syntax basics and other foundational knowledge quickly so they can put their minds to work in higher purposes.
  • Wear your coaching hat. Instill a growth mindset in your students, believe in them, understand what drives them and work to understand how they perceive and think through problems.
  • Tap constantly on metacognition. Use methods and tools that make thought processes explicit and work to refine them. Visualize problems, foster dialogue around code, pseudocode and model strategies that reinforce good engineering habits.
  • Simplify your inputs. Make your explanations and examples relatable when you introduce new concepts. Lean on analogies and go from the very simple to the most elaborated in incremental steps.

Thanks for reading this far! I’m Miguel and I take care of Product and Growth for Le Wagon in Singapore 🇸🇬.

If you want to exchange thoughts on how to create outstanding learning experiences in tech feel free to contact me at miguel.jimenez@lewagon.org.

--

--

Miguel Jimenez
The Startup

Product guy based in Singapore 🇸🇬. Ex-psychologist in love with tech, behavioral science and innovation.