Blog

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."  ↩

FAWM 2014

Last year I took part in FAWMFebruary Album Writing Month. I did it again last year, and actually made an album this time: 15 songs in 28 days. It was super-stressful, and for the sake of my sanity I should not be allowed to aim for 14 songs in 2015.

Most of it was made on my iPad, although I finished a couple of tracks in Ardour. I used a lot of GarageBand, along with Figure, Thor, Korg Gadget, and Funkbox. I bought a cable that let me record my guitar from my iPad, and friends lent me a banjo & a decent audio recorder.

It's a mix of acoustic recordings, synthy dance electronica, and atmospheric darker electronic music. You can explore it track-by-track on my FAWM page (until 2015, as the site retires older stuff each year), or download the whole album as a zip. I've also included the MP3s below.

  1. A Departure (acoustic folk-rock)
  2. Let's Get Started (dance/electronica)
  3. Are You Receiving? (synthpop/electronica)
  4. Synthetic Floor Filler (dance/electronica)
  5. It's Going To Happen (spoken word/folk-rock)
  6. String Theory (instrumental/folk)
  7. Time Slips Past (chiptune/electronica)
  8. Boopwub (electronica/dubstep)
  9. Ice Giants (dance/electronica)
  10. Numericalities (dance/electronica)
  11. Forever Safe (ambient/electronica)
  12. Elongate Thistledown (dance/synthpop)
  13. Thanks, Steve (instrumental/soft rock)
  14. Potbound Mint (synthpop/waltz)
  15. Sometimes Things Just Go Your Way (instrumental/chamber pop)

As an extra bonus track, here's a dance track I made post-FAWM. Unlike last year, I'd like to keep making music in the rest of 2014. I must have at least another EP in me.

Products with benefits

The first in an occasional series about product design heuristics. The second part is about what "social" should really mean.

I generally work with startups, which means I'm working with companies that are still trying to figure out what they're building. I'm not a product manager, but over time I've assembled some heuristics that help figure out if a product is on track.

When you're building a user-driven product or adding a feature, the user must benefit and the company must benefit.

This sounds obvious but it's surprising how many companies put their own wishes above their customers'. It's an approach that can pay off in the short term, but at best you're growing an indifferent, surly customer base. At worst, you're driving people away. Human beings have very little patience. If you're not putting them first, they'll go somewhere else.

It's a heuristic – not a cast-iron rule – so it's not a disaster if you take a different path. But you are swimming against the tide, and you're going to have an uphill battle1. You're going to have to explain to your users why they should do what you want, or create something so compelling they'll jump through your hoops. Equally, if users benefit but the business doesn't, make sure you have a plausible plan for the long run. Your business isn't going to implode, but it might limp along, struggle to attract users, or fail to evoke the passionate response you want.

An example

A startup is building a Foursquare-like service, but instead of checking in to places users will write a short review. The startup thinks they'll gather deeper knowledge about the places you visit, and that they can monetise that database. But they're missing a key step: why will a user write a review? What does the user get out of it?

Writing a review is a lot of effort. Even if it's just a sentence or two, the user still has to figure out:

  • What's good about this place?
  • What's bad about it?
  • Am I broadly for or against?
  • How am I going to express that in words?

And don't forget the user's out in the world, probably with friends. Will they really ignore the people with them and tap out a review on their phone? This is starting to sound like a rather anti-social product. Users won't complain about it; they just won't use it. Check-ins are already an unnatural behaviour – something a user's persuaded into trying, rather than demanding – and the mandatory review step makes that even harder.

Carrot and stick

People use your features for two broad reasons:

  1. Good things happen if they do.
  2. Bad things happen if they don't.

The first motivation is infinitely more preferable: the carrot is better than the stick. If you're lucky you can sometimes force things on your user, but everything flows more smoothly if the users want it themselves.

People shop on Amazon because they want cheap products conveniently delivered to them. They post statuses on Facebook so their friends click 'Like'. They sign up to Groupon to get big discounts in their inbox. Users actively want these things: they would complain if you took them away. But there's a benefit to the companies too: profit for Amazon, and engaged users for Facebook & Groupon2.

The second option puts your company's needs first. It's a mild form of blackmail. You're putting an obstacle in front of your user, and hoping that they want your offering enough to put up with your bullshit3. You can spot these by looking for double obstacles: something you've added to make the first obstacle work4. YouTube's first obstacle was "Watch this advert before you see a video", but nobody wants to watch an ad. So they added a second obstacle that makes you wait 5 seconds before you can skip it. Groupon really wants to email you every day5, so as soon as you land on a Groupon page they show you an undismissable box demanding you sign up.

A screenshot of the undismissable Groupon popup.

Nobody on the internet thinks to themselves "I really wish this web page would demand my email address the instant I visit it." Groupon's betting that the people it turns off – the people who say "Stuff this" and close the tab – are worth less to them than the email addresses from casual visitors who want to see the offer. You could argue that users benefit from this too, as they get offers in their inbox every day, but it's an arm-twisty way of getting users. The user didn't get the chance to see some Groupon deals and decide to sign up because they liked the look of them: they signed up because that's the only way to see the offer in the first place.

Streaming music is another example. Companies like Rdio, Pandora, and Last.fm. Their streaming radio products generally limit how many times per hour you can skip a track. It's because of licensing laws, and the costs of licensing music: labels charge less for "radio" plays than "on-demand" plays, and it's easier to convince the labels that you're in the former category if you have limitations. Users hate it, but the businesses think it's the only way they can survive6.

Magical sticks that turn into carrots

There are occasional circumstances where you obstruct your user for a good reason: for the good of the community. A real life example is airport security: nobody wants to have to queue for ages and get searched, but most people don't want bombs on planes. So we mildly inconvenience everybody so society avoids hostage situations. A tech example is Dattch, a lesbian dating app. There's a bunch of people on the internet who love to spam & harass lesbians. So Dattch insists you sign in with Facebook, and every profile is screened by a human to ensure it's real. It's not that they think their users want to wait for several hours as soon as they sign up – it's that it's better for the rest of the community if the company has a chance to screen out the weirdos.

Who's getting it right?

It's hard to give examples of companies that get it right, because it looks so damn obvious. Consider any online shop, pretty much: people want to buy things, and the company wants to sell things. As long as the customers are happy with the product and the business is making a profit, everybody's happy. Or consider Dropbox: users benefit from having their files available everywhere, and Dropbox benefits from power users who buy premium accounts (and from being the de facto way of syncing files across devices).

Marketplace leaders are good examples, too – the eBays & AirBNBs of the world. Their users are both buyers & sellers: buyers win by having easier access to more products/accommodation, and the sellers can reach a bigger audience or even sell something they couldn't before. The business wins by taking a commission.

And despite the way they haemorrhage money, music streaming services would argue that they benefit from data when users listen. It can use that data to improve its personalisation services, and also sell aggregate information back to the music labels. It's still a cut-throat business, which demonstrates that even if you think you've ticked both boxes you're not guaranteed success.

The takeaway

When creating a product or adding a new feature, make sure that both the user and your company will benefit. Users are fickle; they won't do things solely because you want them to. There's got to be something in it for them. And likewise, you won't last long as a business if you give the users everything: there's got to be something in it for the company too. It might be a long-term payoff. It might not be direct. It's OK if it's not always there, but have a clear reason in your mind to forego it.


  1. I enjoyed this mixed metaphor so I left it in.  ↩

  2. "Engaged users" means "People use your product", which means "We can sell things to them." Groupon sells to people directly. Facebook sells advertising space, which is an opportunity for someone else to sell to their users.  ↩

  3. This isn't the same as a dark pattern – dark patterns are actively trying to trick people. These examples are trying to work around human nature.  ↩

  4. Another way of spotting this is asking, "Did this request come from the advertising department?"  ↩

  5. If they email you every day, they can sell to you every day. If they sell to you every day, you're more likely to buy something.  ↩

  6. Spotify is taking a different approach: they're trying to get a huge audience before they run out of money. They have higher licensing costs, but if they can get enough users then economies of scale kick in and they'll be profitable.  ↩