Learning (and teaching) how to be a good programmer
By: Alexandre Borrego
Tags: Teaching And Learning, Programming

Whatever the context (be it at Blue Harvest, Hack Your Future where I used to give Sunday lessons, or at work, where I spend as much time as possible mentoring juniors), I noticed one thing: teaching and learning programming (or a programming language in particular) is a lengthy process but a relatively simple one. There’s only one rule, the same that applies to learning natural languages: practice.

Teaching and learning how to become a good programmer, however, is a much trickier task.

What I mean by “good programmer”

There are many definitions and criteria out there for what a good programmer is, but this quote from Martin Fowler sums it up pretty well for me:

“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”

Robert C. Martin has been honing the same idea throughout his books and talks about clean code: being a professional software engineer isn’t so much about managing complexity at the machine level (i.e. merely getting code to work) as it is about dealing with it at a human level (i.e. writing maintainable and predictable code that is easy to reason about).

A lot of theory has already been written about what this all means (if you didn’t get the hint, read Fowler and Uncle Bob’s stuff), but to master it, to put it into practice, merely following an enumerable set of guidelines and principles just won’t do. This is where teaching comes in.

What teaching code is about and why we need it

Knowing what you don’t know

It is preferable — and to some extent even necessary — to undertake the intellectual journey of learning to “write code that a computer can understand” alone.

As a mentor, I believe that my role in this area should be mostly that of a curator or a guardrail: I’ll orient my students or mentees to the resources (books, articles, tutorials, etc.) and concepts they need to get started or unstuck. I’ll help them not to waste time and to stay on track, providing them with an outline of what they don’t know. But it’s crucial that they do the heavy lifting, come to solutions on their own and enjoy those very important, very personal aha moments.

This has the added benefit of letting me focus on what’s key and what written and video materials are not so suited for: instilling the right mindset.

“You don’t become a software craftsman by learning a list of heuristics. Professionalism and craftsmanship come from values that drive disciplines.” — Robert C. Martin

Of course, the heuristics — the plethora of acronymic paradigms (OOP, DDD, TDD, BDD, AOP, etc.), design patterns, principles and language-specific best practices — pave the way. But the core of it all lies in more fundamental mental constructs that require a discursive approach to be fostered.

“Whatever is well conceived is clearly said”

More than anything, being a good programmer is about tackling conceptual problems methodically. Most of that happens outside the IDE: clean code is a byproduct of thinking clearly and writing it should feel effortless.

Passing on that mental discipline requires more questions than answers on the part of the teacher, in a Socratic kind of way. While the challenge of learning heuristics lies with digesting a large pool of information, acquiring a certain mindset can only come from challenging one’s perspective and approach to problems.

When I see a mentee struggling with an issue or coming up with hard-to-read code (which is quite often the same thing), I’ll start by pulling the thread of their reasoning, asking them to describe, step-by-step, what they’re trying to achieve. The goal is for them to lay out, either mentally or on paper, the constitutive parts of both the problem and the solution in the most straightforward way possible.

Getting to a clean representation of the task at hand is usually not too difficult if you focus on building a solution composed of very simple, very dumb components — which will ultimately result in very simple classes, functions, loops. Doing this through discussions makes that process even easier, as the constraints of verbal communication will tend to ensure a certain degree of simplification.

This is unfortunately too often overlooked, especially by junior developers who “just want to make it work” and “clean it up afterwards”. If I can convince you of only one thing though, please let it be this: refrain from writing code until you’ve built a clear and simple picture of its overall structure. Not only will you solve problems faster and faster as this habit grows, but other developers will understand your solutions with less and less effort.

These discussions with my mentees and students also help me to improve my coding skills and clarify my own outlook on programming. This famous quote from Richard Feynman comes to mind: “if you can’t explain something in simple terms, you don’t understand it”. I don’t know whether teaching code made me a “good” programmer, but it certainly makes me a much better one. And I can only recommend trying to explain or simply discuss the stuff you learn and know to someone else as often as you can, even if you only just started programming.

In practice

What getting the right mindset means for any given person depends entirely on their particular background and experience. It goes without saying that a CS graduate fresh out of the university and a self-learner with a couple of years of professional experience will require very different approaches. Mentor and mentee must figure out the mentee’s needs together, iteratively, through short feedback loops and open discussions.

At Blue Harvest, we tried to achieve this flexibility by coming up with a set of guidelines rather than a strict plan for our coaching program. Something flexible enough to let us quickly explore new avenues and tailor the format of our coaching sessions to the engineers who would join them. And — maybe more importantly: something centered around actual practice and real-world engineering problems.

Learn and teach by doing

Once again, coaching should be about breaching the gap between the heuristics and their application. Abstract knowledge of the SOLID principles for instance is pretty much pointless without a holistic and instinctive understanding of how to apply them to concrete problems.

To tackle that, we built our coaching program around a real world project. Instead of coming up with our own, we chose the realworld.io example application. It has enough moving parts to offer a healthy variety of problems and raise some fundamental and generic questions, yet the requirements and features are very straightforward and can fade into the background.

The goal was really not to deliver a functioning app, but rather to have a pretext for practice, code reviews, pair programming and informal chats about translating abstract principles into lines of code in a very tangible way.

Save time with good educational materials

Whenever we could, we’d orient our coachees towards educational resources to get the heuristics out of the way and have time to focus on the rest during our sessions.

The group didn’t need an actual presentation of the syntax, as all of them had experience with another language and at least once tried their hand at JavaScript. But if you do need a formal language introduction, cheat-sheets and videos like those of Derek Banas’ Learn in One Video series should be enough to get started with your first program. Keep them at hand and start hacking. Proficiency will come later.

As far as libraries and frameworks go, we’d briefly introduce them by giving an overview of their philosophy and then refer to their documentation, without going into detailed expositions of their idiomatic usage. Learning new libraries, frameworks and APIs on-the-fly is an essential part of the job. So if lack of guidance in that area becomes a problem, it makes more sense to teach how to effectively parse docs in general than painstakingly walking students through this or that specific library.

Lessons and discussions

Typically, short talks and/or chats, sometimes with live coding, about general topics (e.g. micro-service architecture, databases, security, language-specific use cases, etc.) would either kick-start the day or wrap it up. Far from being purely theoretical, they were meant to shed light on the whys and hows of the code we were writing or about to write.

Some other areas — like functional programming, git best practices, unit-, integration- and functional testing, etc. — were also on the menu, but we decided to scrap them as the participants asked for other specific topics to be discussed and especially enjoyed the time spent coding (so we made sure they had plenty of that).