Lately I’ve been trying to learn C, starting with online training a month ago, and continuing with daily practice. Yesterday, inspired by a personal story I told on Developer on Fire 139, I wrote a C program to draw the Mandelbrot set. The first image it generated, intentionally, didn’t look like much:
You’ll see that the image gets progressively better as I learn more. Not even a metaphor. Or is it?
I’m still getting the hang of C. But I’m doing pretty well with the hang-acquiring. You can too. Here’s my advice.
Bring your knowledge
You’ll have a head start if you already feel:
- Productive in your preferred text editor
- Confident with your preferred revision control system
- Proficient in some other programming language (including writing, running, and listening to automated tests)
Be mildly relentless
Do a tiny bit of C each day. Do more than a tiny bit, if you can. Don’t do less. If you miss a day, since you’re aiming for mild relentlessness, no big deal. Just make sure to do a tiny bit the next day. The tactic here is to bring C to your conscious attention often enough to support the strategy. The strategy: keep C rolling around in the back of your brain all the time.
Get a running start
Learning a new language means learning to navigate in a new environment. How to enter new code? build? run? change? test? organize? The C language is merely one aspect of the world you’re entering. If you’re not careful, your focus will be divided by more than one of the things you don’t know well: a language, a build tool, a test library, an IDE, etc.
If your goal is to learn C, pay as little attention to the other stuff as you can get away with. When you’re getting started, don’t try to choose the best compiler, build tool, test library, or IDE, and definitely don’t try to choose the best way to be installing them. You’re not having those problems yet, so you don’t have enough context to choose and the differences don’t matter much.
Start with an environment prepared by someone else. Cyber-Dojo provides compilers, test frameworks, and a text editor, and runs in your browser. You can be learning C right now.
Cyber-Dojo also includes code katas. Pick one at random. Write a trivial failing test and make sure it really fails. Then read the instructions, think a little, and code a little.
You might have an idea of what to do and not know how to express it in C. With a failing test, you can guess and check. If it doesn’t compile, it’s not valid C; try again until it compiles. If the test still fails, it’s C that doesn’t match your intention; try again until it does. Voilà: you’re learning C.
If you think taking a little time to work through a C tutorial would help you move faster, do it. If you think having a C reference at hand would get you unstuck more quickly, find one.
When you’re satisfied with your solution, Cyber-Dojo has plenty more katas. If you’d rather solve a real-world problem, maybe you’ve got one in mind. Or maybe someone you know can ask you for help with something.
Invest in your environment
As you practice, you’re also learning what would help you be more productive. For instance, your preferred text editor would probably help! When you’re making good progress in Cyber-Dojo, you wish it could go a little faster, and your tests are green, it’s time to continue the kata offline.
On your machine, install the same test framework and a compiler (ideally also the same). You might have to twiddle the code to get it to build on your system. Once the tests are green again, return to the kata: think a little, test a little, code a little. You’re learning C again — and now that you’re in an environment you control, it’s worth (1) noticing what gets in your way and (2) taking action to improve it.
If you want to run the tests frequently, teach your text editor to run
them with a keystroke.
If you want it to be easier to undo back to the last green state,
init and start committing whenever you’re green.
If you’re frustrated by how long it’s taking to figure out a particular improvement, it’s worth noticing that too. Leave it for later — if it keeps being annoying, you’ll get more chances to solve it — and get back to the cycle of learning: think a little, test a little, code a little, commit on green.
Even if it’s sometimes frustrating, incrementally removing bottlenecks to your productivity is incredibly worthwhile.
…Especially in tools that can support you
Your C compiler is willing to warn you about lots of potential mistakes, and to treat warnings as errors. You only have to ask.
Your editor might be able to show the same warnings and errors, in the context where you made them, before you try to invoke the tests. Seeing mistakes earlier helps you go faster. Try it.
Your memory-management logic is easy to get wrong.
Valgrind can tell you when and where you’ve made
You’ll want to run it less often than your unit tests, because it’s
slower, but when you’re dealing with
free() you’ll want
to run it pretty often.
(The longer you go between Valgrind runs, the harder it’ll be to figure
out where you screwed up.
git bisect might help.)
…Especially in your fastest feedback loop
You need to be able to rely on your tests to tell you when and where something is wrong.
If it’s hard to spot when a test is failing, fix that.
I noticed myself having this problem with
so I wrote a
tiny wrapper to run Unity tests from Perl’s
which paints failing tests red in a way I’m used to.
If it’s hard to know what a red test is trying to tell you, fix that.
for instance, when
ck_assert() needs to tell you something, it
can’t say much.
Maybe there’s a
more precise assertion available.
ck_assert_msg() at least lets you tell it what to tell you,
so you’ll understand what to do faster the next time it’s red.
Invite your friends
Pairing accelerates learning. During my 3-day online training a month ago, I worked with a remote pair the entire time. Whenever you can find someone to pair with you, do it.
Experts accelerate learning. That’s one of the reasons I took that training course from James Grenning. More recently, I asked some NetBSD developers — who understand from experience how to see the costs and risks of development in C — to review my code and suggest what I might pay more attention to. I’ve gotten some very thoughtful, articulate comments.
Exercism.io has a nascent C track, and its exercises come with ready-made tests you can enable, one at a time, to simulate part of the thinking that goes into TDD. Since you’re not trying to learn a thinking technique, this helps you focus on learning the language.
Exercism is also a way to invite code review. When you post a solution, other Exercism users might notice, read, and comment. Even if they don’t, your code’s posted at a stable URL and you can send the link to anyone you like. Or anyone you don’t like, if you like.
Choose small steps
While trying to draw the Mandelbrot set in C, given that there were lots of things I wasn’t sure how to do, I made extra sure to organize my work into small steps:
- Since I’ve never used GD before, start by generating an image — any image.
- Since I’m not sure GD offers what I need, make sure it can color individual pixels.
- Since I keep opening the image to see whether it’s different, write an approval test to turn red whenever the image has changed.
- Since I want to try plotting an equation, test-drive mapping pixels to (x,y) coordinates.
- Since I think I got that right, try plotting the equation for a Unit circle.
- Since I instead got a weird blocky thing, figure out which coordinate-mapping test I forgot to write, make it pass, and try again.
- Since I got a circle, refactor my (x,y) coordinates to a+bi (complex numbers).
- Since I don’t know C’s complex math functions, compute the Mandelbrot set using C’s ordinary math operations on a and b separately.
- Since that worked, refactor to C’s complex math functions.
- Add some color.
This post isn’t much about C after all, is it. It’s about how to apply what you know, and what you know about what you don’t know, to acquire what you want to know. When you’re trying to learn something, do the fastest thing that gets you a walking skeleton. From there, iterate in tiny steps. As soon as you’re satisfied, stop.
It so happens that the Mandelbrot algorithm itself relies on iteration. The more times you iterate over each point, the more accurate the picture. But there’s no number of iterations that guarantees perfect accuracy, and each iteration costs runtime. So you have to choose how many iterations is good enough, and stop. And that number depends on how fast your computer is, how far you’re zoomed in, and what you want — in other words, on context.
Not a bad metaphor after all.
Not a bad exercise, either, and I’m not satisfied with it yet. I’ve still got plenty of ideas for what I can learn.
This week I ran a workshop. Last week I was in one.
For as long as I can remember, and probably longer, I’ve had a self-directed drive to learn, strong enough to be salient to observers. Lately I’ve become aware that I also have a periodic need for tiny doses of structured learning. Last week made this the third consecutive year I’ve taken some kind of training course. I observed the new pattern when I added James Grenning’s TDD for Embedded C to my résumé and realized that if a recruiter or hiring manager saw nothing but the top three “Education” items, they’d get a remarkably accurate impression of me. I coach, I lead, I Agile, I solve problems, and I test-drive my code.
But not totally accurate. I don’t C.
Not much, anyway. I can think of a total of five bits of code I’ve ever written in C:
- A program to generate the Fibonacci sequence (iteratively and then recursively)
- A JNI library allowing Java programs to create a “link” (symlink on UNIX, shortcut on Windows)
casestatement in the NetBSD/macppc bootloader to allow system administrators to configure kernel behaviors (just like they already could on NetBSD/i386)
- A bugfix for a load-balancing appliance’s web admin GUI that wouldn’t display a particular table in (and only in) Internet Explorer if (and only if) the appliance lacked hardware SSL acceleration, traced to some uninitialized automatically allocated strings in the C CGI program that emitted JSON for the Dojo Toolkit GUI
forloop that instructs ikiwiki, when run as a post-commit hook in a website repository, to do nothing whatsoever when the triggering event was a
cvs add <directory>(because that acts immediately on the repo, does not constitute a commit, and justifiably confuses ikiwiki if not filtered out)
I’m pretty sure that’s everything.
What I hoped for
Last week’s course was designed for people who’ve been developing for embedded systems in C to become acquainted with Test-Driven Development in general and/or in that context. I, on the other hand, am very comfortable test-driving and I wanted to become better acquainted with C. I knew I wasn’t in the course’s target (ha!) audience — but I also knew that someone skilled with TDD can exploit its fast feedback to learn a programming language quite quickly.
With this in mind, I signed up for the course as an act of self-engineering designed to focus 3 days x 5 hours of my attention on the material I wanted to be learning. And I enlisted a pair partner to help me stay focused and un-stuck.
What I did
During exercises, my remote pair and I used Screenhero to share screen, mouse, keyboard, and voice. We didn’t use any particular pairing style, other than a little ping-pong to get rolling. My pair had written some C++ somewhat recently, and often had better guesses than mine about how to say our next idea in C. We both understood when and how to test our guesses as quickly as possible.
During the TDD lectures, we each muted our Screenhero to avoid echoing the incoming audio at each other. I generally continued working on the day’s exercise. Sometimes my pair would join me. Without sound, I’d wiggle my mouse cursor (Screenhero gives each user their own cursor) to indicate I wanted the keyboard.
Because my pair and I had similar levels of skill with TDD (good), C (meh), and pairing itself (good), I didn’t notice silence slowing our pace much. I did, however, notice it constraining the flow of humor.
Each evening, since we hadn’t gotten all the way down the exercise’s test list, I’d continue working (solo) until we had. The following morning, I’d review with my pair.
We’d been doing the training exercises online in Cyber-Dojo, which is very convenient for getting started, especially when the instructor provides ready-made exercises with test-driven supporting code and well-thought-out test lists, along with all the needed tools. Once the course was over, I knew that to sustain my momentum, I’d need to be able to edit, build, and run tests in my usual development machine — and pronto. The day after the course, I created a local git repository, added all three exercises as we’d done them, installed CppUTest, debugged the exercise builds on my particular host OS and compiler, and committed the fixes to
makefiles and code to make the tests green. Then I dumped the next few things I know I want to try, in a sensible order, to a to-do list.
Now that I can run the tests from my usual editor with my usual keystroke, and won’t forget my next goal, I’m confident that my learning will continue.
What I got
I wanted to learn some C. I got what I wanted.
I also got geek joy. When we figured out just enough about bitwise logical operators to test the right thing, make it pass, then refactor to more expressive symbols, that was a programmer’s high. I’ve always wanted to understand this stuff. Given a goal, a fast feedback loop, and a pair, I was able to start understanding it.
The joy was greater because it was shared. Usually I’d consider it risky to pair remotely for three days with someone I’d never met. But if that pair is someone who works for Pillar and thinks learning about TDD in C sounds awesome? I had no doubt in my mind we were gonna have a good time together, and we did.
Some of the joy was meta-joy. Since TDD is a great way to learn design and domain concepts, and I’ve used it to learn one programming language, I hypothesized that it might be a great way to learn another, and that sure feels true. I love it when a mental model holds up!
I’m also feeling joy in my self-mastery: knowing what I wanted to learn, knowing what I needed to start and continue learning it, and arranging to get what I needed. (I talked about when and how I learn, among other things, on this week’s Developer on Fire. I’d love to hear what you think.) And getting better in general at knowing what I want — like spending more of my time programming — and arranging to get it.
There’s a particular refactoring direction I want to take one of the exercises. If it works out the way I hope, I’ll learn some C. If it doesn’t, I’ll learn some C.
We didn’t get to deploy to hardware. I’ve got a Raspberry Pi and an Arduino, never used. I’d like to test-drive a “Hello World” and see it run, then test-drive the basic use of a device-specific hardware feature and see it work. That’ll teach me some embedded basics. Once I’ve done that, I’ll have a better idea what I need to learn next.
Many NetBSD kernel drivers can be recompiled, with no source changes, into standalone userland programs. (See also rump kernels.) This means test failures can crash the process, but never the kernel — so automated test suites can be run freely and frequently, and it might be possible and sensible to test-drive new functionality into the NetBSD kernel. I’d like to try. Maybe I’m closer than I think.
What I’ve been up to
I’ve done very little writing lately. If it weren’t for my guest appearances on other shows, I’d be doing zero podcasting. I’ve been traveling frequently for work and a whole bunch of conferences while effortfully maintaining new food and exercise habits. That’s all I’ve had time for.
After this week’s Path to Agility conference, I’ll be traveling less and working from home more. I expect to be writing regularly again, starting now. Since I’ve been speaking regularly, I’ve been incrementally improving how I present and publish. And since I’m about to take a break from conferences, here’s what I’ve figured out so far.
Notice CFPs of interest
”CFP” == Call For Proposals. Or sometimes Papers, but I haven’t written any of those, and am intimidated by the title.
I mainly become aware of conferences when someone I follow on Twitter mentions them, or sometimes when someone I meet in person recommends them. Last year I assembled a list of “2015 conferences I may want to attend”. I found it so useful that I did it again this year. Suggestions and corrections welcome!
It’s also possible to bypass the usual talk submission process by receiving a personal invitation to speak. I’ve been invited a few times. When this happens, it’s very flattering, and it drives home the continued need for me to participate in the usual process. I’ve had lots of talks rejected. If I hadn’t kept at it, gotten some acceptances, and made the most of them, the folks who’ve invited me to speak to their audiences wouldn’t have known I had anything to offer.
Moral of the story: keep submitting, keep presenting, keep practicing.
Write an abstract
I have not, by any stretch, mastered the art of writing a winning abstract. I do know what I like as an attendee. What draws me in? Among other things, I look for a story from personal experience. That helps me understand how being there might change my own story.
We discussed this on Agile for Humans 032.
Save a copy
If an abstract is accepted, I might want to submit it to more conferences. If an abstract is rejected, I might want to tweak it and submit it to more conferences. Either way, I don’t want to rely on being able to pull a copy out of a submission system. I do my writing in my personal wiki (an ikiwiki instance, naturally), then submit a copy.
Rejected? Try again from the top
Program committees and organizers don’t usually have time and energy to give personalized feedback. If my talk was rejected, it’s up to me to figure out what that might mean and what I might want to do about it. I might try to find someone who gives talks and get their personalized feedback. Or maybe it was one of the many perfectly good talks that get rejected all the time, and submitting verbatim to another conference is worth a shot.
I write a very short page linking to the conference, my session, and where to buy tickets (if they’re still available), and I tag the page talk-pending. That automatically adds it to the list of upcoming appearances on my speaker page, and also (via rss2email) turns it into an email message, which gets sent to my low-volume mailing list.
Sooner or later I’ll mention the talk on Twitter, etc., as well.
[Talk planning goes here]
I have no profound thoughts about how to put together a winning talk. Have experiences, figure out what’s interesting about them, extract the story arc.
I’m not, as yet, attempting to design fast-paced, visually arresting slides. I’ve been trying to keep it simple for myself instead. After several iterations, I’ve landed on a comfortable setup for writing and publishing decent web-based slides.
Last year, after migrating this site to ikiwiki, I was finally able to write new posts in Markdown. I wanted to be able to write slides in Markdown too. Remark.js does that. The easiest way to get started was to use it exactly as directed, then let ikiwiki’s rawhtml plugin preserve it for publishing.
The third time I published Remark.js slides, I noticed it was my third local copy of an old version of Remark.js. So I put it in a central place, adjusted the three references, and upgraded to the latest version — with git commits at each step, just in case I broke something. (I love having my site in git.)
This reduced duplication would certainly simplify my next slide-writing experience. To simplify it further, I wanted to extract the shared HTML and CSS. So I wrote a plugin for ikiwiki that provides the standard Remark.js template and styles, along with ikiwiki-idiomatic ways of overriding the template and styles.
Now I write
my_slides.remark just like I’d write
I locally preview the same way, too: commit, triggering an incremental
render. Quick and easy.
When the talk’s over, I:
- Squash unneeded intermediate commits
- Write a very short post linking to the abstract, slides, and any other related information
- Replace the very short publicity page with a redirect to the new post
This used to be a less streamlined process. Now I do it immediately after the talk.
At some point, it may become important to me to make fancier slides than Remark.js can produce. To do that, I’ll have to develop a new skill.