Writing (日本語版)

Tools

Philosophy on tools

Abraham Lincoln reportedly said that, given eight hours to chop down a tree, he'd spend six sharpening his axe.

If something is worth doing even once, it's worth building a tool to do it.

It's often said that a poor craftsman blames his tools. I like this phrase, but I think it's often misunderstood. Good craftsmen don't blame their tools not because they don't need good tools, but because they spend time to build or choose the right tools for the job. A good craftsman knows the worth of a good tool.

My tools

I have built an entire world around my own tools. They might seem quite alien to many, but for me, they are the best tools for the job.

Operating system: Linux (Gentoo)

My operating system of choice is Gentoo/Linux.

Why do you use Gentoo Linux?

Most operating systems and distributions use pre-compiled binaries as the primitive unit of installable package. For example, if you want to install firefox, your operating system would have you download a bunch of pre-compiled object code, wrapped in a script that would move the executables and files to the right locations. This is the typical flow to "install software" on your computer.

Gentoo, unlike most operating systems, uses a different primitive. Instead of downloading a pre-compiled binary, you actually download the source code. Then, the installation process actually compiles that source code into the build artifacts which are placed in the right locations.

For me, this is the most important reason as for why I've ultimately landed on Gentoo as my operating system of choice: Because it is a source-based distribution.

Why do I care if it is source-based?

It makes it extremely easy for me to inspect what my software is doing, and to make changes to it, and recompile, on the fly. In short, it is the ideal setup for a hacker and open-source enthusiast, because the ability to modify software that is running on my computer is inherently baked into the very DNA of the operating system itself! It is not a workaround or weird hack for me to make a change to firefox and re-compile it. It is the standard installation flow. This makes the edit-compile-test loop for open-source software highly optimized for me, which makes it more likely that I will upstream fixes.

By the way, this also applies to the kernel itself, and not just user-land software. Gentoo installs the kernel from source as well, which makes such edits ~trvially accomplishable.

Windowing system (i3)

I use i3 as my windowing system. The short answer as to why is: I am a proponent of a mouseless existence insofar as most applications are concerned.

A mouse is an interface best suited for continuous-shaped requirements, e.g. aiming a pointer in games, drawing, and other inherently 2-dimensional arbitrary precision input modalities.

Textual interfaces (e.g. code), reading, navigating filesystems, etc. is inherently discrete (folders have files, files have words, words have letters). For discrete applications such as these, I (and many others) prefer mouseless modes of interaction. A mouse can point at many different precise points for a given character in a word, but it's still the same logical place.

Almost all of my modalities of interacting with my computer are inherently discrete, therfore, I optimize all of my tools to be a keyboard-only interaction modality.

i3wm (and tiling windowing managers in general) are the natural conclusion to the question, "How do we build a window manager in a mouseless world, optimized for keyboard interactions?"

As to why it's better:

  • It's faster than mucking around with a mouse.
  • I have a consistent layout and pattern of windows that I build expertise and muscle memory with.
  • This window manager is simple, and written with performance in mind: In short, the performance is staggeringly better than floating-point business.
  • Re-arranging windows side-by-side is one keystroke, and trivially arranged in an optimal layout.

Editor (emacs)

I use emacs, but with vim keybindings. Don't ask, it's a long story. I'm at home with either, but I prefer emacs for its extensibility and vim for its UX. I solved for this by extending emacs to have the same UX as vim, where it suited me. I don't like elisp, and emacs isn't as performant as I think it could be. That being said, I don't know of anything better.

Org-mode has been quite good to me, too.

Language (rust)

These days, I try to write most of my personal things in rust. I simply believe it is the future.

Everything else

I do everything else in my web browser or in my terminal of choice. (These days, it's alacritty, but my terminal choices seem to be much less sticky than OS/editor, and often times I'm using one of the shells within emacs directly.)


Alzheimer's

This story might as well be my own.

These days, other than close friends or family, when someone asks me how my mom is, I say, "she passed away when I was a teenager". They usually say, "Oh, I'm sorry". Then we move on. The real story doesn't fit into a small-talk conversation. It's too long and nuanced. Too brutal. Too unresolved.

She was diagnosed with early-onset Alzheimer's when I was around 12 years old. We (My dad, brother, and I) cared for her at home for about the next decade. Somewhere along the way, she forgot how to speak, how to eat, and who we were.

There was a phase where she would slip out of the house and go for walks on her own. I remember following her at a distance for a bit, letting her enjoy the agency and solitude of a walk around the neighborhood, before catching up to her and holding her hand so she didn't feel lost and alone.

One time, she gave us the slip real good. This was when I was maybe 16 years old. I searched the whole neighborhood to no avail. I eventually found her about a mile away, talking to strangers and laughing at the nearest grocery store. It was a strange phase of the disease — she could still walk and talk mostly, but would often forget major things.

Unfortunately on this day, she didn't know who I was or what "home" was. When I said "let's go home, mom", she started screaming at me in the grocery store, trying to fight me off and run away. "Get away from me", "I don't know who you are". "Stop!" Everyone watched. I don't think the watchers understood what was happening. I couldn't help but think I was somehow perversely benefitting from being on the wrong side of the bystander effect. She fought me the whole way home. The next day, it was like nothing happened.

Every son or daughter that has cared for a parent with dementia likely has hundreds of such stories. It is the ultimate form of hothousing for suffering and loss.

And yet, despite these stories, this couldn't be more true:

So far, with the help of her family, the day program and the sheer force of her own will and devotion to Jo, Robin has managed to provide all his caregiving at home. Families of people with dementia land in different places on this issue, but to her, it's beyond question that Jo is not here anymore; most everything that made him Jo has been stolen. "But it’s almost like an echo that’s left," she says. "It’s like I want to take care of this Jo because it’s honouring the real Jo."

When she's forced to move Jo into a nursing home and Robin loses her ability to care every day for the echo of the man she married, that will mean he's really gone. "Sometimes people very kindly say, 'Oh, it will be better when Jo is living somewhere else.' No, it won’t. That’s the part that people don’t understand. It won’t be better. It will be the worst part," she says through sobs. "Because I don’t consider what I'm doing a negative thing."


Principle of Charity

In this world, much unintended harm is inflicted.

We create a false dichotomy. You are asked to choose a tribe: the insensitive, always-offenders; or the oversensitive, never-offenders. But there is a middle way.

What does Jon Postel's law tell us? Be conservative in what you do, be liberal in what you accept from others. This principle about API and protocol design can help inform us on how to interact with each other. Applied to the topic of giving and taking offence, the principle tells us that our aim ought to be to not offend and to not take offence.

Though it seems oceans away, interlinked with this principle is another, the principle of charity.

The principle of charity or charitable interpretation requires interpreting a speaker's statements in the most rational way possible and, in the case of any argument, considering its best, strongest possible interpretation.

Put differently:

We make maximum sense of the words and thoughts of others when we interpret in a way that optimises agreement.

When Jon Postel was designing TCP, why did he recommend "to be liberal in what you accept from others"?

Because making the maximum sense of the system is furthered by interpreting what you receive from others in a way that optimises agreement. Although no amount of disagreement will hurt the feelings of a congestion control algorithm, it can certainly increase packet retransmissions, packet loss, and other Bad Things. This conclusion equally applies to human communication.

Principally, this is most interesting as applied to resolving apparent deltas of thought. This wording, apparent deltas of thought, seems a bit clumsy — and perhaps it is — but I use it to avoid words with subtly different meanings, such as disagreement or misunderstanding. I avoid these words because they have a connotation of finality. That we aren't actively working towards convergence. (Or at the least, working towards convergence in the sense that we understand each other's apparent delta in thought.)

The principle of charity has a beautiful symmetry. It states how to receive communication, but its implicit normative dual is about transmitting. Not only should you receive communication in the most charitable way, but you should also communicate in a way that optimises the likelihood of a charitable interpretation.

One of the most important tricks I've ever learned is how to convert apparent doubt, disagreement, and misunderstandings into a question about the crux of why I think I might disagree. Usually there is an assumption, a fact, a goal, or something else upon which the decision in question is wholly determined. If I can identify that particular thing, I can assume my understanding of it is flawed, then ask the question. By asking a question, I maximize a charitable outcome: Maybe I'm missing context. Maybe they're missing context, and don't know it yet. Either way, we'll find out.

There is never a reason to not apply the principle of charity. It always optimizes better outcomes.


Bluebird - Charles Bukowski

These words ring so true. See here for a good performance.

there's a bluebird in my heart that

wants to get out

but I'm too tough for him,

I say, stay in there, I'm not going

to let anybody see

you.

there's a bluebird in my heart that

wants to get out

but I pour whiskey on him and inhale

cigarette smoke

and the whores and the bartenders

and the grocery clerks

never know that

he's

in there.

there's a bluebird in my heart that

wants to get out

but I'm too tough for him,

I say,

stay down, do you want to mess

me up?

you want to screw up the

works?

you want to blow my book sales in

Europe?

there's a bluebird in my heart that

wants to get out

but I'm too clever, I only let him out

at night sometimes

when everybody's asleep.

I say, I know that you're there,

so don't be

sad.

then I put him back,

but he's singing a little

in there, I haven't quite let him

die

and we sleep together like

that

with our

secret pact

and it's nice enough to

make a man

weep, but I don't

weep, do

you?


Everything is a trade-off

To not bury the lede: Everything is a trade-off, so making a decision is about understanding which parameters you want to optimize, and their relative weights. This decision process can be easy, but sometimes it is exceptionally hard. Sometimes, a disagreement on the correct solution is actually a subtle disagreement on the relative weights allocated to each parameter. Framing these decisions as optimization problems can be a useful method for driving alignment or achieving consensus. At the very least, it can drive a mutual understanding of why the parties disagree.

For fun, let's model making a decision as some psuedo-code:

/// An outcome is something you decide to do, and it has Consequences.
struct Outcome {
    name: String,
    consequences: Vec<Consequence>
}

/// For our simple model, a Consequence can only be one of three things.
enum Consequence {
    /// How long until we get the outcome -- i.e. Wall clock time.
    TimeToComplete(magnitude: int64),

    /// How much "effort" will it be, in engineer-hours (or person-hours)?
    Effort(magnitude: int64),

    /// An Outcome will have certain Effects, for example if you decide to mow
    /// your lawn, an Effect might be Effect("House looks better", 30). For
    /// simplicity, we're doing this in a loosey-goosey Stringly-typed way, but
    /// it gets the idea across. The int64 is the magnitude of the Effect. Each
    /// Effect should abide by a contract about how to interpret its magnitude.
    Effect(name: String, magnitude: int64)
}

/// A function that decides between Outcomes, given some weights
fn decide(Vec<Outcome> outcomes, HashMap<Consequence, f64> weights) {
    // The decision's Outcome, and how well it scored. Higher is better.
    let mut decision: Option<(Outcome, f64)> = None;

    // Find the best outcome, by summing the magnitude of each outcome's
    // consequence's magnitude, weighted by the weights parameter.
    for outcome in outcomes {
        let score = outcome
            .consequences
            .iter()
            .map(|c| c.magnitude * weights[c])
            .sum();

        // If the outcome we're currently evaluating looks better than any of
        // the previous ones, then it becomes our defacto candidate decision.
        if decision.is_none() || decision.1 < score {
            decision = Some(outcome, score);
        }
    }

    return decision
}

Let me tell a story of an engineer's journey, with three chapters: Junior, Mid-level, and Senior.

Junior

A junior may have some preferences or ideas, but until you've seen how something fails, you don't have well-founded justified beliefs on how to best do things. Even more problematically, you might not be very good at enumerating the possible outcomes.

I want to be very clear: there is a difference between keeping a beginner's mind and being inexperienced. A beginner's mind can bring salient fresh perspectives on a problem. Inexperienced juniors may have a beginner's mind, but not expertise. If you couple expertise with a beginner's mind, that's where the real gains come into play.

Mid-level

A mid-level engineer has started to see how things can fail, and begins to develop strong opinions on how things ought to be. These engineers should be reasonably decent at enumerating the possible outcomes, but they may inevitably miss some important possibilities. A mid-level engineer develops heuristics like Gang of Four patterns are best practices, or everything should be immutable, or good code means no duplicated logic.

The thinking patterns often held by mid-level engineers work in 90+% of cases. They're good enough, for most things. But they break down. Best practice is an oxymoron. A practice is only the best if it's always the best, and nothing is best for every case. Absolutes like "everything should be X" are useful as an approximate default case, but they don't hold true in all cases. Nuance is everywhere, and second- or third-order consequences matter.

A good example of second- and third-order consequential thinking presents itself in the question of code deduplication. Many of us grew up learning how terrible duplicated code is. But did anyone ever teach us how bad deduplication can be? Deduplication is normally achieved by introducing abstraction. This is where the question of Consequence.magnitude comes into play. Ceteris paribus, that code duplication is to be avoided. But all things are not equal. The cost of a wrong abstraction is in fact orders of magnitude worse than duplicated code. Then, we can model this as follows: do we allow some duplication, or do we risk the wrong abstraction? Generally, you should allow duplication until that duplication becomes an actual problem. By that time, you should have sufficient experience in the problem space that the likelihood of choosing a bad abstraction is sufficiently low. Some present this idea as the rule of three. I propose to go even further, the rule of pain: duplicate until it hurts, then refactor. The reason is simple: duplication pain grows mostly linearly, but pain from the wrong abstraction is unbounded and grows in super-linear ways.

Senior

A senior understands that everything is a trade-off. Even things that seem obviously correct are in fact a trade-off — it's just that your weights are so skewed that it doesn't seem like a trade-off to you.

Things a senior might think:

  • "Code duplication isn't ideal, but avoiding it could be even worse."
  • "I may know a better solution, but the juice wouldn't be worth the squeeze."
  • "Many things can be modeled in terms of risk, and that viewpoint can be useful."
  • "Immutability is a good default, but mutability has a place when performance is important."
  • "Design patterns are mostly cargo-culting, and should be carefully evaluated."
  • "'Premature optimization is the root of all evil' is the root of all evil."
  • "Performance always matters, the only question is: To what magnitude?" (c.f. Would you browse Facebook if it took a year to load the homepage?)

Things a senior might think about:

  • Tying engineering back to the business need. e.g., "Our quality of engineering is of the utmost importance, our $50B business depends on our infrastructure platform running smoothly", vs. "We're a startup, we simply need to deliver results quickly, before running out of runway."
  • There are better options we could pursue, but the differences are too minute to be worth arguing.
  • How to communicate and drive a discussion from a trade-off perspective, and clearly enumerate the consequences of each outcome.

Indent With Intent

Imagine you've written the following hack to see which OS you're running.

OperatingSystem determine_os(uname: &str) {
  if (uname.contains("Darwin")) {
    return OperatingSystem.MacOS;
  } else if (uname.contains("Linux")) {
    return OperatingSystem.Linux;
  } else {
    return OperatingSystem.Unknown;
  }
}

You send it for code review, and get the following comment.

Nit: you can remove the final else.

Unfortunately, a common code review comment. But does it hold up to scrutiny?

What does it look like after applying the suggestion?

OperatingSystem determine_os(uname: &str) {
  if (uname.contains("Darwin")) {
    return OperatingSystem.MacOS;
  } else if (uname.contains("Linux")) {
    return OperatingSystem.Linux;
  }
  return OperatingSystem.Unknown;
}

By omitting the final else, we've broken the structural and visual alignment of logic that belongs to the same category.

Indentation is a visual manifestation of logical structure. Things that are alike logically and structurally should reflect that visually.

This function is effectively a tri-branch conditional return. And the code should show that as explicitly as possible. The else logically groups the code and encodes that meaning into it via structure. There's a cargo cult against using else anywhere. Don't fall for that trap.

If you're still not convinced, consider this. Would you write a switch statement like this?

switch(color) {
  case "red":
    return Color.Red;
  case "blue":
    return Color.Blue;
}

return Color.Unknown;

Or would you write it like this?

switch(color) {
  case "red":
    return Color.Red;
  case "blue":
    return Color.Blue;
  default:
    return Color.Unknown;
}

Travel deeply

It's more than dozens of times I have heard or participated in a conversation where people would ask each other, "How many countries have you been to?" At some point it would sound like a competition. 20, 30, 40, “and this year I plan to do A and B and C”.

Yes, you have been to 30 countries in 5 years, but have you really “been” there? How many places do you really know and understand? How many of those amazing, life-changing moments do you actually retain, internalize and use to help yourself become a better human, a better citizen of this world?

Travel should be challenging. It should change the way you think and feel. It isn't about checking the boxes or seeing the sights. It disappoints me to have to say: It's also not about being seen at those sights. The obtuse misappropriation of the art of photography, to travel somewhere — with its own culture, nature, and context — and make yourself the primary object of your photos: Fremdscham is the only word I can muster.

Travel slowly, explore deeply

Go somewhere you don't understand. Take the time, absorb what it offers. Learn something about the place. Learn something about yourself.

(Inspiration and quotes.)


Perfect is the enemy of good

Perfect is the enemy of good. Shipping something something imperfect is usually better than shipping nothing at all. (There are exceptions, but they aren't common.)

Most sufficiently advanced systems have to evolve to become good. Evolution requires exposure and experience in the real world, battle-tested behavior, and importantly, failures. The only path to this kind of evolution is to actually put something out there, even if it isn't perfect. Especially if it isn't perfect.

The elbow in the curve of time spent improving design and analysis comes earlier than you think. You should be spending less time designing, and more time learning from your failures, missteps, and the complex realities of your system in the wild. Your designs are wrong anyway. Natural selection is going to be a far more efficient process for showing you why and how your design is wrong than anything else.

Of note, an old essay by Richard P. Gabriel.


You must try, and then you must ask

When you become stuck on a problem, try for another 15 minutes, and then you must ask.

Document what you try. It becomes a guide that may lead you to the answer. If not, it certainly leads to a better question.

The temptation to immediately ask an expert is tantalizing — but resist! The documentation of your effort serves as a guide for mentoring and teaching.

More importantly, you earn the trust of those experts that you go to them on matters which you've already put effort into solving yourself.

Some of the time in that ~15 minute buffer, you will find the answer yourself. Knowledge gained by trial can often be stronger than knowledge from tutelage. Sometimes, you will even learn something that no one else knows. This is one of the greatest feelings, and often bears incredible fruit.

After all, experts are just people that have already tried and failed a lot.

Any sufficiently advanced form of expertise should look similar to trying a lot — and failing a lot. The best experts become intimate with this feeling. They may even revel in it.

(This writing was inspired by a director of software engineering at Google.)


Read the code

Reading the code shouldn't be your last resort.

Reading the code isn't always comfortable. It can feel like an arduous chore, especially if it isn't a core habit of yours (yet). This feeling is self-inhibiting. Recognize that it is born from discomfort, not logic.

The code is truth. The only complete truth.

If physicists had all the source code for the universe, how many experiments do you think they would run? My guess is zero.

Read the code and all else will follow.

Always read the code. Even when they tell you it doesn't or shouldn't work that way. Especially when they tell you that.


Flow playlist

I long searched for the perfect playlist to listen to as I worked. I never found it. So I tried to create it.

Flow is my playlist for concentration, coding, problem solving, etc. As of Dec 2019, it has over 77 hours of music, 771 tracks, and a healthy few hundred software engineer followers.

Its aim is to optimize for the mental state of Flow. Using nearly exclusively non-vocal tracks, the genres are primarily spacemusic and ambient.


Thoughts should motivate action

Thought is useful when it motivates for action and a hindrance when it substitutes for action.

Bill Raeder


Judgment over technique

The great benefit of computer sequencers is that they remove the issue of skill, and replace it with the issue of judgement.

Brian Eno

Collapse the distance between imagination and implementation by building tools.

Gordon Brander

Less rote practice; more imaginative delivery.


Runbooks

Writing a runbook is a declaration of temporary failure.

The act of writing a runbook is the incantation to a dark, forbidden contract with the better angels of our nature. It is a promise to do the right thing in the future, but not now. Right now, we're going to do the evil thing: write a runbook.

How often do we go back and do the right thing?

Rarely.

How many of these promises do we break?

A lot of them.

An error doesn't become a mistake until you refuse to correct it.

If you have runbooks, consider them as backlog tasks. Try not to add to their numbers. Failure begets runbooks; runbooks beget runbooks; and runbooks beget failure.


Fallacies of distributed computing

A good, simple writeup on the fallacies of distributed computing.

PDF Essay: Fallacies of Distributed Computing


Consistency models

A good index resource on consistency models, their guarentees, proofs, origins, etc.

Consistency Models


How to build a company

An incredible read on how to build a company from the ground up.

Founder to CEO


Nobody listens

Everything has been said before, but since nobody listens we have to keep going back and beginning all over again.

André Gide


Think big picture and fine detail

"It is an attitide that can be encapsulated in a simple but demanding rule: always think big picture and fine detail."

Will Gompertz, Think Like an Artist


Tribe-think

Everyone belongs to a tribe. Everyone underestimates the influence those tribes have on their thinking.

Acknowledge your tribes. Try to separate your thinking from your identity.

(Inspiration from pg, if I recall correctly.)


Interdisciplinary learning

I'm convinced that the best kind of learning is interdisciplinary. Fields of study have much to learn from each other. The most interesting learnings are those that transcend fields of study. The rest is just details.


Small identity

Keep Your Identity Small (2009) is an essay by Paul Graham which describes something I've intuitively practiced since my early twenties. But I'd never formalized the thought.

My set of justified true beliefs come not as an expression or extension of my identity, but from reasoned logic combined with a set of morals, ideal outcomes, and other justified true beliefs. I've internalized the idea that I shouldn't let these beliefs constitute my identity.

A related idea is that of “Strong Opinons, Weakly Held", a framework for thinking espoused by Palo Alto's Institute for the Future. The idea is that opinions should be "weakly held" — be unattached to the idea, so that you do not irrationaly reject its refutation. The other side of this coin is that you must not let this lack of attachment dampen your rigor, passion, or strength in forming, understanding, and justifying your beliefs.


Performance tools

This talk by Brendan Gregg is a fantastic introduction to the tools available in linux for performance instrumentation, monitoring, and debugging.

More to come on which of these tools I use and documenting how I like to use them. (e.g. bcantrill's dtrace, ebpf, metrics, etc.)


The true burden of masters

We are what they grow beyond. That is the true burden of all masters.

Master Yoda


Shared success

It is not enough to create your own success; you must create a shared success.

Build a taller boat to see further along the horizon, or find a way to raise the tide?

A shared success is not limited in scope to your team — it transcends teams. Find ways to drive shared successes across your org and company. Find the common good in a solution — the positive incentivization scheme that drives collaboration towards a shared goal. Express that common good persuasively and collaborate to achieve a shared success.


Shared context

Shape the understanding and thinking of those around you to a shared context. Tailor the message to the audience. Engineers, managers, product managers, directors: To each of these people, the message must be presented in a different light. For some, only some of the message is relevant. For others, it is a matter of making the message comprehensible. Without a shared context, you will inspire very few — and even fewer will create a shared success with you.


Taking children seriously

This essay corroborates a lot of my own views about how I would choose to raise my children. Whenever interacting with children, I prefer to treat them exactly as I do adults, respecting their ability to reason, learn, and comprehend.

As a kid, I resented being treated like a child. I didn't like it when adults would dumb down a concept or hand-wave things because it was assumed I wouldn't be able to understand.

I think if we treated kids more like adults, the adults of tomorrow would be better.


Waiting for the check

What is a deadlock?

A deadlock is when you're at a restaurant waiting to get the check, and your waiter is wating for you to ask for it.

And you're both wondering why it is taking so long.

You're both waiters now.


The power and cost of abstraction

First you learn the value of abstraction, then you learn the cost of abstraction, then you're ready to engineer.

Kent Beck


Design with migration in mind

Always design your programs as a member of a whole family of programs, including those that are likely to succeed it.

Edsger W. Dijkstra


CS is not about computers

Computer science is no more about computers than astronomy is about telescopes.

Edsger Dijkstra


Functionality is an asset; code is a liability

Functionality is an asset; code is a liability.

Kirk Pepperdine, @kcpeppe


Unlearning

Perfecting oneself is as much unlearning as it is learning.

Edsger W. Dijkstra


The lurking suspicion of simplification

The lurking suspicion that something could be simplified is the world's richest source of rewarding challenges.

Edsger W. Dijkstra


Refactoring

Refactoring (noun): a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.

Refactoring is often misunderstood. It shouldn't change the behavior. That's the point.


Comments

A delicate matter, requiring taste and judgement. I tend to err on the side of eliminating comments, for several reasons. First, if the code is clear, and uses good type names and variable names, it should explain itself. Second, comments aren't checked by the compiler, so there is no guarantee they're right, especially after the code is modified. A misleading comment can be very confusing. Third, the issue of typography: comments clutter code.

Rob Pike, Notes on Programming in C

I used to believe this more.

Like many things, it isn't black and white. It's complicated.

It is true, in most cases, code with good names, types, and abstractions should explain itself. However, explaining the what of the code* is only one of the useful modes a comment has. And it's easily the least common/useful mode.

Let's explore the questions that comments can help us answer.

Why does this code exist?

It is not self-evident why all code exists. One of the best use cases for a comment is to answer "why?", not "what?".

/*
 * Distribute memory according to CPU & memory use on each node,
 * with 3/4 hysteresis to avoid unnecessary memory migrations:
 *
 * faults_cpu(dst)   3   faults_cpu(src)
 * --------------- * - > ---------------
 * faults_mem(dst)   4   faults_mem(src)
 */
return group_faults_cpu(ng, dst_nid) * group_faults(p, src_nid) * 3 >
       group_faults_cpu(ng, src_nid) * group_faults(p, dst_nid) * 4;

This is an example of a good comment from the Linux kernel, which concisely captures both the "what?" and the "why?" of a non-trivial expression.

What are we doing?

Distributing memory, according to the formula shown above.

Why do it like this?

We're adding a 3/4 hysteresis to avoid the churn and burn of twitchy, sensitive-to-change migration formulas.


Cargo cult programming

Cargo cult programming is a style of computer programming characterized by the ritual inclusion of code or program structures that serve no real purpose.

Wikipedia

Most engineers go through a progression of understanding the balance of simplicity versus other values. Oftentimes, simplicity should win.

Examples

Why write a switch statement here instead of employing the strategy pattern?

More generally:

How about using XYZ Gang of Four pattern here instead?

I'm not against these patterns because I'm unfamiliar with them. Quite the opposite. Healthy experience with these patterns should tend to reduce the urge to reach for them.


Phenomenal pas de deux

Gods and Dogs, Jiří Kylián

https://www.youtube.com/watch?v=jF8nfg2oKzo


Tiling window managers

The i3 window manager is the best thing to happen to my desktop in a long time.

Much like modal editing, it is a paradigm shift in how to interface with your computer. No floating windows. Everything is snapped. If you're a vim/emacs user, you'll like tiling window managers. Try i3.


Documentation

A few days ago, our team launched a new component. A week after launch, while dealing with some ops tickets, we discovered that the detailed, extensive wiki document for that component was already out-of-date.

This is a familiar story to any dev. Documentation is hard to keep accurate. The knee-jerk reaction is to assess that the miss here was that we allowed the documentation to become inaccurate. The knee-jerk reaction is wrong. It is not feasible to maintain documentation if that documentation is too specific.

The solution is to change the philosophical approach to documentation. For software systems of any reasonable complexity, the code is the only source of truth for its behavior and its descriptive reality. Documentation ought to answer the following questions, in basic non-technical english prose:

  1. Why did we create this component? (prescriptive statement)
  2. At a high-level, how does it solve its problem? (This answer should absolutely not map anywhere near 1:1 to its implementation. It should be at an extremely high-level.)
  3. Going no deeper than a file/class level view, describe in a paragraph the function of the most critical classes/files/modules in the code. This is as specific as it should get, and this serves as a guide for an engineer to dive into the code.

Again, documentation should never map 1:1 to anything more specific than a class. Anything more specific than that, and it effectively has to change whenever the code changes. If you're changing the function of tons of classes and files, it is reasonable to expect that the documentation would necessarily have to change in kind.

Writing documentation at a high-level helps to minimize the potential that your documentation will become out-of-date and inaccurate. It is easier to understand, too. Which is kind of the point. If someone wants to know the objective truth exactly as it exists, the documentation is absolutely the wrong place for that. That's what the code is for.


My dream house

A home is something personal, a space that can shape your habits, emotions, and thoughts. I do not see it as overly materialistic to dream of an ideal place to call home.

My wishlist

  • Eastern light (I wake best by natural light)
  • Western light (sunsets are romantic)
  • In Seattle, preferably Capitol Hill, Queen Anne, Montlake
  • Ceiling-to-floor windows, and plenty of them
  • Grand views of mountains and water
  • High ceilings
  • Open floor plan, without needless walls or partitioning elements
  • A modest and easily maintainable yard
  • A spacious, open kitchen that allows for entertaining while cooking
  • Relatively medium-to-small house — 3br, less than 2500ft²
  • Stone exterior
  • Sitting high above the road
  • Nearby or adjacent to green space
  • Minimalism over complicated or noisy designs
  • Some styles I like: Tudor, Mid-Century Modern, Gothic, Craftsman, Pacfic Lodge
  • Understated or modest from the exterior — I don't want it to be showy

Examples of homes I like


Scale later

Starting a business is antithetical to scaling efforts.

Early in my career, I worked at an early stage startup. I fought tooth and nail in design meetings to apply solid engineering principles to everything we did: scalability, extensibility, modularity, generalization, etc.

I was wrong to do so.

I was attacking the problem from a different perspective, a perspective that would fare well in a large company with massive distributed systems — a company inherently optimizing for the long run. Startups, however, are not optimizing for the long run. They can afford to worry about that later. They need to solve for tomorrow.

This doesn't mean you should throw your engineering principles away. They're not inherently wrong; they are simply the wrong tool for the job, if you're at a startup. Everything is a matter of compromise, and at a startup, you will be compromising more often than not.

Murali Krishnan, my manager at the time, got this right. I was often on the other side of the table, arguing for engineering excellence. I wasn't yet wise enough to understand. It was only later I understood: startups are optimizing a much different set of parameters.

As a young engineer that wants to hone these areas of excellence, it can be a very hard pill to swallow, intentionally setting aside engineering best practices. It's okay. You're not optimizing for the long run. Not yet.

Don't just take my word for it, though. An essay of Paul Graham's inspired this post. An entirely worthwhile read.


Context switching an engineer

Every time you ask an engineer to postpone what he/she was working on and immediately work on some new task, you've done the equivalent of grabbing at least $500 from your company's piggy bank and flushing it down the toilet. You'd better make sure that new task is worth it.

Any progress the engineer has made on the previous task, you can now kiss goodbye. You should consider that previous task as completely unbegun.

Context switching is incredibly expensive for engineers. It can take upwards of an hour for an engineer to build state on a problem. Building this state is a fixed cost of entry for the engineer to enter a state of flow that enables productivity. Breaking that flow and wasting all that state is expensive.


A computer scientists view of life, the universe, and everything

This paper by Jurgen Schmidhuber is incredible. I'll take some notes on it here in the future.


The FiveThirtyEight Riddler

If you like imaginative/challenging math/logic/CS problems, take a gander at FiveThirtyEight's The Riddler.

FiveThirtyEight is Nate Silver's website that does precise, academic, and statistical commentary on a variety of issues. Their The Riddler column features hard and interesting problems on a weekly cadence.

In the past, I have solved several, with at least one of my solutions being featured with a shout-out. My solution with code is on GitHub. This particular problem was a fun little geometry problem which I opted to solve via simulation and numerical geometry, though others found an elegant analytical solution.


Dancing the quicksort


Good architecture

If you think good architecture is expensive, try bad architecture.

Brian Foote & Joseph Yoder


Avoid hard problems

A clever person solves a problem.
A wise person avoids it.

Albert Einstein


Rule of three

omne trium perfectum

— Latin for "everything that comes in threes is perfect."

Kudos to a previous manager of mine, [Murali Krishnan][murali-linkedin], whom I met while working at Transform for demonstrating true mastery over the [rule of three][rule-of-three-wiki] and opening my eyes to its power.

The rule of three (of which there is so much written that it [needs a disambiguation page][disambiguation-wiki]) is an incredibly potent tool to communicate ideas, persuade others, and to tell stories.

  1. A threefold model lends explanatory power to difficult-to-explain topics, simplifying the conceptual model and providing and elegant framework to teach.
  2. Problems often cleanly subdivide into three sub-problems.
  3. The cadence and presentation of threes tend to be inherently agreeable to humans, augmenting credibility and persuasiveness.

[rule-of-three-wiki]: https://en.wikipedia.org/wiki/Rule_of_three_/writing/) [disambiguation-wiki]: https://en.wikipedia.org/wiki/Rule_of_three [murali-linkedin]: https://www.linkedin.com/in/muralirkrishnan/


An error is not a mistake

“An error doesn't become a mistake until you refuse to correct it.”

Orlando Aloysius Battista


Less is more

Perfection (in design) is achieved not when there is nothing more to add, but rather when there is nothing more to take away.

Antoine de Saint-Exupery


Almost is not

You will often hear X is almost Y. Almost in this context should be read as not. Almost is a face-saving mechanism when you don't want to say no.