Designing Great Ruby Practice Exercises

Joe Mastey
The Apprenticeship Community
5 min readJun 9, 2015

--

Most ruby coding exercises just aren’t very good.

Actually, that’s a bit unfair. As a new rubyist (or someone looking to sharpen your skills on, say, testing), you have a huge number of decent options for learning the grammar of a language.

On the other hand, if you’re a new programmer, what you need is not primarily grammar (though you’ll need that as well). What you need to practice is composition. And for this, most websites and books dedicated to learning ruby fall completely flat.

By focusing on grammar and fill-in-the-blank questions, new learners often end up in the unenviable position where— while they can write a mean koan — they have a lot of trouble when presented with “real” problems. This isn’t a failing on their part, and as mentors we can do better.

So why do ruby exercises fail so badly, and how can we improve them?

Hard Skills and Soft Skills

I wrote a bit about the Little Book of Talent a while back, but let’s expand a bit on it. The most pertinent takeaway here is simply that programming requires both hard skills and soft skills.

Hard skills are those that require repeatable precision. Using vim, for example, is not a matter of improvisation or problem solving. Once you’ve memorized the keybindings, if you can hit them reliably, you’re good to go. And they’re best trained by precise repetition and muscle memory. Recognize which action is appropriate, then slowly and precisely invoke it. Repeat until you’re sick of it and can do it in your sleep. Like doing scales on a guitar, this sort of basic practice makes manual skills into automatic ones.

Soft skills, on the other hand, are characterized by the need to improvise and apply pattern matching. The middle game of a chess match is a great example of a soft skill. While chess players often practice a set of rote moves and counters for the beginning of the game, it’s not long before the complexity of possible decisions makes memorization impractical. At this point, a chess player has to improvise and experiment to defeat her opponent. For this reason, there aren’t many books of “problems” for the middle game.

Similarly, when we write code, it doesn’t take long before we get to a confusingly open set of choices. Koans and katas can help us write a line of code, but not to choose which line of code to write. According to the Little Book of Talent, the best way to train these soft skills is by allowing learners to experiment within a challenging environment. That’s where most resources for learning ruby fall short: you can’t improvise with fill-in-the-blank questions.

That’s how we approach the exercises for LevelUpRails. Over time, we’ve honed most of the questions to provide opportunities for improvisation, and learners have let us know how much of a difference it makes. And in that time, we’ve learned a few others things that I’d like you to consider.

Five Steps to Better Programming Exercises

To start, we try to keep each exercise focused. Part of the reason training on production is ineffective is that it’s hard to repeatably find bugs to teach particular skills. Instead, we pick a small set of skills and weak areas that we’d like to address as our focus, and back into the right exercise to illustrate them (in teaching, this is called Understanding By Design). Exercises need to force users to use the target skills and illustrate the problems that arise when we don’t use them.

Focus also implies that learners should approach exercises with most or all of the tangential skills necessary to succeed, or they’ll get bogged down with irrelevant details. This was the case historically with one of the original LevelUpRails exercises, which tripped up users who weren’t familiar with statistics ahead of time. When this happens, it’s best to just split the exercise into multiple smaller exercises, each with their own focus.

Next, we ensure that exercises are substantial. While fill-in-the-blank type exercises are attractively easy to grade, we’ve found them to be ineffective for learning effectively.

We target exercises that usually require around a few days to complete. Assignments of this size have enough content for learners to really stretch themselves, without letting them get too lost. These assignments mimic the length of real work assignments. They also expose the kinds of maintainability problems that clever shortcuts cause, before learners can make a habit of them.

Multi-day assignments also illustrate the difference between grammar and composition. You can fumble your way through a dozen lines of code with just grammar. To finish something in the hundreds of lines, you’ll have to learn to compose those pieces together.

Thirdly, we want exercises to be an attainable stretch. Too easy, and they’re a waste of time. Too tough, and learners flail and give up altogether. Finding a balance here is especially tough as different people come in with different levels of experience. In the past, we’ve definitely had greener programmers get stuck. The best solution I have so far is to give them enough personal support to get them through the rough patch and back on track.

We also hold more experienced programmers to a higher standard. Juniors can get away with code that’s a little too complicated, but seniors will get busted on it every time. We keep the requirements flexible enough that while juniors can focus on writing a specific solution, seniors naturally drift towards more generalized answers. And when those answers are over-architected, we can productively guide them towards something simpler.

Next, we try to make exercises recognizable. So, even though the problems themselves are fake (and often include dinosaurs and bombs), they have the same substance as real-world assignments. Remember, we practice soft skills by exploring in a challenging environment, but we have to be able to apply those lessons back to the real world.

In LevelUpRails, the dinosaurs are really just window dressing. Behind that contrivance is an exercise about parsing and normalizing data, separation of concerns, and filtering logic. Our CSVs are filled with trivia, but who cares? The issues are the same ones learners will face in the “real world”.

As another example, to teach rails, we force developers to consume a marginally crappy API, wrap it, transform it, and republish it. This is as much of a microcosm of our real work as I can handle without crying a little.

Finally — and we can’t skip this step — we absolutely make sure that exercises are interesting. If you’ve ever taken a macroeconomics class, you know exactly how hard it is to learn when the material is dry. If that material is complicated to boot, you’re going to fail.

So, we try to wrap a bit of fun into each exercise. This is something that most ruby training resources do well — zombies and chunky bacon abound. We use dinosaurs, bombs, and nefarious plots by local weathermen.

Sometimes we do well at these criteria, sometimes we fall sadly short. All in all, it’s an ongoing process, and one that we won’t be done with any time soon. You can check out the entire set of practice exercises, which are completely open source. We’re always looking for improvements, so feel free to contribute suggestions.

--

--

Joe Mastey
The Apprenticeship Community

Ruby developer, rock climber. I help technology companies build fantastic cultures.