Ending self-hosting

I’m not sure if I’ll regret this or not, but I’ve shut down my little WordPress docker container and moved the site to wordpress.com.

I’ve needed to decommission the EC2 instance running the site anyway, because I moved Secateur to a Graviton instance to save money, and the amd64 instance is just sitting there running this site, my git repositories, and basically just costing me money.

So this is a an experiment: I’ve moved some of the static sights I’ve been hosting to Netlify, and instead of moving the blog to Hugo, like I’ve been meaning to try, in the mean time I’m simply putting it in someone else’s hands.

What it took to delete my ‘like’ history

A while ago I decided to delete my ‘like’ history on Twitter.

Since then, I’ve discovered interesting ways in which Twitter’s engineering makes it impossible to do.

The recent ones are easy, if you have access to the Twitter API. You can request the list of ‘favorites’ (they never renamed them to ‘likes’ on the API layer) and from there you can delete them, one at a time.

#!/usr/bin/env python3
import os
import twitter

api = twitter.Api(
    os.environ['CONSUMER_KEY'], os.environ['CONSUMER_SECRET'],
    os.environ['ACCESS_TOKEN_KEY'], os.environ['ACCESS_TOKEN_SECRET'],
while True:
    favorites = api.GetFavorites(count=200)
    if not favorites:
    print(f"GetFavorites(): Received {len(favorites)} status objects.")
    for status in favorites:
        print(f"{status.user.screen_name:20}: {status.text}")
        status = api.DestroyFavorite(status_id=status.id)

From the looks of things, you can delete the most recent 3,000 or so ‘likes’ you’ve ever made using a script like this.

After that, things get harder.

I’ve been thinking about what a ‘like’ on Twitter and Facebook means.

When you click it, you’re sending a single piece of information to the person who wrote the tweet. Ostensibly, I guess, that you liked their tweet. Really it’s just read-receipt for Twitter — you’re just telling someone that you read what they wrote. If you’re mentioned in a tweet it seems almost fait accompli that you’re going to hit ‘like’. It means “yeah yeah, I got your message”, and not much else.

After that, it just sits there, in your history, and in Twitter’s database. It becomes part of their algorithm to tune your feed to keep you hooked.

Once you’ve deleted all the ‘likes’ that the API will allow you to see, you’ll find, to your surprise, that there are still thousands to go. They show up on your ‘likes’ page, and they show up in your exported data archive, but the little red ‘heart’ isn’t active.

I guess these ‘likes’ are in some sort of static cold storage. I’d be fascinated to hear from a Twitter engineer (a) how it works, and (b) why they still work for Twitter.

You can’t delete them, the platform doesn’t treat them as ‘liked’ tweets at all.

The only way to get rid of them is to is to click the heart twice: to ‘like’ it anew and then ‘unlike’ it. This sends the author and anyone mentioned a fresh notification telling them that you liked their tweet.

If you want to delete your old history of tweets. You’re going to have to send a new notification to every single person in every single tweet you’ve ever liked. Thousands upon thousands of them. For tweets that can be years old.

I like it when people like my tweets. I come up with witty, snarky things to say so that people click ‘like’. I want to write more of them. It keeps me coming up with ‘tweetable’ things to say.

At its most cynical, “like” is the endorphin button: Every time you like someone’s tweet you’re sending them a tiny little burst of pleasure that keeps them addicted to Twitter.

The sad thing is, the better you are at twitter — the better you are at pithy, wry sarcasm in short form — the more effective you are at keeping everyone else addicted to an intentionally-addictive platform.

We all are each other’s drug pushers.

The only way someone won’t get a notification when you “like” their tweet is if (a) they don’t follow you, and (b) you set your account to ‘private’ mode first.

To get an exhaustive list of the tweets you’ve liked, you have to download your Twitter “data archive”, a .zip file of all data Twitter keeps on you (that they want to admit to). In the date/like.js file is the list of all the likes, along with their ID numbers.

This would be enough to use the API to ‘like’ and ‘unlike’ each tweet, but if you want to reduce the disruption to others you need to take more steps.

I used the Twitter API to retrieve the full details of every Tweet in the list so that I could group them by author and by accounts mentioned in the Tweet. I ended up writing some unreasonable amount of Python and SQLAlchemy code to correlate them all.

My plan was to group the operations so that I could (a) prevent notifications to strangers by being in ‘private mode’ whenever I was running my program, and (b) when it came time to disrupt my friends’ by sending them notifications, I’d try to group them all so it all happened at once instead of spread out over interminable days of hundreds of notifications.

It would be hundreds, after all: your likes are pretty proportional to the tweets you see. The tweets you see most tend to be the friends you’ve followed the longest.

I started working my way through the list. A couple of times a day the routine was:

  • set my twitter profile to ‘protected mode’
  • start my script. Watch it chug through ‘liking’ and ‘unliking’ a stream of tweets
  • crash when the rate limit was hit
  • reset my Twitter profile to ‘public’ mode
  • wait a couple hours, and repeat

For friends of mine who’s tweets I’d liked *hundreds* of times, I either blocked them first so they wouldn’t get notifications (knowing I could unblock them afterwards, and invite them to re-follow me), or I tried to message them first to warn them that I was about to spam them.

For the people who get scores-to-hundreds of notifications, having this tweet pinned on the top of my profile would at least give them some explanation as to what the hell was happening.

It definitely weirded people the hell out though. Some of my friends politely removed me till it was done.

I also accidentally re-started conversation threads left hanging years previously, thanks to the fresh notification on an old tweet.

But finally, after about two weeks of running the script at least once a day until rate-limited, I had finally deleted all my likes. Finally, I had a clean slate.

Only, I didn’t.

Completionists gonna have a hard time with this

You can’t delete a like on an old tweet without ‘liking’ the tweet again.

It turns out that there are plenty of tweets old tweets you might have hit ‘like’ on once, but you can’t see any more.

Tweets from deleted or suspended accounts remain in the count even if they’re not visible by the system. Tweets by people who you don’t follow who have protected their accounts are inaccessible.

Tweets that you liked, by Twitter users who have subsequently blocked you, can never, ever by removed by you.

And so, at the end of the day, I can no longer see my liked tweets, but maybe you can. And some of the tweets will be people who no longer follow me, and some of them — there forever — will be from people who loath me.

What was the point of all this?

When it became obvious that I wasn’t going to be able to delete my like history without sending thousands of notifications. I decided to press on with the project because I wanted to see exactly what social cost Twitter would exact upon me, just for trying to reclaim my own data.

I didn’t measure how many people unfollowed me, but I certainly got some bewildered reactions. I had friends reach out to me out-of-band to ask me if my account had been hacked.

And for all that, it turned out to be un-completable. Twitter will not allow you the means to completely wipe your “like” history short of actually deleting your account.

‘Likes’ might not seem important. But it’s critical to the sorts of algorithms social media use to tune their feeds to keep you hooked, and to target you with advertising. They’re also a paltry, lazy mode of communication. From now on, if I like someone’s tweet, I’ll conjure the mental effort to at least tell them so, with words.

Don’t click “like”, Digital Minimalism by Cal Newport (2019)


Every first-year psychology student eventually does the rat experiment.

The rats are hanging out in cages called “Skinner boxes“. They’re equipped with a food dispenser its fuzzy occupant activates by manipulating a lever. The rat pulls the lever, and a food pellet may (or may not) come out of the food dispenser.

In first-year psychology, you use these rats and Skinner boxes to learn about ‘operant conditioning‘. You learn that the rats’ behaviour varies dramatically depending on the reliability of the lever.

The cages are configured a couple of different ways:

  1. Continuous reinforcement: every time the lever is pulled, a pellet of food is delivered, it’s 100% reliable and predictable.
  2. Fixed ratio reinforcement: It’s not every time the lever is pulled, but it’s a set ratio, like every 5th time the lever is pulled, a pellet of food is delivered.
  3. Variable ratio reinforcement: The lever will work, but only after an unpredictable number of attempts, like a roll of the dice.

The rats have been in their assigned cages for a while, they know the deal. The experiment that you are performing is to measure how often the rats are pulling those levers, and how long it takes them to stop pulling the lever when you disable the food supply — when you make all the levers stop working.

You may have heard about this experiment before, and you probably know how the results shake out.

The rats in the ‘continuous reinforcement’ cages aren’t actually very interested in the lever. Their lever was 100% reliable. They were pretty much just pulling it when they got hungry, and they’re the first to give up on it when it stops working. To these rats, the lever is simply a tool they use to get food. When it’s clear that food isn’t coming out anymore, they abandon it.

For ‘fixed ratio’ reinforcement, the rats’ behaviour is very different. Once the rats realise that the lever only sometimes delivers food, they wrap their little claws around it and tug it frantically for all they’re worth. Their ‘response rate’ skyrockets. Not only that, but when the food stops, it takes these rats a lot longer to give up on pulling that lever.

And the ‘variable ratio’ rats? The ones who’s lever worked like a roll of the dice? They will never stop. Your two-and-a-half-hour undergraduate psychology class will not be long enough for you to wait around to see them give up pulling that lever. Desperately trying, again and again, for a food pellet that isn’t coming. Even though they’re not hungry.

…the ‘variable ratio’ rats? The ones who’s lever worked like a roll of the dice?

They will never stop.

This probably sounds familiar to you. You’ve seen this before. You’ve seen the ranks of slot machines at casinos. The relationship of operant conditioning and intermittent reinforcement to human behaviour is pretty well known. It’s not just the rats that fall for this, and you know it.

Have you ever found yourself wondering why Facebook sucks now?

It didn’t used to. That’s the honest truth. Facebook, when I first signed up in 2007, was the most astonishing website on the planet. For the first time, I could go to a single website and see, at a glance, what was going on in the lives of everybody I cared about. It was amazing, and new, and valuable. I could go to Facebook, catch up with my world, and then go on with my day.

Have you ever found yourself wondering why Facebook sucks now?

That’s not the case anymore. No matter how many friends on Facebook you have, what you see when you go there is a string of shared memes from a subset of about a dozen people on your friends list. On your feed you’ll see posts from strangers. Friends of friends that you’ve never met, you never wanted to know what they thought of water fluoridation, or vaccination, but Facebook has decided that that’s what you need to be looking at right now.

Facebook decides. Not you.

The linear timeline is pretty much gone entirely. Facebook goes out of their way to prevent you from seeing the one thing you wanted: a straight line of chronologically sorted posts, by the friends in your friends list.

Every social media platform eventually adopts a non-linear timeline. Instagram is dreadful: three weeks into 2019 and Instagram is finally showing me my friends’ New Years Eve pics. Twitter tried in 2016 and backtracked after an outcry, but now, in 2019, they’re rolling it out for real:

Why? Why the inevitable shift away from a linear timeline?

The answer is that while a linear timeline is perfect for readers, it’s cripplingly counterproductive to the fundamental mission of a social media platform — the consumption of your time and attention.

A linear timeline can only hold your attention for a finite period: until you’ve caught up. Once you’re caught up with all the news, you close the window and go on with your day.

Closing Facebook is not in Facebook’s best interests. Facebook’s business model depends on your attention, not on your satisfaction.

Consider a tool, like a screwdriver. The measure of success for someone making a screwdriver is that you, in fact, spend less time screwing in screws than you did before you bought their high-quality screwdriver.

A linear timeline can only hold your attention for a finite period: until you’ve caught up.

Facebook used to be like that, too. I’d go to Facebook, get what I wanted, and then close it again. It was the greatest website in the world. That was good enough for Facebook too, for a while.

But “engagement” is increased, not by providing what the customer wants, but by withholding it, so you keep coming back.

Imagine your screwdriver again. What if its business model actually depended on you continuing to screw in screws, no matter what? What if all their research was bent towards making sure you were never quite finished with the screwdriver? What would that look like? Would it still be a good screwdriver?

“engagement” is increased, not by providing what the customer wants, but by withholding it, so you keep coming back.

You’ve heard the phrase “if the product is free, then you are the product”. Everyone understands that Facebook is making money off of your attention. What I want you to understand, today, is that the things that annoy you the most about Facebook are deliberately designed to increase your time spent there.

Social media platforms don’t make their money by providing pellets of nourishment every time you pull a lever. They make all the money in the world just by making sure that you keep on pulling.

The one true way of doing Django deployments…

…doesn’t exist. (Sorry)

Presented at PyCon AU, Brisbane, on August 3, 2015

The power and flexibility of Django comes with drawbacks. One of the toughest for project management is working out how to deploy your Django application. If you ask five different authorities on how you should do it, you’ll get six different answers.

And if someone says “Just use fabric!”: they’re not helping.

Release management, dependency wrangling, virtualenv care and feeding; to .whl or .deb? To containerize or Heroku-ize? Do you really have to allow your servers unconstrained Internet access just to build your virtualenv?

As a Django user, you might end up writing more deployment solutions than Django projects. I know I have.

There’s no one true way of doing Django deployments, but some work better than others. Maybe I can show you.