unModified()

Break stuff. Now.

Great Magic Happens In Three Acts

How three is magical in programming

October 29, 2017

The other day, I was browsing through movie listings hoping to find a movie that I haven't watched yet. I came across The Prestige, one of the few magic-themed movies I've watched (besides Now You See Me and Now You See Me 2. As I re-read the plot, I encountered that famous quote of magic being a three-part act. Then it hit me... I think I've applied it to programming.

Every great magic trick consists of three parts...

“Every great magic trick consists of three parts or acts. The first part is called "The Pledge". The magician shows you something ordinary: a deck of cards, a bird or a man. He shows you this object. Perhaps he asks you to inspect it to see if it is indeed real, unaltered, normal. But of course... it probably isn't. The second act is called "The Turn". The magician takes the ordinary something and makes it do something extraordinary. Now you're looking for the secret... but you won't find it, because of course you're not really looking. You don't really want to know. You want to be fooled. But you wouldn't clap yet. Because making something disappear isn't enough; you have to bring it back. That's why every magic trick has a third act, the hardest part, the part we call "The Prestige".”

Christopher Priest, The Prestige

Applied to documentation

I've always preached that documentation should be short, simple, and contain three parts:

  1. What it is.
  2. What it does.
  3. How to use it.

This approach gives documentation consistent structure. Present the fact that it exists, explain how it works, and then take the reader to a dive into how it's actually used. This structure caters the three kinds of people that read documentation: the ones looking for some functionality, the ones wanting to know how it works, and the ones wanting to know how to use it.

You can see this pattern in action in the Ractive API documentation for v0.9:

Variables

{{ }}, {{& }} and {{{ }}} render a reference. They are also used for binding when used on directives. {{ }} escapes the reference while {{& }} and {{{ }}} do not.

Ractive({
  data: {
    name: "Chris",
    company: "<b>GitHub</b>"
  },
  template: `
    {{name}}      <!-- Chris -->
    {{age}}       <!--  -->
    {{company}}   <!-- &lt;b&gt;GitHub&lt;/b&gt; -->
    {{&company}}  <!-- <b>GitHub</b> -->
    {{{company}}} <!-- <b>GitHub</b> -->
  `
})

Applied to unit tests

Often times I come across unit tests that don't make sense. They're as spaghetti as the code they're testing. It shouldn't be that way, especially when unit tests are the next best thing when documentation is non-existent. All unit tests need are three things:

  1. The setup
  2. The action
  3. The effect

This keeps tests simple, independent, portable, and only testing one functionality at a time. It also gives future test writers a good structure to follow. Anything that breaks this structure, especially repeating any one of the three steps in one test block, belong to another test block. Although there are fringe cases where you need to repeat steps 2 and/or 3, that usually indicates the functionality is non-deterministic - a story for another time.

You can see this pattern of writing unit tests in one of the test specs for Ractive to fix a plugin bug:

test('Teardown to a non-adapted value', t => {
  // The setup
  const Adaptor = {
    filter ( object ) {
      return object && typeof object.then === 'function';
    },
    wrap () {
      const get = () => null;
      const set = () => {};
      const reset = () => false;
      const teardown = () => {};
      return { get, set, reset, teardown };
    }
  };

  const model = Promise.resolve();
  const instance = Ractive({
    adapt: [Adaptor],
    data: { model },
    el: fixture,
    template: '<p>{{ model }}</p>'
  });

  t.strictEqual(instance.get('model'), model);
  t.strictEqual(instance.get('model', { unwrap: false }), null);
  t.strictEqual(instance.find('p').innerHTML, '');

  // The the action
  instance.set('model', 1);

  // The the effect
  t.strictEqual(instance.get('model'), 1);
  t.strictEqual(instance.get('model', { unwrap: false }), 1);
  t.strictEqual(instance.find('p').innerHTML, '1');
});

Conclusion

Any sufficiently advanced technology is indistinguishable from magic.

Arthur C. Clarke

Technology should be fun, exciting, entertaining, engaging like the magic tricks you see in TV and in real life. It shouldn't be the fuel to the fears that keep you up at night. It shouldn't be the kind of sorcery that drives you insane. Technology isn't hard, it just ended up hard. Through good practices, good code and documentation can be achieved. For great things to happen, it should be laid out in as little as three parts.