Blog

Exploring weird maths with code

Sometimes, while reading an innocuous-seeming article, I stumble across an aside that makes me sit bolt upright and mutter something incredulous. Asides like this one:

A counterintuitive property of coin-tossing: If Alice tosses a coin until she sees a head followed by a tail, and Bob tosses a coin until he sees two heads in a row, then on average, Alice will require four tosses while Bob will require six tosses (try this at home!), even though head-tail and head-head have an equal chance of appearing after two coin tosses.

Wired

This was a surprise! The four possible outcomes of two tosses are equally likely, so it seems weird that a heads-tails outcome would take longer to reach than a heads-heads. Weird enough to try it at home – at least by programming. Let's write some Ruby and see if we get the same result. (I recommend opening irb and exploring these examples for yourself if you want to fully understand them.)

Checking some assumptions

First of all, let's agree to toss a coin by picking a random symbol from an array1:

def coin_toss
  %i(heads tails).sample #=> :heads or :tails. 
end

And let's confirm that this is close enough to 50/50, by counting the result of tossing a coin 100,000 times:

results = {heads: 0, tails: 0}
1000000.times { results[coin_toss] += 1 }

puts "After 100000 tosses we saw #{results[:heads]} heads and #{results[:tails]} tails."

.sample chooses an element at random, so the result will be a little different each time. I ran this program 10 times, and got these results:

After 100000 tosses we saw 50131 heads and 49869 tails.
After 100000 tosses we saw 49845 heads and 50155 tails.
After 100000 tosses we saw 50094 heads and 49906 tails.
After 100000 tosses we saw 49672 heads and 50328 tails.
After 100000 tosses we saw 50062 heads and 49938 tails.
After 100000 tosses we saw 50046 heads and 49954 tails.
After 100000 tosses we saw 50003 heads and 49997 tails.
After 100000 tosses we saw 50094 heads and 49906 tails.
After 100000 tosses we saw 50124 heads and 49876 tails.
After 100000 tosses we saw 49838 heads and 50162 tails.

I think these results look OK, but the next thing I tried was busting out some statistics and checking the standard deviation. You can think of it as a measure of how closely-clustered our results are – we'd expect to get a low standard deviation if .sample is fair. Calculating the standard deviation is a little bit complicated, so I used the descriptive_statistics gem to make it easier. Let's calculate the standard deviation of the number of heads in each run:

require 'descriptive_statistics'
[50131, 49845, 50094, 49672, 50062, 50046, 50003, 50094, 50124, 49838].standard_deviation #=> 146.014

But is 146.014 low or not? I have no idea! This is where my statistics knowledge runs out. For now, let's presume our eyeballs are correct and our coin tosses are fair.

Back to the question

If we can toss a coin fairly, we can return to our original question: how many tosses, on average, does it take to reach a given combination?

We'll need a target combination, we need to toss at least twice, and we want to toss until we hit the target:

target = [:heads, :heads]
tosses = [coin_toss, coin_toss]

until tosses.last(2) == target
  tosses << coin_toss
end

I ran this in irb and I got [:tails, :tails, :heads, :tails, :heads, :heads]. It works! Let's turn this into a method so we can reuse it:

def tosses_until(target)
  tosses = [coin_toss, coin_toss]
  until tosses.last(2) == target
    tosses << coin_toss
  end
  tosses
end

Running the experiment repeatedly will make our result more reliable. If something weird happens once it could be a fluke, but you can't fluke something thousands of times. We could use the .times method again, and build up an array of results like we built the array of tosses:

experiments = []
100000.times { experiments << tosses_until([:heads, :heads]) }

Or we can make this shorter by using Ruby's .map method. .map applies a method to every element in a list. It's normally used to modify an existing list:

["cat", "dog", "avocado"].map { |t| t.upcase } #=> ["CAT", "DOG", "AVOCADO"]
(1..4).map { |n| n * 3 } #=> [3, 6, 9, 12]

But it doesn't matter if we throw the original elements away instead. You can try this in the console, but beware! It's going to print out all 100,000 results.

experiments = (0..100000).map { tosses_until([:heads, :heads]) }

It's not really relevant to our experiment, but I wondered what the shortest and longest sequence until our target was. You might expect that we can use experiments.min and experiments.max to find out:

experiments.min #=> [:heads, :heads]
experiments.min.length #=> 2
experiments.max #=> [:tails, :tails, :tails, :tails, :tails, :tails, :tails, :tails, :tails, :tails, :tails, :tails, :tails, :tails, :tails, :heads, :tails, :heads, :tails, :heads, :heads]
experiments.max.length #=> 21

But that's not quite right2 for the maximum case. It looks right, though – a handy reminder that verifying data by eye can lead you astray. Instead, we need to use .max_by to explicitly look at the length of the array:

experiments.max_by { |e| e.length }

This pattern – calling a method on the value passed into the block – is common, so Ruby provides a shorthand for this:

experiments.max_by(&:length) #=> [:heads, :tails, :tails, :tails, :heads, :tails, :tails, :tails, :heads, :tails, :tails, :tails, :tails, :tails, :heads, :tails, :tails, :heads, :tails, :heads, :tails, :tails, :tails, :heads, :tails, :tails, :heads, :tails, :tails, :heads, :tails, :tails, :heads, :tails, :tails, :heads, :tails, :heads, :tails, :tails, :heads, :tails, :tails, :heads, :tails, :tails, :heads, :tails, :tails, :heads, :tails, :tails, :tails, :tails, :tails, :tails, :tails, :tails, :tails, :heads, :heads]
experiments.max_by(&:length).length #=> 61

Let's put all this together in one place, and add some output about our results:

def coin_toss
  %i(heads tails).sample #=> :heads or :tails. 
end

def tosses_until(target)
  tosses = [coin_toss, coin_toss]
  until tosses.last(2) == target
    tosses << coin_toss
  end
  tosses
end

experiments = (0..100000).map { tosses_until([:heads, :heads]) }
average_toss_count = experiments.reduce(0) { |sum, n| sum + n.length } / experiments.length.to_f # We'll talk about this line below.

puts "Our shortest sequence was #{experiments.min_by(&:length)}"
puts "Our longest sequence was #{experiments.max_by(&:length)}"
puts "On average, we had to toss #{average_toss_count} times before (heads, heads) came up."

.reduce is a close cousin of .map. .map does something to every element in a list; .reduce takes two elements from a list and boils them down into one. It does that repeatedly to produce a final value:

[1, 2].reduce { |a, b| a + b } #=> 3
[1, 2, 3].reduce { |a, b| a + b } #=> 6: [1, 2, 3] → [3, 3] → 6.
[1, 2, 3, 4].reduce { |a, b| a + b } #=> 10: [1, 2, 3, 4] → [3, 3, 4] → [6, 4] → 10.

You can also give .reduce a starting value, which is what we did in our program:

[1, 2].reduce(10) { |sum, a| sum + a } #=> 13: 10 + 1 = 11 then 11 + 2 = 13.
[1, 2, 3].reduce(10) { |total, a| total + (a * 2) } #=> 22.

We started our toss count at 0, then added the length of each run to that total. Finally, we divided it by the total number of runs to get an average. The .to_f on the end converts the length to a floating point number, because we'd like to see the decimal places in the result.

9 / 2 #=> 4; really "4 remainder 1", but Ruby throws the remainder away
9 / 2.to_f #=> 4.5

Simplifying our code

This works, but is more complicated than it needs to be. Our goal was to find out how many tosses, on average, it takes to hit our target – we don't care about the sequence of tosses to get there. Let's change our tosses_until method to return the number of tosses instead of the sequence itself:

def tosses_until(target)
  tosses = [coin_toss, coin_toss]
  until tosses.last(2) == target
    tosses << coin_toss
  end
  tosses.length
end

This lets us make our trial run code simpler. We could build an array of the sequence counts, then add it up:

experiments = (0..100000).map { tosses_until([:heads, :heads]) }
average_toss_count = experiments.reduce(&:+) / experiments.length.to_f

We could skip the array entirely, and just maintain a total:

total_experiments = 100000
total_tosses = 0
total_experiments.times { total_tosses += tosses_until([:heads, :heads]) }
average_toss_count = total_tosses / total_experiments.to_f

Or we could use reduce again:

total_experiments = 100000
total_tosses = (0..total_experiments).reduce(0) { |sum, _| tosses_until([:heads, heads]) }
average_toss_count = total_tosses / total_experiments.to_f

The "best" version is a matter of taste, but personally I prefer the first version. It uses more memory, but that doesn't matter in experiments like these. It's the shortest code, we can find the longest run of tosses, and it's reasonably clear how it works once you get your head around .reduce.

Let's put the first version into a method that runs the experiment and reports the outcome for a given target:

def coin_toss
  %i(heads tails).sample
end

def tosses_until(target)
  tosses = [coin_toss, coin_toss]
  until tosses.last(2) == target
    tosses << coin_toss
  end
  tosses.length
end

def average_toss_count(target, num_experiments)
  experiments = (0..num_experiments).map { tosses_until(target) }
  average_toss_count = experiments.reduce(&:+) / experiments.length.to_f

  # sprintf formats the average so it prints to two decimal places only.
  puts "On average, we had to toss #{sprintf('%.2f', average_toss_count)} times before #{target.inspect} came up. Our longest run was #{experiments.max} tosses."
end

The other cases

Now we have all the building blocks to run the experiment for each of the four possible outcomes:

targets = [[:heads, :heads], [:heads, :tails], [:tails, :heads], [:tails, :tails]]
targets.each { |target| average_toss_count(target, 100000) }

Which produces:

On average, we had to toss 5.98 times before [:heads, :heads] came up. Our longest run was 52 tosses.
On average, we had to toss 4.00 times before [:heads, :tails] came up. Our longest run was 22 tosses.
On average, we had to toss 3.99 times before [:tails, :heads] came up. Our longest run was 20 tosses.
On average, we had to toss 6.00 times before [:tails, :tails] came up. Our longest run was 55 tosses.

Sure enough, it takes longer on average to hit [:heads, :heads] or [:tails, :tails] than [:heads, :tails] or [:tails, :heads], even though each outcome has an equal probability. It's still weird, but now I'm satisfied it's true.

Why does this happen?

Let's go back to Alice and Bob, who are targeting [:heads, :tails] and [:heads, :heads] respectively:

Player Target
Alice H T
Bob H H

Let's presume they both win their first toss – they both get a result they're looking for:

Player Target Result 1
Alice H T H
Bob H H H

Then, presume they lose their second toss:

Player Target Result 1 Result 2
Alice H T H H
Bob H H H T

There's now a major difference between the two players: Alice can hit her target on toss 3, but Bob can't until toss 4. Bob must start over after losing on toss 2; Alice's loss can be part of a win if she gets a tails on turn 3.

Exercises

If you'd like to explore this some more, here's some suggestions for things to try:

  1. Change the program so it runs the experiment a million times instead of 100,000.
  2. If we toss three coins, there's eight possible outcomes. How long does it take, on average, to hit each combination? Are there some sequences that take longer than others?
  3. We left our proof of a fair coin toss at "Yeah, that looks OK." Can you do better? How would you satisfy yourself that it's producing fair results?

  1. %i() is Ruby shorthand that generates an array of symbols. %i(foo bar baz) means the same as [:foo, :bar, :baz].  ↩

  2. But why doesn't this work? When we call .min, Ruby uses the <=> comparison operator to find the smallest value in the list. experiments is an array of arrays; <=> for arrays calls <=> on each of the elements of the list in turn until it finds a difference. In this case, our list elements are symbols. Symbols get converted to strings before comparison, and "heads" < "tails" because "h" < "t". So the upshot of this is that experiments.max returns the result with the longest initial streak of tails.

    Yes, I had to look this up in the documentation.  ↩

The EU Referendum: A Retrospective

I have tried for days to write about the referendum, but I keep getting overwhelmed by the immensity of it. Will we actually leave, or prevaricate forever? Can we negotiate reasonable trade deals, or will the EU make an example out of us? Will companies still open offices in the UK now we're no longer a gateway to Europe? Will Scotland become independent? What happens next in Ireland? Will our most deprived regions keep their funding? Will workers' rights be protected? Any one of these would be Pandora's box; we have opened many at once.

All of these issues are important, and all of these are beyond my control. They're also beyond my foresight: I have no idea what happens next. The stock market is suffering and the pound is at a thirty year low. These falls came from the decision to leave, but the fluctuation comes from the uncertainty. Uncertainty is the UK's greatest national resource now. We can certainly export that to the world.

A collage of anti-EU, anti-migrant front pages from UK newspapers.
Nothing says tolerance, compassion, and decency like calling people "Ethnics". Collage via @gameoldgirl.

Everyone promptly found out that the "leave" campaign was a Potemkin village, but its shoddy foundations were laid over the previous decades. The tabloid press constantly pumped out anti-EU & anti-immigrant froth, and nobody found a way to combat it effectively. Politicians found they could use these fears to their advantage, so why try to dispel them? Besides, it would invite the wrath of the press.

Without this backdrop – a nation flooded by freeloaders, powerless to prevent pointless meddling from Brussels – the UK would never vote to leave. It would have sounded preposterous. It was our government alone that failed to invest in the NHS, to build houses and schools, to make sure our post-industrial regions weren't dependent on grants, and allowed employment to become more precarious. Nothing to do with Europe. But it's no surprise that the people on the losing end of rising inequality would vote against the status quo.

As a user, given that I have a time machine...

I've grappled with two questions since Thursday night: "What should I1 have done differently?" and "What should I1 do now?". I'd kept my own counsel in previous elections but I spoke up a little this time. Some of that was amongst friends, but I also made a small website that laid out the benefits of European co-operation. I tried to back up all my claims, but my goal was to change people's feelings – not their minds. I wanted undecided people to see this long list and think "Wow, I never realised that the EU had a part in all this". People in the UK think of the EU as faceless, ineffectual, meddling bureaucrats who force legislation upon us; I hoped to replace that with some affection.

The site was a small success. It reached a couple of thousand people, and sparked some discussion showing it reached folk who weren't voting "remain" already. But I can't shake the feeling that my aim was off. Older people are more likely to vote, and more likely to vote "leave" – but they're harder to reach through the internet, and I don't have a voice in traditional media. People outside of large cities were more likely to vote "leave" – but they're harder to reach as my social circle is very urban. What could I have done differently to reach those groups? What medium should I have used? Would a different message have resonated more?

This campaign seriously impressed me. It's so simple, but appeals directly to the viewer's sense of identity. Just three words and a picture of Churchill speak volumes about persevering through tough times and standing with our neighbours.

Or is this the wrong question? Instead of asking how to reach a different audience, perhaps it's better to convince my audience they need to vote. My gut says that's a harder problem – people have been trying to motivate the younger generation to participate in politics unsuccessfully for years. Transforming online activism into real-world action is Herculean. I don't know what I can do as an individual, but Facebook's "I voted" feature is the strongest encouragement I've seen online.

What do we do now?

I doubt we'll see a second referendum. We'd need to negotiate a new deal with the EU – one different enough to merit putting it to the vote again. But Europe wants us out and doesn't need to negotiate with a gun to its head. We already had many exceptions to EU rules but voted to leave anyway. So Europe has no motivation to offer us a deal, and no pro-EU politician will want to risk a second "leave" outcome. We might hope for a stalemate – the UK never invoking Article 50, the EU not finding a way to force us out – but I expect some combination of economic uncertainty & European resentment will result in Britain leaving the EU.

Journalists and politicians will try to identify the effects of leaving, but conclusive evidence will be scarce. You can't see the corporate headquarters that gets built in France instead, nor can you see the uncreated jobs from a lack of economic growth. Businesses don't fail for one reason alone. Infrastructure takes at least a decade to become obviously dated; too slow to recognise and attribute.

Individuals can't change the UK's situation, but we can make our communities better. I have four concrete suggestions:

  • Stand up for others when you see abuse and prejudice.
  • Talk with your friends and neighbours about your beliefs. Don't proselytise; just listen to what they say, and gently try to move their opinions a little. Be compassionate and polite. You're trying to show people that there's a huge range of perspectives in the world, and to dispel myths & fears.
  • Lobby your MP to focus on their constituency instead of party politics. MPs need to support job security and job creation. They need to protect worker's rights and the social safety net. Let them know you expect this of them.
  • Hold the people who got us into this mess to account. They convinced us to leave, but don't want the responsibility of figuring out the details or standing by their pledges. And don't forget the disgusting parts either.

I also have an idea for another project. Something that makes it easier for people to engage with the politics that affects them, not the Westminster soap opera. I don't know if it will see the light of day, but I'm trying to use my anxiety about the future to propel it forward. It might not help after all, but anything's better than just looking on in horror.


  1. "I" really means "we": "what should an individual citizen, acting in their country's best interest, have done differently?"  ↩

How to Expand an OSX FileVault partition on El Capitan

I switched to OSX as my primary operating system around a year ago, after a lifetime of running Linux on the desktop. Using Ubuntu on a Macbook Pro is surprisingly straightforward and didn't require any low-level finagling, but it did come with some annoyances; annoyances that led me to try OSX as my primary OS. I kept the dual boot, but over time I wanted more disk space available to OSX. I had to piece the process together every time; here's what worked for me.

Step 0: take a good backup.

Editing partitions carries a risk of losing all your data. Backup everything! In both operating systems! These steps worked for me, but might not work for you. And while we're talking precautions: choose a time when you don't have important deadlines, meetings, or other computer-centric tasks.

Step 1: make space on the drive.

You need free space for your OSX partition to expand into. The disk utility in OSX is limited and can't resize Linux partitions, but GParted can. I downloaded Ubuntu and made a bootable USB key. Plug it in, then reboot while holding down the option ⌥ key. You can then choose a device to boot from.

Once in Linux, my process was:

  1. Clear some space on the Linux partition beforehand, then shrink it in GParted.
  2. Move the now-smaller partition to the end of the drive.
  3. Move any other partitions (eg. OS X recovery partitions) towards the end of the drive, so there's unallocated space after the partition you want to expand. Something like this1:
A screenshot of gparted, showing some unallocated space.

GParted will let you queue up these changes and try to apply them all in one go, but that gave me some (apparently harmless) error messages. I'd recommend making the changes one at a time.

Step 2: reboot into OSX and turn off CoreStorage.

OSX uses a volume manager called CoreStorage that acts as an intermediary between the operating system and the hardware. It's a requirement for FileVault encryption, but we can't expand drives while it's enabled. First, let's see all the CoreStorage volumes using diskutil cs list on the terminal:

CoreStorage logical volume groups (2 found)
|
+-- Logical Volume Group UUID 9559695B-73C6-40ED-B6EB-F3DE8767058A
|   =========================================================
|   Name:         Macintosh HD
|   Status:       Online
|   Size:         249222377472 B (249.2 GB)
|   Free Space:   0 B (0 B)
|   |
|   +-< Physical Volume UUID A76BF102-C0CF-41C4-9D88-27F8BB9A180E
|   |   ----------------------------------------------------
|   |   Index:    0
|   |   Disk:     disk0s2
|   |   Status:   Online
|   |   Size:     249222377472 B (249.2 GB)
|   |
|   +-> Logical Volume Family UUID EDA455C5-3FD0-444E-B00C-F9F8F2EF88EC
|       ----------------------------------------------------------
|       Encryption Type:         AES-XTS
|       Encryption Status:       Unlocked
|       Conversion Status:       Complete
|       High Level Queries:      Fully Secure
|       |                        Passphrase Required
|       |                        Accepts New Users
|       |                        Has Visible Users
|       |                        Has Volume Key
|       |
|       +-> Logical Volume UUID 4F3C168A-F0BB-40B6-B3FF-CE94D38506AD
|           ---------------------------------------------------
|           Disk:                  disk1
|           Status:                Online
|           Size (Total):          248873222144 B (248.9 GB)
|           Revertible:            Yes (unlock and decryption required)
|           Revert Status:         Reboot required
|           LV Name:               Macintosh HD
|           Volume Name:           Macintosh HD
|           Content Hint:          Apple_HFS

The most nested entry is the logical volume with a UUID of 4F3C168A-F0BB-40B6-B3FF-CE94D38506AD. Copy that UUID and use it in diskutil cs revert <UUID>:

diskutil cs revert 4F3C168A-F0BB-40B6-B3FF-CE94D38506AD

Reverting back to a regular volume takes some time, but you can check on the progress by running diskutil cs list until it shows as complete.

Step 3: Reboot, then expand the partition.

Your drive is no longer encrypted and doesn't use CoreStorage any more, so you can use Apple's Disk Utility to expand it. The 'partition' section has a pie chart with handles you can drag. Something like this:

A screenshot of Apple's disk utility, showing the pie chart with handles.

Step 4: Reboot again, then convert your drive back to a CoreStorage partition.

Run diskutil list to see all the partitions in your Mac. The one you want to convert is probably called "Macintosh HD". Let's re-enable CoreStorage:

diskutil cs convert "Macintosh HD"

Step 5: Reboot, then re-enable FileVault.

You can re-enable this from System Preferences → Security & Privacy → FileVault. This will also prompt you to reboot, for the last time.

Getting out of trouble

Everything broke with this final reboot. On startup, the Apple logo & a progress bar appeared before being replaced with a "no entry" logo (🚫) around ⅔ of the way through. This is how a Mac says "I found something that looked like OSX, but didn't contain a valid system folder."

The short-term fix was to reboot while holding the option ⌥ key. There was only one option ("Macintosh HD") in the list, which booted fine. The permanent fix was to use "System Preferences" → "Startup disks" and ensure that "Macintosh HD" was selected.


  1. This screenshot isn't from my system, so don't worry about the lack of OSX partitions here. It's just to show the unallocated space after the first partition on the drive.  ↩

Using structs in Arduino projects

I had some trouble getting structs to work in my Arduino project. This is how I fixed my code.

My project's ultimate goal is to replace the innards of a fibre optic lamp with a custom lightshow, but it's also a chance to play around with low-level circuitry & coding1. So far, I've designed and prototyped a hardware LED controller that's driven by an Arduino. The Arduino pumps out binary to the controller; this determines which LEDs light up.

A close-up of the circuit board

Each of the 4 LEDs you see on the board is an RGB LED, meaning it's actually a package of individual red, green, and blue LEDs. My old code used numbers to choose which colour to display, so it had function signatures like these:

void turnOnLED(int colour, int led);
void bounceColour(int colour); // A 'chase' pattern across all 4 LEDs.
void fadeBetween(int startingColour, int endingColour, int duration); // Fade between two colours in `duration` milliseconds

That's fine for pure colours, but it doesn't allow for compound colours (mixes of red, green, and blue) because the controller can only turn LEDs on and off. If you want purple, for instance, you turn on the red LED for a few milliseconds, then turn it off & turn blue on for a few milliseconds. Repeat this over & over and persistence of vision does the rest.

I could have used an integer for each colour, but using a struct keeps all the information in a single variable. I also created some constants for common colours using my new struct:

struct Colour {
    byte red;
    byte green;
    byte blue;
};

const Colour C_RED = {1, 0, 0};
const Colour C_BLUE = {1, 0, 1};
const Colour C_PURPLE = {1, 0, 2}; // 2 parts blue to 1 part red.
const Colour C_COLOURS[] = {C_RED, C_BLUE, C_PURPLE};

Next, I updated my functions to take a Colour parameter instead of an int. I also changed my parameters so they were pointers to the Colour instead; I couldn't get my code to compile without this.

void turnOnLED(Colour* colour, int led);
void bounceColour(Colour* colour);
void fadeBetween(Colour* start, Colour* ending, int duration);

The -> operator is used to access the values from the Colour* arguments2:

void turnOnLED(Colour* colour, int led) {
    float total_time = 3500;
    float total_colours = colour->red + colour->green + colour->blue;
    float timeRed = total_time * (colour->red / total_colours);
    float timeGreen = total_time * (colour->green / total_colours);
    float timeBlue = total_time * (colour->blue / total_colours);

    // Rest of function that rapidly changes between red, green, and blue removed for brevity.
}

Now the functions take a pointer to a Colour, I can change the calls to them to pass the address of a colour (using the & operator):

void loop() {
    bounceColour(&C_RED);
    bounceColour(&C_COLOURS[random(0, 3)]);
}

The final piece of the puzzle is to work around some limitations in the Arduino IDE. The IDE preprocesses your code before passing it to the compiler. One of its transformations is to generate function prototypes for your code – but it doesn't get it right for functions that use custom types, so you have to define them youself. The docs recommend you add it to a header file, but if you've only got a few then you can add them directly to your sketch. I added my function signatures below the struct Colour definition.

To summarise:

  1. Declare your struct at the top of your file.
  2. Update or override your functions so they take a pointer to your new struct. Remember to use the & operator when calling the new functions!
  3. Add function prototypes immediately after your struct definition for functions that take your struct as a parameter (or return the struct).
  4. Use the -> operator to access the properties in your struct.

An aside on understanding pointers

Pointers are a straightforward concept – "a variable that holds the address of a value, rather than holding the value directly," – but are really challenging to fully understand and use.

One trick that helped me was pronouncing the * in variable declarations as 'a pointer to'3, and pronouncing & as "the address of". So turnOnLED(Colour* colour, int led) is read aloud as "a function turn-on-LED that takes a pointer to a Colour and an integer". Or, consider the call to bounceColour here:

void loop() {
    bounceColour(&C_RED);
}

I say this as "call bounce-colour, and pass it the address of C_RED."

Another thing that helped was grasping that * means different things in declarations & usage. In declarations, * means "this is a pointer":

int* avocado; // Define a variable 'avocado' containing a pointer to an integer

But when using a variables, * means dereference: follow this pointer and use the thing it's pointing at.

// Define a couple of numbers, and a pointer to an integer
int x = 3;
int y = 8;
int* num;
num = &x; // "num equals the address of x"; it's now a pointer to x.
*num = *num + 4; // "Follow num and use the value of what it's pointing to, add 5 to it, and then store it back in the slot pointed to by num."

After this code, x is equal to 7, and *num is equal to 7 – they're both ways to access the same section of memory. std::cout << num will print the memory address (eg. "0x1234ABCD"); std::cout << *num will print the value "7".

This difference is why I write declarations as int* foo (and not int *foo). Keeping the asterisk next to the type emphasises that it's part of the type, not part of the name. foo is a pointer to an integer; there's no variable named *foo in this program. I find it a useful reminder (as does Bjarne Stroustrup), but it can trip you up if you declare multiple variables on one line:

int* bell, book, candle; // Declares one pointer to an int, and 2 ints
int *blinky, inky, clyde; // Also declares one pointer to an int, and 2 ints

int* bell, *book, *candle; // 3 pointers, but looks messy
int *blinky, *inky, *clyde; // Also 3 pointers, with a consistent style

Personally, I think declaring multiple variables on one line is best avoided. It's hard to tell if it's clever code or a subtle bug with declarations like that. But like all code formatting choices, it's down to individual taste.


  1. There are much better ways to make your own lighting projects if you don't have an interest in the low-level details. Neopixels, for instance, are reasonably priced, easier to extend, and easier to code for.  ↩

  2. -> is used because the argument is a pointer. If we were passing a Colour directly, we'd use the . operator instead (eg. colour.red).  ↩

  3. I preferred the word 'reference' instead of 'pointer' when I was learning C, but C++ has a references feature that makes that confusing now.  ↩

The Friend Multiplier

The second in an occasional series about product design heuristics. The first article was about mutual benefits.

Another heuristic that's useful in product design is something I call "The Friend Multiplier". There's two parts to it, and both are requirements:

  1. Your product must be useful on its own.
  2. Your product must be n times more useful if a user's friends use it too.

It has a powerful effect when your product is inordinately more useful (or more compelling, fun, deep, etc) when a user's friends also use it. Done right, it feels like an entirely new dimension opening up: a new world of possibilities, not a couple of new features1. It's important that the benefits are genuine (there's nothing magical about an artificial barrier of "Wait 4 hours, or invite 5 friends to continue"), and that the friends are a user's real friends2 – people who would have a coffee with your user, given the chance.

But don't overlook the first part: few products can get away with being useless for solo users. Users are more likely to stick around if it's easy for them to start using your product3, and everybody wants to try something before recommending it to their friends. And what if they're not your ideal user? What if they don't live in a big city, or lack early-adopter friends? What if they've got a 4 year old phone or a 10 year old laptop? Will your product still work well for them? Millions of people fall into this demographic and they're often neglected, especially by tech startups. They might not be your primary audience, but if you improve their experience then your product will be better for everybody.

But the multiplier is the magic part; the reason this heuristic indicates your product is on-track, and that word-of-mouth growth is plausible.

Some examples of friend multiplier magic

I really like FourSquare as an example of the friend multiplier, despite their recent disoriented product decisions. You use FourSquare to log where you've been4 and it recommends new places to you. That's mildly useful & fun on its own, but the day FourSquare really clicked for me is burnt into my brain. I was in a café that I'd never visited before and I checked in on FourSquare. FourSquare popped up a 2-year-old note from a friend, giving me the wifi password. Magical! My friend hadn't left it for me; it was a generic tip. But FourSquare knew we were friends, and knew that my friend had left a note here, so it showed me the note when I checked in. There's no wizardry in the technology powering it, but the effect was profound. I was now aware of hidden traces left by my friends; traces I could stumble upon, seek out, or leave for others.

LiveJournal is a simpler example. LiveJournal users post diary entries to the web, either publicly or privately. Which is useful in itself, if you want to keep a diary. But when your friends are on LiveJournal too, you get an intimate window into their lives. LJ's fine-grained privacy settings mean your friends have control over what they're sharing – but you'll know more about how they feel, what's on their mind, and what they're doing. I never had a LiveJournal, but a lot of my university friends did. I always had a sense that there was an in circle – a deeper, mutually supportive, more emotionally bonded group – that I was outside of. Again, there was no groundbreaking technology involved. But the social impact on individuals was huge.

It won't last forever

It's important to have a target number in mind for your ideal user's friends. The magical feeling normally ebbs away after a (normally low) threshold. Twitter's a rare example with a high threshold: it's better when you follow a few hundred people. Most products have more in common with a shared calendar; more than a handful of people using one results in a deluge of events, making it hard to find ones relevant to you & making the calendar feel like a mess. So pick a low number & design with that number in mind.

Some counterexamples

This heuristic isn't a law. Lots of products thrive without any kind of social features, and (as before) there's lots of products where it's best avoided. Sometimes that's immediately apparent: I don't want my friends involved in my banking or my to-do list. I think that it's best to keep them away from dating & relationships, too; Tinder's "friends you have in common" feature always felt like a warning about potential future awkwardness, rather than an endorsement.

There's also fuzzier cases. Features that seem like a good idea can fall apart like wet cake once the complications of real life get involved. Windows 10 has a feature where your friends can log on automatically to your wifi. Which sounds great – your computer already knows who your friends are, so why not let them onto your wifi without having to ask for the password? Ah, but the details: your computer doesn't know about your friends. It knows about your contacts. Contacts contain friends, family, colleagues, ex-lovers, stalkers, businesses, abusers, toxic people you've cut out of your life. A whole lot of people you'd want kept away from your wifi network.


The friend multiplier is a simple but effective guideline. It's not a requirement – if it doesn't make sense for your product, don't worry about it. But in an increasingly connected world, it makes sense to think about how that can make your products much more compelling.


  1. A "solo" vs "with friends" feature comparison would be missing the point, anyway: product design is about what your product lets users accomplish, not the bullet points you can list.  ↩

  2. "Real" isn't the same as "real life"; internet-only friends count for this too. It's about genuine, two-way friendships – not people followed because a user likes their jokes, or stuff they make.  ↩

  3. Or service – especially services. If it's easy for someone to start using your service it's more likely they'll find value in it, so it's less likely they'll cancel. Fewer cancellations mean a lower churn rate, and a lower churn rate makes it easier to build a viable business.  ↩

  4. Which is interesting in itself; "Wow, have I really not been here for a year and a half? Really?"  ↩

Why your multipart emails show up as plaintext

While building Electric Handshakes, a tool that connects job-hunters to employers via email, I spent days debugging an email problem. My code sent valid multipart emails – emails with both HTML and plaintext parts – but Gmail and Mail.app showed the plaintext. Why?

I found my answer buried on page 35 of an RFC12. The order of the parts matters. Later parts override earlier ones. Your mail client will display the final part it understands. I’d placed my plaintext version after the HTML version. Swapping those sections fixed the problem.

Other common causes of errors include:

  • Sending an invalid multipart message. There’s a few online validators. Both of these declared my message to be invalid, but didn’t point out the ordering error above.
    • In particular, make sure you send a valid Content-Type header. The capitalisation matters, and the value should be multipart/alternative.
  • Sending invalid HTML. I expected a complete HTML document would be required - one with <html>, <head>, and <body> tags rather than just formatting. But there isn’t consensus on that. Microsoft Outlook sends full HTML documents, as does Yahoo Mail, Hotmail, and Apple’s Mail.app. But Gmail sends formatting only, with no document structure. I think such an email is technically invalid3, but every mail client renders it anyway. Gmail can send & store smaller emails by omitting the document structure.
    • Even without a fully-structured document, markup errors can still prevent your message from being displayed. Make sure all your tags are closed, you’ve closed them in the right order, and so on.

If you’re using Rails, you can take some email pain away by using the letter_opener gem to preview your mails in your browser, and the premailer-rails gem to inline your CSS. It can also generate your plaintext parts automatically, and generally does a decent job of it.


  1. “In general, user agents that compose multipart/alternative entities should place the body parts in increasing order of preference, that is, with the preferred format last. For fancy text, the sending user agent should put the plainest format first and the richest format last. Receiving user agents should pick and display the last format they are capable of displaying.”  ↩

  2. When you find yourself on page 35 of a standards document praying for answers, your day’s already blown.

  3. It’s invalid because the HTML part is declared as text/html, and you need the structural tags for your document to be valid HTML.  ↩

Don't call it magic

I recently wrapped up 6 months of teaching at General Assembly. I was teaching WDI - a 12 week course that turns beginner programmers into employable junior web developers. It taught me a lot about teaching. One subtle thing I picked up is that we shouldn't talk about magic when we talk to novices.

Novice programmers like to ask questions, but those questions can't always be answered immediately. Either the explanation would be too confusing, or take up too much time, or be a useless distraction. So when the beginner asks, "But how did it know to do that?" we reply "Magic." We say "Magic" because developer culture is tied up with D&D, fantasy novels, and the idea that programmers are mystical and inscrutable. But what we really mean is "Don't worry about it." Rails isn't actually magic. You know that. I know that. The novices know that too1.

But the beginners don't always hear "Don't worry about it right now." Sometimes, they hear "It's beyond everybody's understanding, and we're all OK with that." These brackets and semicolons are confusing to everyone. Just repeat it back phonetically and everything will be fine. You can fix your bugs via trial & error. Nobody understands it anyway.

And that's a dangerous untruth. Programming is entirely understandable: every line, every word, every symbol. Some of the most productive teaching sessions have come from not allowing a student to handwave past some code and insisting they explain how it works, atom by atom. What's this line do? It assigns something to a variable. What's in that variable - what's its type? OK, what did we set it to? That second half, after the plus - what's it doing? Good. Next line.

One half of an instructor's role is imparting knowledge & understanding. The knowledge will come in time if you immerse the students in it, but the understanding will be slower to arrive if students aren't looking out for it. There's a difference between "We don't have to look behind the curtain right now," and "The Wizard is real". the language teachers use should reflect that.

The other half of the job is instilling confidence & self-belief. Programming is hard, technical, confusing work. It gnaws at almost everyone. You have to reinforce that yes, the students are programmers. What they are doing is programming - no matter how much they have to Google for answers and ask for help.

Again, the language you use can reinforce this message. It's better to ask a student "What do you know about [subject]?" instead of "Do you know about [subject]?". The former question is an open-ended chance for the student to show off their knowledge. The latter is often heard as "I have set an arbitrary high standard for your knowledge about [subject]. Do you dare assert that you meet it?". Beginners will always say "No," even if they have encountered it before. You'll quickly find out they do know about [subject]2, but it's still a tiny drag-down instead of a tiny build-up. You can't give people a giant boulder of confidence in one go. You can only give them tiny grains of positive feedback, and trust that one day it will outweigh their self-doubt.


  1. The Ruby source code might not know that.  ↩

  2. "Okay, the thing about frobbing the whatsit is..." "Oh, I know about that, I used that last week."  ↩

The Vimrc Antiques Roadshow

Last week, I gave a talk at Vim London about Vim's configuration file. We examined my .vimrc, Nic West's, Tim Mower's, Arturo Herrero's, James Cooke's, and Oliver Caldwell's. We also had a look at Drew Neil's during the Q&A.

The basics

Vim reads a configuration file called ~/.vimrc when it starts. You don't have to keep all your configuration in one file; several people put settings in other files (such as ~/.vim/bundle) and add source ~/.vim/bundle to their .vimrc. You can run vim -u filename to read a different settings file; nobody at the meeting used that, but some people use it to manage different settings across projects. You can do this automatically with set exrc and set secure.

Good habits

  • Add a comment with a URL when copying settings to your .vimrc from an online article.
  • Don't be afraid to ask Vim for :help.
  • Keep your .vimrc in source control. As well as keeping track of what you add and remove, it makes it easier to keep your files in sync between several different machines (such as home & work).
  • Bind your most used commands to the Fx keys; they're unused in Vim, so are free for your own use.
  • Use a tool to manage your Vim plugins; Vundle is preferable to Pathogen because it saves you from the horrible world of Git submodules.
  • Remember that Github won't show non-printable characters, so if you're browsing .vimrcs you might miss important details.

Modernising vim & making it more sensible

Most .vimrcs contain some settings to smooth over some foibles and legacy features:

  • set nocompatible tells Vim to forget about Vi compatibility. Interestingly, as soon as you create a ~/.vimrc this option gets set automatically.
  • set hidden allows for hidden buffers - basically stopping Vim whining when you open more than one file.
  • set ttyfast might also be on by default, depending on your $TERM environment variable. When on, Vim will update your screen in bigger batches.
  • set backspace=indent,eol,start enables backspacing over everything, like every modern editor ever.
  • nmap j gj and nmap k gk make j/k move the cursor up/down by lines on the screen, not lines in the file. Navigation feels more natural when using word wrap this way.
  • au BufRead,BufNewFile *.md set filetype=markdown sets foo.md files to be Markdown, not Modula2.
  • You can get better tab completion for Vim commands if you set wildmenu. You may also like to set wildmode=list:longest, and you can ignore certain files - like Python compiled files - with set wildignore=*.pyc.
  • You can set a mark in Vim with ma, and jump back to that line with 'a. If you want to back to that exact cursor position, you can use `a, but that's harder to press. You can swap ` and ' with nnoremap ' ` and nnoremap ` '.
  • Normally Vim will scroll the screen when the cursor hits the first/last line. You can make it scroll 3 lines earlier with set scrolloff=3. Nic went as far as to set scrolloff=15.

Views & Sessions

Vim supports views, which save the cursor position & code folds between editing sessions (along with a few other settings). I've used an autocommand to automatically save and load a view whenever I edit a file:

au BufWinLeave ?* silent mkview
au BufWinEnter ?* silent loadview

Vim also has a sessions feature, which saves the whole editing session - everything in the views, along with the open buffers, window layout, tabs, etc. Oliver has bound F7 and F8 to save/load a session:

nnoremap <F7> :mksession! .quicksave.vim<CR>
nnoremap <F8> :source .quicksave.vim<CR>

I recommended he move them to non-neighbouring keys, to make it harder to accidentally hit the wrong key & save an empty session.

Plugins and other tools

The most interesting plugins were:

  • CtrlP, a fuzzy-finder for navigating projects.
  • Vim-endwise, to add end automatically in Ruby.
  • Vim-numbertoggle will show you relative line numbers in normal mode and absolute line numbers in insert mode. This makes it easier to write multiple line-spanning motions. You can use set number to always show line numbers and set relativenumber to always show relative numbers.
  • Vim-unimpaired adds some navigation commands to the [] keys.
  • Easymotion turns motions into a multiple-choice option.

Some tools that people use with Vim include:

  • Ack (and the similar Ag, AKA "The Silver Searcher"). They're grep-like tools that have useful features like respecting your .gitignore file. You can tell Vim to use ag for its built-in grep, as well as telling CtrlP to use it too:

    if executable('ag')
      set grepprg=ag\ --nogroup\ --nocolor
      let g:ctrlp_user_command = 'ag %s -l --nocolor -g ""'
    endif
    
  • Screen and tmux remain popular. Plugins like vim-tmux-navigator and tmuxline provide some Vim integration.

  • Git is used by everyone for source control, often with the vim-fugitive plugin.

Search and search highlighting

Vim can highlight search results as you type:

set hlsearch
set incsearch

set ignorecase makes searching case insensitive; set smartcase makes searching case insensitive unless you use any capital letters. If you find yourself using substitutions a lot then you might want to set gdefault to make replacement global without having to add the g flag.

Refreshing

I've found that Vim sometimes messes up syntax highlighting or forgets the filetype. I've bound F5 to refresh highlighting and filetype detection:

noremap <F5> <esc>:syntax sync fromstart<cr>:filetype detect<cr>
inoremap <F5> <esc>:syntax sync fromstart<cr>:filetype detect<cr>a

Oliver used his F5 key to regenerate his ctags:

command! GenerateTags call system('ctags -Rf ./.tags --python-kinds=-i --exclude=.git `cat .srclist`') | echo
nnoremap <F5> :GenerateTags<CR>

You could combine these if you wanted to.

Swapfiles, backups, and undo

Vim can create backups when you write files, save your undo history between sessions, and uses a swap file to track changes when running.

Just about everybody tells Vim to put these files in their own directories. At the very least, you won't have to add them to your .gitignore files that way:

set undodir=~/.vim/tmp/undo/
set backupdir=~/.vim/tmp/backup/
set directory=~/.vim/tmp/swap/

It seems like a good idea to make those directories if they don't already exist:

if !isdirectory(expand(&undodir))
  call mkdir(expand(&undodir), "p")
endif
if !isdirectory(expand(&backupdir))
  call mkdir(expand(&backupdir), "p")
endif
if !isdirectory(expand(&directory))
  call mkdir(expand(&directory), "p")
endif

Turn on persistent undo with set undofile. Turn backups on with set backup. Some people turn off swapfiles with set noswapfile; Vim speeds up, but you're promising that Vim will always shut down cleanly & you'll never open the same file in two different Vim instances.

Avoiding the escape key, and other personal taste issues

Some people like to avoid the escape key, and use keystrokes like jj and jk instead. Tim forced himself to learn this by remapping his Esc to a no-op:

noremap <Esc> <Nop>
inoremap jk <Esc>
inoremap jj <Nop>

We also mentioned the influence of the ADM-3A, a common terminal used when Vi was written. Its Escape was located where modern Tab keys are and its tilde key was labelled "Home". This led to the use of ~ as a shortcut for the home directory. It also had arrows on h, j, k, and l - which is why they're used for navigation in Vim and other programs.

James' .vimrc swapped : for ;, to cut down on shift usage:

nnoremap ; :
nnoremap : ;

But James recommended against this, as when he uses other programs with Vim mappings his muscle memory is basically useless.

Obeying coding conventions

If your coding style guide specifies a maximum line length, you can set colorcolumn=80 to add a stripe at the 80 column mark.

You can show hidden characters (like trailing whitespace and tabs) with custom symbols. Here's James':

set list 
set listchars=eol:,tab:▷\ ,

Shortcuts for stripping trailing whitespace were also popular: nnoremap <F2> :%s/\s\+$//e<CR>

Micro-optimisations

If you find yourself performing a repetitive task, it's worth finding a way to optimise it. Nic wrote a function that copies a path to his clipboard, so he could paste it easily into a VM. Tim had a snippet that var_dump()s the variable under the cursor (a PHP way of printing out variables for debugging): nmap dvd viwy<Esc>odie(var_dump());<Esc>hhP

James likes to use Ctrl+j as a shortcut for :tabedit:

nnoremap <C-j> :tabe

The trailing space is important so you can start typing the filename immediately.

Oliver bound leader z to replace the misspelling under the cursor with the first suggested fix: nnoremap <leader>z 1z=

Arturo added a mapping for :w!! to save the current file via sudo:

cmap w!! %!sudo tee > /dev/null %

Highlight words you want to avoid

It's good practice to avoid words like "obviously", "just", & "simply" when writing educational articles. You can use a custom highlighter to make them stand out.

Miscellaneous

  • set matchtime=2 determines how long Vim will highlight matching brackets, in tenths of a second.
  • You can use vim folds to hide code in long files by "folding" it up into one line.
  • Searching Github for ':wq' gives 7.1 million results.
  • set autoread will reload the file if it changes. It's not tail -f, though: you have to run a shell command or invoke :checktime to make Vim re-check the file.
  • James recommended Bare-bones navigation, a previous talk by Kris Jenkins.
  • Oliver had an embedded Python program in his .vimrc to automatically set up his virtualenv. I'd keep this in a dedicated script and call it from the .vimrc instead.
  • set nostartofline keeps the cursor in place when you re-indent code. Sadly, this doesn't move the cursor with the indent so it's not as useful as it sounds.
  • zz moves the line under the cursor to the middle of the screen, which is useful when coding in front of an audience. zt moves the current line to the top. You can also use set cursorline to underline the current line, which can also help an audience follow along (or a presenter to follow their notes).
  • Text objects are awesome and you should definitely learn them.

The mysterious pink ping pong balls

After the talk, Tim wrote in about the Ctrl + l mapping we failed to decipher. He had some bugfixes and an explanation: inoremap <C-R> @@@<Esc>hhkywjl?@@@<CR>P/@@@<CR>3s lets you use Ctrl + r to copy the previous line word-by-word.


Thanks to Drew for organising Vim London and to Skimlinks for hosting the meetup.

How to customise rotating wallpapers on Ubuntu 13.10

Ubuntu has a nice selection of wallpapers that change throughout the day. But there were a couple that I didn't like, and the GUI doesn't have a way to customise the selection. Here's how I got rid of the images I disliked and added my own to the rotation.

The photos are kept in /usr/share/backgrounds/. This directory turned out to contain a bunch of pretty pictures I'd never seen before — each version upgrade brings a new set, but they're not included in the rotation automatically. There's also no GUI to flip through a set of images throughout the day; you must create a couple of XML files instead. I followed these steps:

  1. Delete any images you don't want from /usr/share/backgrounds.
  2. Create a directory for your custom XML files:
    mkdir ~/.local/share/gnome-background-properties; cd ~/.local/share/gnome-background-properties
  3. Create an XML file that describes a slideshow. I wrote some Ruby to generate the file; you can run it like this:

    ruby generator.rb /usr/share/backgrounds/*.jpg > mywallpapers.xml

    If you've got other files you'd like to include, you can include them as extra arguments before the >:

    ruby generator.rb /usr/share/backgrounds/*.jpg /home/alex/wallpapers/*.jpg > mywallpapers.xml

    If you'd like to create the files by hand, you're mad, but here are the rules:

    • It should have a <starttime> element with a date in the past.
    • It should have pairs of <static> and <transition> elements. The <static> element displays a wallpaper for the set time; the <transition> element crossfades it to the next one. The duration is given in seconds, so each wallpaper displays for 30 minutes.
    • The last element should transition back to the first. This makes the slideshow loop forever.

  4. Create an XML file mywallpaperlist.xml to show your new wallpapers in the appearance properties. Don't forget to change the <filename> property to contain your own home directory. You can use pwd from the terminal to show it if you don't know the correct path.

    <!DOCTYPE wallpapers SYSTEM "gnome-wp-list.dtd">
    <wallpapers>
      <wallpaper deleted="false">
    <name>My Wallpapers</name>
    <filename>/home/alex/.local/share/gnome-background-properties/mywallpapers.xml</filename>
    <options>zoom</options>
    <pcolor>#2c001e</pcolor>
    <scolor>#2c001e</scolor>
    <shade_type>solid</shade_type>
      </wallpaper>
    </wallpapers>
    

That's it! I didn't have to restart Unity, or jump through any other hoops. If your files are readable and valid XML, then your wallpaper pack should show up if you reopen the appearance properties.

Burn Facebook to the ground

AKA "A tale of two share widgets".

I recently built an internet toy. A big part of the toy is social sharing; I wanted players to tell their friends their scores. Most people use Twitter or Facebook, so sharing to those services should be as simple as possible.

A "Share your score on Twitter" button took me about 5 minutes. It's just a link. Customise a couple of parameters and you're good to go. Tumblr's just as easy. You have to click through a few options to see an example, but it's also just a link. But Facebook... oh, Facebook.

Facebook's sharing process is shamefully convoluted. If you just want to share a URL, you can use a straightforward hyperlink, though it will discourage you1. But if you want to share a URL with a customised description – something Twitter and Tumblr managed with a simple link – here's what you have to do:

  1. Create a developer account.
  2. Create a new app.
  3. Get that app approved & available to the public.
  4. Include Facebook's JavaScript in your own page2.
  5. Include an empty div in your page for Facebook's JavaScript to use.
  6. Make a call to FB.ui with the relevant details.

And to top it all off you're now maintaining that code forever, because Facebook's API thrashes around like a ferret on a leash. The Share button is deprecated! No, the Share button is supported! But you should use the JavaScript Feed Dialog! There's also a URL redirection method, hidden away with a minimum of documentation (and still requiring an App ID). And the format might change, so make sure you're reading Facebook bug reports! But good luck finding them, as they're excluded from the Google search index. The Facebook developer experience is a fractal of being told to go fuck yourself.

The story might end there. We have to create an app first, but the URL redirection method is just a link. So it should be equally simple, right? No. It doesn't work. I got an error 100: "Invalid parameter (requires valid redirect_uri)." What is a valid redirect_uri? What's an invalid redirect_uri? There is nothing in the documentation. Why was mine invalid? The error doesn't say. I'd fat-fingered it and missed off an ampersand so I wasn't supplying a redirect_uri at all, but there's a lot of voodoo out there about not escaping the URL or setting your app domain. Obviously I wasted an hour on that before realising my mistake. Couldn't we have had a "missing required parameter" error instead of "invalid parameter"? No, because then you wouldn't feel the full force of Facebook's contempt. And even then, when you've finally got it working, mobile users won't be able to share it.

Move fast and break things is a valid philosophy for a startup, but it doesn't work if you're a platform. Cool URLs don't change; cool APIs don't flipflop around based on whimsy.

Further reading: oAuth of Fealty by Ian Bogost.


  1. Laughably describing the Share Button as a 'simpler' alternative.  ↩

  2. This step includes an implicit "... and trust Facebook to do something harmful to your page, either accidentally or intentionally."  ↩