Why Teaching is The Only Way to Write Better Code

Why Teaching is The Only Way to Write Better Code

return !!someVar ? (
  someArr.length ? someArr[0].someProperty : somethingElse || 'cheese'
) : foo ? getFooBar(foo) : setAndGetBar(foo);

This horribly contrived nested ternary statement could be the best way of expressing the solution to your problem (it probably isn't, but let's pretend). Should you use it? Or should you unpack it into several named variables and if statements? You've probably already answered in your head with your own personal opinion, but you actually shouldn't answer yet because you don't have enough information — I haven't told you about the team that will be reading it.

Every day we go to work and we write code, and as we're writing code, we're consciously or unconsciously asking ourselves questions about the code we're writing:

  • Is it the right solution?
  • Are there any bugs I've missed?
  • Is this function doing too much?
  • How will I make sure this is working in production?
  • Should this wake someone up if it fails?

But a question we often forget is will everyone on my team understand what I've written and why?

Code is read far more than it is written, by an order of magnitude at least. It's imperative that our code is understandable to everyone mid-level and up (we don't expect juniors to understand everything, and we don't expect them to work as independently). If we're writing code that not everyone understands, then we're not team players, we're not helping the business, and while it might feel like we're creating job security if we create systems that only we can understand, we're actually building a lightning rod for our back as we make ourselves the bottleneck.

If people can only contribute to code that we've worked on by asking us questions about it and having us explain it to them, we will eventually find ourselves overwhelmed with queries from others and no time of our own to write code. Or you could ignore your colleagues and continue writing indecipherable code, but that is definitely a career limiting choice. Eventually, you will become the bottleneck to all significant work, and the business will start breathing down your neck to get everything done. You will be stressed, and you might feel like a rockstar, but it is a false high.

When I find a bottleneck engineer in a system (a Brent, I call them, from a character in The Phoenix Project), I politely ask they take a break from the project for a few weeks, stop answering questions about it, and ask for their help elsewhere. Inevitably, other engineers need to take up the slack, and struggle through the code, but eventually, knowledge about the system is shared, the bottleneck mitigated, and the team starts having opinions about how to improve the system so its simpler to work with.

So what do we do when we want to write more advanced code?

One word: Teach.

And that is no easy thing to do! But a rising tide lifts all ships, and believe me, it's much better to be the rising tide than a bottleneck. You won't be the rockstar people need and put up with, you'll be the champion everyone respects and loves working with.

How do we teach? Let's run through some techniques that I've found extremely useful in teaching others and raising the capabilities of the team. You'll likely find that you're naturally stronger at some of these than others, but you'll also find some of these work better for some members of your team than others. It's important to work with your other more senior colleagues to train the team with complimentary teaching styles.

Pair Programming

This is one of the most effective ways I've found to close skills gaps on a team. When pair programming, each of you sit together (or screen share) and view the code together in real time. If you only have one keyboard, then one person can act as the driver while the other acts as the navigator. The driver physically writes the code, while the navigator gives feedback and suggestions. It's important to switch roles frequently, commonly with each pull request.

This might sound like it will take longer, but overall it can be much faster. Pair programming tends to produce less defects, shares knowledge, results in simpler solutions, more maintainable code, and everything pair programmed has effectively been reviewed, meaning pull requests are merged faster rather than sitting in review limbo.

When I'm pairing with someone junior to me, I will often have them drive so that they can learn by doing. This helps them develop some muscle memory for the patterns and solutions, and helps them retain more of what they learn. When I'm driving, I ask a lot of questions, if I suggest an idea and write it out, I ask for their thoughts on it. I ask them what might be wrong with my idea, or "If you would change one thing about this, what would it be?" which is a great question to ask because it forces an answer while shielding them from the anxiety of potentially offending you.

Yes, some people will struggle to offer suggestions to colleagues they see as senior or more knowledgable. Be cognisant that your pairing buddy might be nervous if this is new to them. If so, make some harmless jokes about your own code, compliment them on their suggestions, and ask them questions that show you care about their opinion! Junior developers have beginners mind and can see things anew. At the same time, having to explain things to someone helps you learn too; it deepens and solidifies your knowledge.

Exercises and Kaizen

This is a great way to strengthen new or shaky concepts on a team. You can create coding exercises and challenges for a team that require them to solve problems in new ways. The team works on these challenges during their professional development time, and hones their craft before applying it in production. If you have a pattern or solution in your toolkit that your team is unfamiliar with, this can be a powerful way to bring the team with you so you can use it in production.

It's important that you don't tell people how to do things. We learn best when we're challenged; there is a difference between synthesising new ideas, recollecting knowledge, and recognising knowledge. This is why flash cards and spaced repetition work, because recollection strengthens pathways better than recognition. You have to struggle in order to learn, hence the increasing delay between challenges in spaced repetition. But more powerful than that is making the leap of discovery yourself. When you give your colleagues just enough information to have the "Aha!" moment themselves, they have a deeper and stronger understanding of the concept or solution. They're also more invested in it.

Instead of giving people the solutions to challenges, let them come up with their own solution, then ask lots of questions about their solution, offer some feedback, and only a few suggestions. Gently nudge people towards a better solution and then let them try again. It will take a few cycles, but don't rush it! You want people to make mistakes and get burnt here, they will learn much better, and won't do it in production.

On my teams, I like to run weekly catch up sessions where we all share our progress on these exercises. It's mob programming for professional development, it's highly effective, and you will learn a lot about the teaching and learning styles of your colleagues.

Talks and skill shares

Giving talks and presentations on a topic can be a great way of drumming up interest or awareness in that topic, approach, or pattern. To be clear, it isn't a great way of teaching or getting that knowledge deeply seated in your colleagues such that they can use it in production. It is a great way of introducing new and bigger concepts, and for practicing giving talks! If you want to introduce a new concept to the team, you might give a skill share first, then have the team work on exercises, and then pair with people as they implement it.

Remember, when you're giving a talk, you're giving a high level overview of various concepts. Your slides shouldn't contain too much text, at most three dot points relating to a single idea per slide. Please, don't just read what is written on the slides! Nothing puts people to sleep faster than listening to someone read off a slide deck. Your objective isn't to convey every detail and minutiae in a talk, your objective is to get people to ask you good questions at the end!

It's okay if the slides don't make too much sense on their own. They're only there to act as prompts.

Reading lists

A development team that reads together grows together. It's important you all share the same vocabulary and toolsets of concepts. Having a common set of books that the entire team has read helps with this immensely. If you've all read Domain Driven Design Distilled then you can have a conversation about ubiquitous language, domain boundaries, and domain mappings, and know that everyone is able to join in and engage.

You can always stick the reading list in git and have everyone contribute to it. Getting people to actually read them is the other half of the challenge. A little healthy competition here can be helpful, but be mindful that your colleagues will be at different life stages. A junior with no partner and no family might have a lot more time for reading than someone with kids and a newborn. That's just a fact of life.

If you keep talking about the books you are reading, and taking an interest in the reading habits of others, asking for suggestions and recommendations, you can encourage your team to read the reading list, especially if they've contributed to it themselves. Maybe even add it to your weekly learning exercise catch up.

Asking questions instead of making statements

I've mentioned this briefly throughout this article, but it's such an important concept it deserves its own section. The best way to teach someone anything, is through questions, not statements. It can be very tempting to give the answer, make a statement about the "correct" way to do something, because it makes us feel good.

It's not our job to make ourselves feel good and show off our knowledge, and it isn't how we get promotions either. It's our job to discuss ideas with our colleagues, fill knowledge gaps, and bring people on the journey with us rather than mailing them a postcard from the destination. You do this by asking questions.

Plus you never know, you might get an answer you weren't expecting. You might come up with a new idea or solution, or find out that your idea wasn't so "correct" after all. That's a good thing. Asking questions gives us the opportunity to find out we are wrong. You absolutely want to know when you're wrong as early as possible.

You'll also have better relationships with your colleagues. They will feel more respected, and you will respect them more, because you actually create opportunities for mutual learning. We all learn best in a psychologically safe environment, and questions help foster that sense of safety.

I need to be clear here: These NEED to be honest, open ended questions. If you ask too many leading questions it can come across as condescending. Ask questions with more than a single word answer. Ask questions that need an opinion, or are answered with another question! Don't ask yes or no questions, ask questions that query various elements of quality — performance, reliability, robustness. Ask questions that move us to action. Ask "How could we implement observability here?" or "How could we make this more performant?"

Asking questions is an art form that you can spend your life perfecting, and has implications well beyond software engineering that are out of scope for this article. So make a habit of asking questions, you can only get better at it with practice.

Eating the elephant

There's a lot to take in here, and a lot to do. How do we do it all? How can you achieve what might be a big undertaking? The same way you eat an elephant — one piece at a time. Introduce one of these concepts at a time. Get good at it, get your team good at it, and then introduce another.

If you go into the office and introduce pair programming, reading lists, programming exercises, and skill shares all at once, you will fail. It might take a week or a month for each of these to stick. You might find your colleagues are resistant to some of it. Figure out what makes sense for your team, and then start experimenting.

Taking responsibility

You might be reading this and thinking "But Anthony, I'm just a junior/mid-level/senior developer, I can't do all this, I don't have the authority." You don't need any. You may need to persuade people, you may need managers to give you time, you may need scrum masters to help you protect professional development time, but leaders don't need authority because they lead from the front.

If you want to be a great developer, you're going to need to develop a leadership style that suits your personality — and yes quiet introverts can absolutely be effective leaders, quiet leadership is a real thing. You'll find that as you take responsibility, as you show leadership, people start giving you authority (and promotions). This is a double edged sword, authority is anathema to leadership. If you have any, avoid using it at all costs.

You also don't need to know everything or take responsibility for everything to be a great teacher. You just need to care about other people's learning, knowledge, and success. If you start from there, and build on that fundamental principle, you will succeed in being the rising tide that lifts all ships.

If you have any techniques that helped you share knowledge and expertise, please let us know in the comments!