Copy
View this email in your browser
Arch-Engineer
Code better

 

It's been quiet but busy over here the last few months. I recently finished running a 6-month group-coaching program for advanced students. Expect some of the curriculum I wrote for that to creep into blog posts.

This year, I wrote three papers, including two research tools, and a practical code-search tool I'm excited to announce next year, which recently made its first major success: a search query found a small bug in a million-line codebase, one which was missed by a static-analyzer specifically written for that kind of bug. And personally, I've gotten to test my software-design knowledge in two new domains: VR/AR, and electronics (Arduino).

Now it's winter, and a good time for reflection on my favorite topic: software design.

Foreseeing a fork()


I remember taking a systems programming class as a freshman, where we learned how to create a new process in Unix: you make a copy of the current process, and then modify it to run a different program. "How odd," I thought. Yet the arguments were strong and commonplace, and I quietly accepted it, albeit with reservations.

Earlier this year, I read the essay "A fork() in the road" by a group of OS researchers, which argued what my freshman self had wanted to hear: that the Unix fork() API was a relic from an earlier time, unsuitable for most use-cases, and a design liability.

Their case is ironclad, their proposed alternatives detailed, and I'll let you read it yourself. I want to ask a question with broader software design relevance: how could the original Unix developers have foreseen the problems with fork()?

I teach that the answers to software design questions are to be found not in the code, but in the specifications. And the specification for fork(), written in English, looks something like this:

Preconditions: The calling program is a valid process.
Postcondition: There are now two processes identical to the caller's process. Everything is the same about these two processes, except that one is considered the parent of the other in the OS process tree; fork() returns the PID of the child process to the parent, and returns 0 to the child.

And this is already enough for a trained software designer to recognize the disaster. Because "everything" is a very scary word to appear in a specification.

"Everything" means that the code must interact with every feature added to processes. When fork() was first created, this was not significant, but its growth was entirely predictable. As the authors write:
Fork’s semantics have infected the design of each new API that creates process state. The POSIX specification now lists 25 special cases in how the parent’s state is copied to the child: file locks, timers, asynchronous IO operations, tracing, etc. [...] Any non-trivial OS facility must document its behaviour across a fork, and user-mode libraries must be prepared for their state to be forked at any time. The simplicity and orthogonality of fork is now a myth.

When you're facing an "everything" in a specification, you have a few options.

The naive option is to let your program be, and accept that one part of your code will need to be modified for every other feature added.

An alternative is to try to contain the "everything." If you're implementing a trading card game which each card can do "anything," then you can try to structure everything a card may affect into a GameState, and design special categories of cards that only affect a well-defined portion of this GameState, so that only the most insidious of cards require facing the full complexity.

Or, you can take the option proposed by these essay authors: get rid of the need to deal with "everything" in the first place.


Read: A fork() in the road

Advanced Software Design on CoRecursive


A few months ago, I sat down with Adam Gordon-Bell of CoRecursive. We discussed programminng tools research, software design, and self-improvement.

I was organizing a new workshop with a partner organization in New York, and planned to announce the podcast and the workshop both in the same newsletter. That was a mistake; there was a sudden change in leadership, and that project became stalled.

Better late than never. Enjoy: My interview with CoRecursive, podcast and transcript.
Copyright © 2019 Mirdin, All rights reserved.