Think like an educator about code quality

by adamzerner12 min read27th Mar 20218 comments

42

Computer ScienceDistillation & PedagogyPractical
Frontpage

Cross posted on my blog.


It's been said a thousand times: code is for humans to read, not for machines to execute. However, writing code that is easy for humans to read is much easier said than done. It's something that takes years to learn, and decades to master.

I think I might be able to offer a shortcut though: think like an educator about code quality.

Ok, "shortcut" is a strong word. It's not a shortcut. But I think that perspective counts for a lot. And Alan Kay said it counts for 80 IQ points.

Know your audience

Rails

At work we are using Rails, Node, and Vue. The main app is written in Rails, parts of the front end in Vue, and then we have lambda functions in Node. In a perfect world, Rails people write Rails, Node people write Node, and Vue people write Vue, but that's not how things work out in practice. In practice, we run into situations where the Node and Vue people end up having to read and/or write Rails code.

Rails, to put it mildly, is a quirky framework. There is a lot of magic that happens. Convention over configuration. For example, if you have:

# app/controllers/api/foo/bar/baz_controller.rb

def show
end

Rails will automatically look inside of app/views/api/foo/bar/baz for something like show.html.erb or show.jbuilder to respond with. But if you're not a Rails programmer... you wouldn't know this! All you see is an empty method that appears to be doing nothing!

And more importantly, you wouldn't be able to figure it out. The answer isn't hidden in some parent class or mixin. Instead, it's locked away in this sort of book of tribal knowledge.

I'm not sure if this example of controller actions is a good one. In practice, it's something you'd pick up pretty quickly, or someone on your team could identify and help you with right away. But there are other situations where you can do quirky things with Rails that only those with the right sort of tribal knowledge would understand.

When you are working in a team of highly experienced Rails experts, this isn't a problem. In fact, these sorts of quirky things can help Rails experts be more productive. But if you are working on a team with people new to Rails, the noobs will, without fail, end up hopelessly stuck and frustrated.

Here's where thinking like an educator comes in. Imagine that you're a professor. If you're standing in front of a small group of PhDs giving a highly specialized, focused seminar you can use fancy terminology and stuff without worrying that it'll go over people's heads. But... if you find yourself in a lecture hall in front of a bunch of undergraduates, well, using such terminology is not a wise choice.

It's the same thing with Rails. The question isn't whether something is a "best practice" or whether it's "The Rails Way". The question is whether it will make sense to your audience.

Angular

I made this mistake in the past.

At a previous company, we were using Rails, Angular and Python. I was "the Angular guy". The rest of the team were mostly Rails people.

I would get a little fancy with my use of directives. My boss told me to stop all of that and just stick to normal controllers. He even mentioned that his reasoning is because that is what software developers more broadly would expect to see if they jump into a codebase.

At the time, I thought it was obvious that he was wrong. I would watch all of these videos and read these books written by experts on Angular telling me about all of these best practices. They're the experts, and they seem to know much more about Angular than my boss, so I think I should trust them. Or so I thought.

ELI5

In Why Functional Programming Matters, Eric Normand talks about a program involving a game tree. He says how he remembers writing a similar program in college where he used a bunch of for loops. Then he talks about the approach that the author of a paper took:

And his solution, while certainly more concise, is... very terse. Very terse. And I don't know if I could read it.

This says something, because Eric Normand is an expert on functional programming. If your code is so terse that even a domain expert has to strain to understand it, that probably isn't what you should aim for. In that same podcast episode, Normand ponders whether functional programming languages/code are too terse.

This reminds me of something that Eliezer Yudkowsky wrote in Explainers Shoot High. Aim Low!:

A few years ago, an eminent scientist once told me how he'd written an explanation of his field aimed at a much lower technical level than usual. He had thought it would be useful to academics outside the field, or even reporters. This ended up being one of his most popular papers within his field, cited more often than anything else he'd written.

The lesson was not that his fellow scientists were stupid, but that we tend to enormously underestimate the effort required to properly explain things.

I think that this is a good thing to keep in mind when writing code.

Dumb it down?

"Know your audience" doesn't necessarily mean that you need to dumb everything down.

Think about a college professor teaching undergraduates. In the beginning of the semester it probably makes sense to take things slow and be very deliberate in explaining things. But as certain terms and concepts start becoming familiar to the class, it then starts making sense to sprinkle them in more liberally.

Similarly, when there are terms and concepts that are hard for people to grasp, rather than avoiding them entirely, it probably makes sense to slowly introduce them so that the class can learn them and use them in the future.

I think the point, like always, is that there are tradeoffs involved, and that you need to be aware of them and consider them in your decision making.

Visuals

What tools do educators use to teach? Powerpoints. Textbooks. Lecture videos. Demonstrations. Quizzes. Office hours. Homework assignments. Diagrams. Simulations. Etc, etc. Do any of these tools make sense for us to use as developers when we're writing code?

Some of them are impractical. Eg. a full lecture video. Others are just sorta silly. Eg. Quizzes. But I think that some of the tools are, at the very least, promising.

Recordings

Let's reconsider lecture videos actually. It would be impractical to have an in depth lecture about every 10 line function you write. But what about for a larger chunk of code? I could see it making sense for something like a lambda function, or an important module.

In fact, I think that something similar is already happening. One of my favorite little tricks when I'm working in a part of the codebase that is unfamiliar to me and I need help understanding is to use git blame. I see who wrote most of the code, DM them on Slack, and hop on a call where they give me a little ~20 minute walkthrough. I know that I find it to be incredibly useful. So then, why not record a walkthrough like this and post a link to it at as a code comment at the top of the file?

I think the biggest reason is maintainability. As the codebase evolves, the video will grow stale and out of date. When something like code comments grow out of date it is easy enough to edit them, but for a video, you can't really go in and edit the slice from 17:34 to 21:40. At least not easily.

I have a few counterpoints to this objection. Yes, it'll grow stale eventually, but so what? If the code changes are minor, the video still probably is fresh enough where the benefits outweigh the costs. If the code changes are major, then you can take 20 minutes and record another walkthrough. And even if for whatever reason the team is not on the same page and ends up not recording a new video after major code changes, I don't see that causing any major harm. You just end up having a code comment pointing to an out of date video. If someone clicks it and starts watching it, they'll probably realize pretty quickly that the video is out of date and decide to stop watching.

Another objection I'd anticipate is that recording a video is time consuming. For that, I call bull shit. We already spend many hours trying to produce high quality code: upfront work, refactoring, code reviews, etc. Taking 20 minutes to talk to the camera in a casual, stream of consciousness manner is small potatoes compared to the rest of the time you spend. I think that what's really going on with this objection is that a video merely feels imposing, like a Big Thing to do.

Diagrams

Ok, let's try looking at some other tools. What about diagrams?

I think that diagrams are fantastic! Fortunately, they are already a thing that has gotten some adoption. Particularly to illustrate how different modules connect to one another, at an architectural level.

However, I get the feeling that diagrams are still pretty underutilized.

Here's an example of how it can be used for something at a less architectural level. For the Container With Most Water question, having a visual reference like the following is super useful:

One area in particular where I see them as underutilized is with front end code. I think it'd be cool to have diagrams accompanying code so that you form a picture of what eg. a React component is going to look like. Yes, you could already do that by opening up a web page and using the inspector (or just common sense) to figure out what code maps to what UI, but there's some friction in doing that. Maybe it'd be a good idea to reduce that friction.

In particular, what I have in mind is the following. There'd be this plugin in your text editor. When your text edittor sees a code comment followed by a URL that ends in something like .jpg...

// https://example.com/code-images/modal.jpg

There would be a little collapse/expand arrow to the left of it, where when you click to expand it, it would display the image inline! I think that could reduce some friction.

My friend Brendan Long had a cool idea about having some sort of plugin autogenerate such diagrams/images using some sort of mock data for the components.

Overall I think this line of thinking is moreso a hypothesis than something I feel strongly about, but it sure is interesting!

Stepthroughs

I have something like this in mind:

I don't know of any tools that currently exist that would let you do this, at least not in a flexible enough way where you'd be able to adapt your stuff to it, but I envision a future where such tools do exist and are used!

Clean code

Let's step away from this fringe stuff about videos and whatnot and come back to a world that is more familiar to us. A world where we try to write our code in such a way that it is understandable to others.

Like I started this post off saying, perspective means a lot, and if you think of yourself as someone who is educating the rest of the team on how this code works, I think that a lot of commonly accepted ideas about clean code flow naturally from that. Descriptive variable names, modularity, propper indentation, etc. I'll steal some examples from clean-code-javascript.

Bad

// What the heck is 86400000 for?
setTimeout(blastOff, 86400000);

Good

// Declare them as capitalized named constants.
const MILLISECONDS_IN_A_DAY = 60 * 60 * 24 * 1000; //86400000;

setTimeout(blastOff, MILLISECONDS_IN_A_DAY);

Bad

const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(
  address.match(cityZipCodeRegex)[1],
  address.match(cityZipCodeRegex)[2]
);

Good

const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [_, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);

Bad

function emailClients(clients) {
  clients.forEach(client => {
    const clientRecord = database.lookup(client);
    if (clientRecord.isActive()) {
      email(client);
    }
  });
}

Good

function emailActiveClients(clients) {
  clients.filter(isActiveClient).forEach(email);
}

function isActiveClient(client) {
  const clientRecord = database.lookup(client);
  return clientRecord.isActive();
}

Bad

function addToDate(date, month) {
  // ...
}

const date = new Date();

// It's hard to tell from the function name what is added
addToDate(date, 1);

Good

function addMonthToDate(month, date) {
  // ...
}

const date = new Date();
addMonthToDate(1, date);

Code comments

I tend to agree with the conventional wisdom that things like journal comments

/**
 * 2016-12-20: Removed monads, didn't understand them (RM)
 * 2016-10-01: Improved using special monads (JP)
 * 2016-02-03: Removed type-checking (LI)
 * 2015-03-14: Added combine with type-checking (JR)
 */
function combine(a, b) {
  return a + b;
}

positional markers

////////////////////////////////////////////////////////////////////////////////
// Scope Model Instantiation
////////////////////////////////////////////////////////////////////////////////
$scope.model = {
  menu: "foo",
  nav: "bar"
};

////////////////////////////////////////////////////////////////////////////////
// Action setup
////////////////////////////////////////////////////////////////////////////////
const actions = function() {
  // ...
};

and obvious comments

function hashIt(data) {
  // The hash
  let hash = 0;

  // Length of string
  const length = data.length;

  // Loop through every character in data
  for (let i = 0; i < length; i++) {
    // Get character code.
    const char = data.charCodeAt(i);
    // Make the hash
    hash = (hash << 5) - hash + char;
    // Convert to 32-bit integer
    hash &= hash;
  }
}

should be avoided. However, I'm not so sure that I agree that good code mostly documents itself. It's been something that I've always pretty much assumed as a default, but a) I've been noticing people at work use explanatory comments in cases where I wouldn't think to do so, and I've found them very helpful, and b) this "think like an educator" framing has got me feeling like optimistic about their value.

I don't want to make the argument that they are underused. That'd be hard. Instead, I just want to propose it as something to re-evaluate your stance on. Next time you write a function, ask yourself whether you think someone else would have a hard time understanding it. Ask yourself whether there are comments you can add that wouldn't be redundant and bloated. Ask yourself what an educator would do.

Postscript: Think like a usability designer?

This post was about thinking like an educator when you are writing code. I think it's a cool idea, but is it the only cool idea? Fill in the blank: "Think like a ____ about code quality". What else makes sense?

The big thing that comes to my mind is "usability designer". Why? Because I've always thought that user testing is something that people should do in codebases!

Think about it, in my concluding paragraph I say:

ask yourself whether you think someone else would have a hard time understanding it

Usability designers do this sort of stuff all the time! It's their job! Except they don't stop there. What else do they do?

User testing! They don't just assume that people will understand how to use their products. They test it out. Put it in front of actual users and see where the friction points are. Why don't we do this with code?

42

8 comments, sorted by Highlighting new comments since Today at 3:50 PM
New Comment

Fill in the blank: "Think like a ____ about code quality". What else makes sense?

From my own experience, 'think like an open-source maintainer':

  • One goal is to make it easy (and fun!) enough to work with the code that others will use it and contribute to it - voluntarily, not because they need to for a job or a class. Clarity and brevity are virtues, as is functionality.
  • The code, in itself, is an instrument for the education of users (and contributors). Readers should be enlightened about the purpose of the code, and at a more granular level focus on "why" rather than "what". I use "how" comments roughly in proportion to the black-magic-ness of the implementation, for myself as well as others.
  • Remember that the code is only part of a larger system. Without good docs, runtime UX, community outreach, responses to bug reports and feature requests, etc., the system is less impactful. Code is not an end in itself; only the means to an end. (when thinking like an OSS maintainer; code may of course be an end-in-itself in other ways)

That's amazing! Great idea!

It seems to me that your post is missing something: what specifically do you want people to learn?

I am an educator - currently teaching CS research and Python for a general audience - and I find it's easy to get people to learn... and surprisingly hard to have everyone learn the same specific thing (to a degree they can use, not just repeat on a test <=2 weeks later). Circumstances to discover and apply the ideas for themselves are best, followed by a variety of communications strategies like the videos, diagrams, and docs you mention.

For code-quality I think you're asking "how do I help colleagues with different expertise work with this code", and to me that calls for thinking about communication rather than education. Education includes communication, but it's larger, slower, harder, and fortunately not necessary here :-)

It seems to me that your post is missing something: what specifically do you want people to learn?

Hm, I'm not sure I'm following. It sounds like you're asking this from the perspective of "I'm a developer. What specifically do I want to teach the other developers about how this code works?" The answer to that totally depends on what the code is for. I do think it would have been better if I had a running, concrete example to reference throughout the post though.

For code-quality I think you're asking "how do I help colleagues with different expertise work with this code"

Or is this specific enough to count as an answer? If so, yes, that's what I'm going for.

to me that calls for thinking about communication rather than education

That's an interesting perspective. I don't have a good enough grasp of what each term really means, but to me education is a type of communication that has a connotation of being about something that is harder to grasp. Ie. if I'm teaching you calculus, that's education, but if I'm figuring out a time for us to meet for coffee, that's communication. With that, education seems like a better term for what I'm going for than communication, but it's very possible I'm using the terms improperly.

A thing about "know your audience" is that you may be able to choose your audience to some extent. Like, if you're an early employee at a startup, you might be able to decide where to fall on a spectrum like "use advanced features, limit your hiring pool to people with three years' experience in the language" versus "don't use those features, be able to teach people the language after hiring". Or if you're writing an open source project, instead of a hiring pool it's a pool of potential contributors.

(And as is often the case, you might be making these choices without realizing.)

For step-throughs, the best tool I know of is Philip Guo's PythonTutor: Python Tutor - Visualize Python, Java, C, C++, JavaScript, TypeScript, and Ruby code execution. (As you can see, it's been extended to a few other programming languages - I can only vouch for the Python version.)

To save you a click, I've copied the example visualization on the homepage below. It shows all of the variables in the entire stack at the specified point in the execution.

It's all auto-generated, so it doesn't support more complex visualizations like your Container With Most Water example. But maybe it could be extended so that the user could define custom visualizations for certain objects?

I really like the idea of an editor plugin to display images from URL's inline. It seems like one of those 80/20 ideas where you could probably think of something fancier but just having the simple thing would be a major improvement.

Great point about it being 80/20. I had that thought in my head but didn't really get it out in the post.