Background Image

Actualize Blog

July 29, 2016

What Is Teachable Code?

Software developers strive to write code that is as readable as possible. But what does it mean for code to be readable? One definition that sums it up well is:

Readable code is code that clearly communicates its intention to the reader.

There’s an important point to emphasize in this definition - code readability depends on who’s reading it, which means it’s subjective. It’s not an objective measure or score you can use to evaluate code. What’s readable to an advanced developer is very different than what’s readable to a beginning developer.

When you are teaching a concept to a beginner for the first time, it’s important to keep the subjective nature of code readability in mind. Abstractions, design patterns, shortcuts - these are all advanced tools used to make code maintainable, but to a person who is not familiar with them, the code becomes nearly impossible to understand.

To avoid confusing beginners when teaching new concepts, you should instead focus on writing teachable code. We can define teachable code as follows:

Teachable code is code that makes the specific concept being taught as explicit as possible.

In other words, it’s objectively making the code as readable as possible for a specific concept for an absolute beginner. Let’s look at an example - writing an initialize method for a Ruby class. Typically, it would look something like this:

# readable
def initialize(age)
  @age = age
end

For a student who’s just learning about classes and methods, this is surprisingly confusing. The curse of knowledge makes it hard to appreciate how much is actually happening here. Nearly every variable is named the same thing, and it’s hard to distinguish which variable does what. A more teachable way to write this code would be:

# teachable
def initialize(input_age)
  @age = input_age
end

This code makes it clear which variable is the input (by adding an overly explicit prefix) and which is the instance variable. This code would generally be considered to be less readable due to its overly explicit nature, but it helps students distinguish between the various pieces being presented here.

It’s important to note that teachable code is utilized when presenting a concept to students for the first time. Once you feel the concept has been grasped, it makes perfect sense to show students a more “normal” or readable way of writing the code. When you demo code this way, you are showing the progression from basic code to advanced code. You can evolve your coding style to match student understanding, instead of showing advanced code to beginners and overloading the number of concepts they need to learn at once. Let's examine some concrete tips to improve the teachability of your code.

Use overly expressive variable names.

Variable names can help remove some of the subtle abstractions inherent in code. For example, http parameters in the Ruby on Rails framework are parsed from multiple sources (query parameters, url segment parameters, form input parameters) into a single hash called params. When using the params hash, it is unclear where the parameters are coming from. This makes it hard for beginners to understand problems in their code. Instead of writing code like this:

# readable
@product = Product.find_by(id: params[:id])
@product.update(name: params[:name])

Try using more explicit names like this:

# teachable
@product = Product.find_by(id: params[:url_segment_id])
@product.update(name: params[:form_input_name])

Note that this takes some dedication - to teach code using these explicit variable names, you would also need to use the same explicit names in the routes file and html form to match. As cumbersome as this sounds, this approach completely eliminates the abstraction in the params hash and helps students make the appropriate connections. Once they grasp the concept, feel free to demo code using more typical conventions!

Use different variable names for different things.

As described earlier, similar variable names for different concepts can make it confusing to determine which variable is responsible for what. The classic example would be a method like:

# readable
def initialize(name, age)
  @name = name
  @age = age
end

For a beginner, it is very unclear which variable name is connected to which concept. You could improve the teachability of this code as follows:

# teachable
def initialize(input_name, input_age)
  @name = input_name
  @age = input_age
end

Do one thing per line of code.

It’s very easy to underestimate the number of things happening in a single line of code when you’re not a beginner. Instead of teaching with the code:

# readable
@products = Category.find_by(name: params[:name]).products

Try teaching with this code instead:

# teachable
selected_name = params[:url_query_name]
selected_category = Category.find_by(name: selected_name)
@products = selected_category.products

Not only does this help with the teachability of the code, but it also helps students debug their code when things go wrong - it’s easier to see which part of the code has the problem when it’s not all on the same line.

Write everything in a single method before breaking things into smaller methods.

It’s easier to understand a method at first when everything is written in one long method. Breaking it up into smaller sub-methods increases cognitive load on students. Instead of writing this code:

# readable
def print_owing
  outstanding = calculate_outstanding
  print_details(outstanding)
end

def calculate_outstanding
  outstanding = 0.0
  @orders.each do |order|
    outstanding += order.amount
  end
  outstanding
end

def print_details(amount)
  puts "name: #{@name}"
  puts "amount: #{amount}"
end

Keep it all initially in a single method:

# teachable
def print_owing
  outstanding = 0.0
  @orders.each do |order|
    outstanding += order.amount
  end
  puts "name: #{@name}"
  puts "amount: #{outstanding}"
end

It’s easier for a beginner to understand the second code with a single method, despite the fact that the first code with multiple methods is easier for an advanced developer to read and maintain. Again, once the structure of the method is understood, it is acceptable to break it up into smaller methods. But don’t jump ahead until you are sure students understand the overall structure of the method.

Test to see if your code is working frequently in small chunks.

Consider the scenario when you’re teaching parameters for the first time. Instead of demoing this code:

# readable
Recipe.create(title: params[:title], ingredients: params[:ingredients])

Try demoing this code first:

# teachable
Recipe.create(title: "Ice cubes", ingredients: "Water")

In other words, the first code is demoing two separate concepts - getting values from params and saving something to the database. The second example is demoing the single concept of saving something to the database. After you prove this works, then replace the hard-coded values with the params. This helps students see the evolution of the code in testable pieces.

Demo code the wrong way and ask students what went wrong.

Often teachers demo code without making any mistakes to show how things work. Then students try doing the same, but spend most of their time writing broken code and trying to fix it. Instead of demoing perfectly working code all the time, it is very beneficial to demo code using common student misconceptions, watch it break, then pose to the students how to fix it. For example, instead of demoing the correct code first:

# readable
def update
  @tag = Tag.find_by(id: params[:id])
  @tag.update(name: params[:name])
  redirect_to "/tags/#{@tag.id}"
end

Try demoing the incorrect code instead:

# teachable
def update
  @tag = Tag.find_by(id: params[:id])
  @tag.update(name: params[:name])
  redirect_to "/tags/:id"
end

This is a common mistake that beginners make working with Ruby on Rails (it’s easy to think that the wildcard url syntax in the routes file works in other places). Watch the code break, read the error message, and show the process of debugging the code. This helps squash misunderstandings before they begin.