10 growth hacks that helped Metro.co.uk achieve 27 Million Monthly visitors

Metro Monthly Unique Visitors

Metro Monthly Unique Visitors (Jan’12 – Feb’14)

Over the past 12 months Metro has been on an amazing growth curve. Some of it is being in the right place at the right time for algorithm changes but a lot of it was planning and then execution of a growth (hacking) strategy.

Hack 1: Responsive Design

Metro Mobile Monthly Unique Visitors

Monthly Unique Visitors from Mobile (Jan’12 – Feb’14)

We decided a responsive design would be the best way to capitalise on the explosive growth in mobile. A nine month redesign process culminated in Metro going responsive on 7th Dec ’12. We immediately saw growth from social referrals with Twitter’s almost doubling over night. The other benefit of responsive sites is that there is only one URL. As this is the key used to store ranking information in search and social algorithms you don’t want this split between multiple domains like m dot.

The key takeaway is that if you give people a great experience on all devices then they are much more likely to read, share and return to your content.

Twitter referral visits Nov '12 - Feb '13

Weekly Visits from Twitter (Oct’12 – Feb’13)

Hack 2: Focus all development efforts on growth

WordPress.com Stats for Metro

WordPress.com Stats for Metro (Dec’12 – Feb’14)

We also migrated to the hosted platform as a service provided by Automattic on vip.wordpress.com. This enabled all of our costs and resources to be focused on growth as they didn’t have to worry about caching, servers or anything that didn’t improve experience for our readers or editorial users. The amazing thing about the platform is that it is a flat fee. This means that although our traffic has grown 350% year on year our costs have not changed. The depth of their out of the box features plus ecosystem of plugins ensured that we did’t have to worry about commodity features such as SEO, site maps and editorial workflows as someone else had built and open sourced an approach.

Hack 3: Open up content creation to anyone

Metro Blogs Monthly Unique Users

Metro Blogs Monthly Unique Users (Dec’12 – Feb’14)

The other great thing about the WordPress platform is that it enabled us to allow bloggers to contribute content directly into our core CMS. This started out as a feature for Metro employees but grew to encompass a much wider set of sources. Club Metro is now the most prominent and now contributes over 1M unique visitors a month. Having blogs content on the same domain placed amongst the rest of Metro’s content ensured we leveraged our existing algorithmic rankings. A single editorial workflow also helped keep the overheads low. The added bonus that most bloggers were already using WordPress and can write from anywhere helped us secure some top talent.

Club Metro Article

Club Metro Article

Hack 4: Facebook page as a major marketing channel

173,000 Facebook Page likes Feb ’13 to 562,000 Facebook Page Likes Feb ’14

Social referrals were another large contributor to growth. We focused on growing our Facebook page “Likes” as a content marketing strategy and this was very successful. We had always been careful to only send a small amount of our best posts out a day on our Facebook page. This had a solid base of users and growing this was a key goal for the last year. We employed many strategies including competitions with like gates and boosting posts. Competitions were effective in bringing in users but there were a lot of repeat entries. Varying the prize helped to minimise this and our email based CRM platform really helped to drive entries. The most cost effective way we found was boosting posts as friends of people who already liked Metro were shown a great piece of our content. They were then much more likely to then go on to like the page. As they were similar to people who already liked Metro they were very receptive and continued to engage with our content.

Metro Facebook Page

Metro Facebook Page

Hack 5: Made to share content

Weekly Social Visits to Metro

Weekly Social Visits to Metro (Jan’13 – Feb’14)

Ensuring that not just the content put out on the Facebook page was made to share also really helped grow our social referrals. When the content is written they set a success measure of 100 shares on an article and then set performance targets on achieving that on 25% of our content. This has allowed some interesting conversations around the areas of the site where that was less prevalent. More than anything it is a very easy test for all of the content creators to know what they should focus on. If it won’t hit that bar then find something else. A key growth hack we developed was the ability to show different headlines for social and search so we didn’t impact our search traffic.

SEO and Social Headlines on Made to Share Content

SEO and Social Headlines on Made to Share Content

Hack 6: Made to share UX

Share and follow functionality on Metro.co.uk after the Made to Share focus.

Share and follow functionality on Metro.co.uk after the Made to Share focus.

The other side of social was increasing the number of people sharing from the site. The development team had a made to share focus where we introduced much larger, clearer social buttons and reduced the number of clicks it took to share. This with the addition of a sticky sharing bar that floats on desktop has seen a large increase in the number of direct shares from our site. This seems to have also affected the amount of people copying and pasting links from the site from the subtle reinforcement due constantly present share cues. It would also seem that Facebook take direct site shares as a strong signal in their algorithm as we have continued to see growth from social.

Hack 7: If something feels wrong don’t give up on it

Referral from Natural Search (Dec'12 - Feb'14)

Referral from Natural Search (Google Fixed, July ’12)

After the redesign only 20% of our stories indexed in Google News had our pictures next to them. We spent months experimenting on different options before we finally managed to ask Google the right question in July ’13 so they could fix it for us. It turned out that they were still using the Webmaster Tools account we had been using before the migration which pointed to our old domain that included www. Not only did this change help our referrals from Google News but it gave us a major kick in all search referrals. It would have been much easier to give up on this earlier but relentlessly focusing on this until we solved it really paid dividends.

Hack 8: Let technology automate repetition

Metro Development Releases to Production

Metro Development Releases to Production (Nov’12 – Feb’14)

Automation of all of our development and test processes allowed us to release 4.5 times on average every day (apart from Fridays) for the past 12 months. This enabled short feedback cycles and decision making to happen at a much faster pace. We have five different environments that are used for testing before we push code live. This kept errors to a minimum and kept the feedback flowing. This environment of automated front end tests and frameworks was a major investment but has continued to pay dividends.

Hack 9: Ensure proximity of key people who are focusing on a goal

Single Goal: 700,000 Average Daily Mobile Visitors in September 2013

A single goal of growth allowed us to work together cross functionally and a focus on data and numbers ensured that feedback was alway digestible. Content, social and tech sitting together and working together enabled the good ideas to come to the top quicker and equally the bad ones get ignored. Equally focusing on data helped take emotion out of decision making which enabled data to win arguments. This sped up innovation and focus. In most cases we have done less but done what we have done better to achieve growth.

Hack 10: Get out of the way

Voticle: Are you a true Brit?

Voticle: Are you a true Brit?

Once people are working cross functionally together towards a goal then get out of the way and let them get on with it. In the past two months we have relaxed our process and now the content creators are working directly with the developers on new article formats to continue our growth. Out of this we have developed five new ways of displaying content from quizzes to lists and beyond.

Quizicle: How much of a Londoner are you.

Quizicle: How much of a Londoner are you.


None of the above would have been possible without the adoption of a lean mindset and the approach of build, measure, learn, iterate. It has been an amazing 12 months of growth at Metro and an great feeling to be part of a team that came up with and then executed a plan which delivered these results.

It was a pleasure to work with the below as well as many others on this journey.

Music to write this code to

SQL script for easy 301 redirect WordPress blog htaccess

I have just setup some 301 redirects in order to remove /development from the URL of my blog and wrote a handy piece of SQL to simplify 301 redirect WordPress blog htaccess. Once you have made the changes you can update your redirects /wp-admin/options-permalink.php. The reason I am redirecting to the guid which is the id based form is in case I change the slug in the future.

SELECT CONCAT('Redirect 301 ','/slugtoreplace/', post_name, ' ', guid)
  FROM wpdb.wp_posts 
 WHERE post_type = 'post'
   AND post_status = 'publish';

You can then post the output from that script into your .htaccess file and everything will redirect properly.

Music to write this code to

Claude VonStroke’s Essential Mix minus all of the Pete Tong guff in all of its bass heavy glory always gets me at the command line.

SQL script to restore WordPress backup localhost

I needed a simple SQL script to help me restore WordPress backup localhost to ensure that I didn’t have to go through the pain every time that I wanted to bring my data to my local server. This worked really well and all you need to do is search and replace on local.blog to the name of your localhost.

-- update site
UPDATE wpdb.wp_site
   SET domain = 'local.blog'
 WHERE id = 1;

  FROM wpdb.wp_site;

-- update blog details
UPDATE wpdb.wp_blogs
   SET domain = 'local.blog'
 WHERE blog_id = 1;

  FROM wpdb.wp_blogs;

-- update site meta
UPDATE wpdb.wp_sitemeta
   SET meta_value = 'http://local.blog'
 WHERE meta_key = 'siteurl';

UPDATE wpdb.wp_sitemeta
   SET meta_value = ''
 WHERE meta_key = 'dm_ipaddress';

  FROM wpdb.wp_sitemeta
 WHERE meta_key in ('siteurl','dm_ipaddress');

-- update options
UPDATE wpdb.wp_options
   SET option_value = 'http://local.blog'
 WHERE option_name in ('siteurl','home');

  FROM wpdb.wp_options
 WHERE option_name in ('siteurl','home');

-- update posts
UPDATE wpdb.wp_posts
   SET guid = REPLACE(guid,'blog.david-jensen.com','local.blog');

I have gone through each of the tables above that need updating, you could probably get away without updating all of these but I figure that as it is a script you might has well do it properly. I have also included the select statements afterwards to ensure that you can see the value has changed.

Music to write this code to

Erol Alkan classic is something super chilled to sip your tea to for the calm before the development storm once you have your local restored.

Communication and trust are the foundation of high performing teams

High performing teams can achieve unbelievable performance multipliers over sets of like minded individuals. I have spent the last few years working on building high performing teams and from this experience I have found the foundation of high performance is communication and trust.

It takes time

It is hard to get communication right within a team, even when they are sitting next to each other. Often it can take months rather than weeks to build up the right cadence of communication and understanding that are required to build trust. This process is even harder when you are also in the midst of creating a new company as it is only one of the many things that require focus. Luckily there are templates that help you hack these processes to make them happen quicker.

Agile mindset

Working with an agile mindset encourages regular feedback and can provide templates for the flow of information and cadence of communication. It also gives you the regular opportunity to tweak your process to ensure that it fits your environment in the form of retrospectives. Proximity is another clear hack and the closer that people sit the easiest it is to get them to communicate.


I have found that daily stand ups are a great way to hack communication within teams. I do daily stand ups with development teams and at least weekly stand ups with the business and stakeholders. These are best done around the agile wall and include new features as well as an overview of roadmaps. Regular feedback in the form of small regular conversations beats larger infrequent conversations as they allow for regular course correction. This helps prevent waste that comes from working on things that are not the most important for the business.

Consistency of language

The other benefit of regular communication is that both sides have the ability to learn the others language. Having a commonly understood language takes time and reduces the waste caused by miscommunication. Trust is much easier to lose than it is to build and in the majority of cases it is a miscommunication that leads to erosion of trust.

Find common ground

When people are under pressure to deliver it can be easy to blame something you don’t understand. There are many different cultures that make up an organisation and how you build a software system isn’t the same as how you create budgets. In order for cultures to understand each other you need to establish regular communication over common ground.

Have a clear vision

Common ground should be a clear vision based around a set of hard but achievable goals that everyone can affect. Clear goals enable decision making to be decentralised which helps to avoid one person blocking progress. Maintaining flow is essential to the creation of momentum which forms the baseline of high performance. Making your vision and current goals very visible should allow your team to see how the work that they are doing is affecting the key business metrics.


When teams are communicating well within a clear framework of success it allows everyone to focus on delivering value. The ability to influence business goals with regular feedback creates an ideal environment for high productivity. Communication is an often over looked discipline when forming teams and especially companies. It is the foundation of common understanding and this builds trust that will allow you take your performance from good to great.

Music to write this code to

Bonobo stepped up to the essential mix and the resulting two hours are outstanding.

How to install Varnish Cache on AWS Centos to speed up WordPress Apache

I have decided to give my server the final boost in my quest for ultimate cachability and install Varnish cache. It was a toss up between doing this and installing NGINX but I have a reasonably large set of rules in my .htaccess to prevent hackers and 301s so I figured the migration would be a bit more than the few hours I had to spare.

First install varnish.

sudo yum install varnish

Make sure that it comes back on startup.

chkconfig varnish on

Now edit the following file.

sudo vim /etc/sysconfig/varnish

The below is what I ended up with in my file thanks to this. The main difference from the original is the change of the VARNISH_STORAGE_TYPE to malloc which means that it is stored in memory rather than disk. I chose 128MB as my blog is pretty small and I wanted to have a bit of room to spare.

# Varnish Users

# Should we start varnishd at boot?  Set to "yes" to enable.

# Maximum number of open files (for ulimit -n)
# NFILES=131072

# Locked shared memory (for ulimit -l)
# Default log size is 82MB + header

# # Maximum number of threads (for ulimit -u)

# # Maximum size of corefile (for ulimit -c). Default in Fedora is 0

# Should probably change this

# Not setting VARNISH_LISTEN_ADDRESS makes Varnish listen on all IPs on this box
# (Both IPv4 and IPv6 if available). Set manually to override this.

# Telnet admin interface listen address and port

# Shared secret file for admin interface

# The minimum number of worker threads to start

 # The Maximum number of worker threads to start

# Idle timeout for worker threads

# Best option is malloc if you can. malloc will make use of swap space smartly if
# you have it and need it.

# Cache file size: in bytes, optionally using k / M / G / T suffix,
# or in percentage of available disk space using the % suffix.


# Default TTL used when the backend does not specify one

# DAEMON_OPTS is used by the init script.  If you add or remove options, make
# sure you update this section, too.
             -f ${VARNISH_VCL_CONF} \
             -t ${VARNISH_TTL} \
             -u ${VARNISH_RUN_USER} -g ${VARNISH_RUN_GROUP} \
             -S ${VARNISH_SECRET_FILE} \
             -s ${VARNISH_STORAGE}"

Then you need to edit your /etc/varnish/default.vcl the below is what I ended up with which allows you to see the WordPress Admin Bar but caches most other bits. It also takes invalidation requests from the Varnish WordPress Plugin which is the easiest way to keep your content fresh.

backend default {
  .host = "";
  .port = "8080";

acl purge {

sub vcl_recv {
  if (req.request == "BAN") {
    if(!client.ip ~ purge) {
      error 405 "Not allowed.";
    ban("req.url ~ "+req.url+" && req.http.host == "+req.http.host);
    error 200 "Banned.";

  if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "DELETE") {
    return (pipe);

  if (req.request != "GET" && req.request != "HEAD") {
    return (pass);

  # don't cache authenticated sessions
  if (req.http.Cookie && req.http.Cookie ~ "(wordpress_|PHPSESSID)") {

  # don't cache ajax requests
  if(req.http.X-Requested-With == "XMLHttpRequest" || req.url ~ "nocache" || req.url ~ "(control.php|wp-comments-post.php|wp-login.php|bb-login.php|bb-reset-password.php|register.php)") {
      return (pass);

  if (req.url ~ "wp-(login|admin)" || req.url ~ "preview=true") {
    return (pass);

  remove req.http.cookie;
  return (lookup);

sub vcl_fetch {
  if (beresp.status => 400) {
    set beresp.ttl = 0m;

  if (req.url ~ "wp-(login|admin)" || req.url ~ "preview=true") {
    return (hit_for_pass);

  set beresp.ttl = 24h;
  return (deliver);

You then need to update your Apache to listen to port 8080 as Varnish will be listening on port 80.

sudo vim /etc/httpd/conf/httpd.conf

Update Listen 80 to Listen 8080 and then restart Apache and Varnish and then you can run varnishstat to see the details of how your cache is performing.

sudo service httpd restart
sudo service varnish restart

I then installed the Varnish WordPress Plugin and then went to Dashboard -> Settings -> WP-Varnish and updated the following fields.

  • Varnish Administration IP Address
  • Varnish Administration Port
  • Varnish Administration Secret

The secret is a GUID which is stored in the below.

sudo vim /etc/varnish/secret

These are some really useful Varnish commands to see how your cache is performing or check out the docs for full details.

  • varnishtop: like top but for varnish.
  • varnishhist: histogram of request time
  • varnishstat: cache hits, resource consumption and many other data points
  • varnishlog: log of all requests made to the cache

You should then have a super charged server where most of the files are coming from memory rather than disk. The below sites were helpful along this journey.

Install Varnish HTTP Accelerator with WordPress
Speed up WordPress with Apache and Varnish
WordPress Varnish Cache Config / VCL

Music to write this code to

Makoto the drum and bass maestro does some house and techno which always gets me going at the command line.

Making change a habit is the best way to create an agile mindset

“You must be the change you wish to see in the world.”
Mahatma Gandhi

Making change a habit is a powerful way of not just creating but maintaining an agile mindset. In the fast paced world that we occupy an agile mindset has come to be valued over many other attributes of successful companies. Agile is a word that is often used without a true understanding of the mindset that underpins its values.


A mindset is “a set of assumptions, methods, or notations held by one or more people or groups of people that is so established that it creates a powerful incentive within these people or groups to continue to adopt or accept prior behaviors, choices, or tools”. Another way of looking at that is a set of habits which an individual/group perform similar outcomes to. Agile has its origins in software development with the Agile Manifesto which was signed in 2001 and outlines the mindset needed to create successful software. The group that signed the Agile Manifesto stated that they value:

  • Individuals and interactions over processes and tools
  • Working software over comprehensive documentation
  • Customer collaboration over contract negotiation
  • Responding to change over following a plan

How your brain works

In order to better understand how you create the set of habits that form a mindset it helps to understand how the brain works. There are two distinct systems in your brain used for thinking as described by Daniel Kahneman in his book “Thinking Fast, Thinking Slow“.

  • System 1: Fast, automatic, frequent, emotional, stereotypic, subconscious
  • System 2: Slow, effortful, infrequent, logical, calculating, conscious

System 1 is where you spend most of your time and could be described as the autopilot part of your brain. Basically this allows you to do complex things without large amounts of energy and effort. System 2 is what you use when you encounter a problem that System 1 doesn’t recognise and this requires a different deeper type of thought.

Most people spend the majority of their time using the System 1 part of their brain. However like most things System 2 becomes more effective the more you utilise it. If you can take something from System 2 and engrain it in System 1 then you are then able to spend the energy you save on solving new problems. It also becomes the default behaviour for how you respond to this type of situation.


Another way to describe this type behaviour is a habit, defined as “A settled or regular tendency or practice, esp. one that is hard to give up” or simply something which has been engrained in System 1. They can be created and/or modified and a great description of this process can be found in “The Power of Habit” a book by Charles Duhig. He defines a habit of consisting of the following:

  • A trigger
  • A routine (what you do to respond to the trigger)
  • A reward

The easiest way to change a habit is to understand what the trigger and reward are and to replace the routine in the middle with a newer improved version. There are many triggers that exist in the software development lifecycle such as being stuck on a problem or lack of detail in requirements definition. When you start out most people have different routines for dealing with these type of problems. The ideal place to start to discuss these triggers is in a retrospective. In this environment it is great to understand the differing approaches and choose the most effective one for the group to adopt. The reward for this is the great feeling of shipping quality code on a regular basis.

Change a habit

I have found that to change a habit it is always best to start small and find a habit that will be reasonably straightforward to improve. This allows people to understand the process and see the benefits of the approach. I have also found that small regular conversations are much more effective for changing habits than large infrequent conversations. It takes a while for the habit to form and change and being subtly reminded on a regular basis is great way to reinforce the correct behaviour.

In order to create a mindset for a team you need to gradually change their individual habits to be consistent. This is not an easy or quick task but one that delivers an amazing amount of long term benefit to both the individuals and the company/team that they work for. This is probably the single largest factor in turning people into high performing teams due to the efficiency gains and consistency of outputs. If they have made a habit of hacking habits then they are constantly improving and helping each other to do the same.


It has taken me a few years to create a habit of change at Metro and these are some of the practical things that I did to help that journey. Initially I set a very clear high level goal of releasing software every day. This required a lot of changes and I wanted to ensure people knew what success looked like. I then setup regular retrospectives to create a change dialog. I then gave everyone my word that I would be a willing participant of this journey into change and set a clear commitment to remove impediments. This mainly involved getting the business and stakeholders to change alongside our journey. Once this was setup we started small, started changing and are iterating as there is no such thing as perfect process.

Music to write this code to

Fake Blood has some serious habits around create digging and turning old breaks into amazingly crafted mixes.

How to speed up WordPress Apache using Batcache Multisite and Memcache on CentOS

I run a small WordPress network on an Amazon Micro Instance and this means that I need to stay on top of caching. Having had a look around at a lot of different options I decided that I would use Batcache and Memcache to provide me with in memory caching should one of my posts get popular. Batcache multisite capabilities means that I don’t have to stress about individual caching setups per site. Also Batcache is what WordPress.com uses to handle caching and you can utilise Memcache in a distributed way should you need to scale out from a single server.

Another reason I like it is that it is very non-invasive caching technique, I know that WP-Super-Cache and the way that it generates static files is probably a better fit for my blog as the load isn’t usually very concurrent. However that sounded quite straight forward and I like a command line challenge plus I have a few side projects that this might come in handy for. You also need to get this setup if you are ever deploying anything to WordPress.com which we utilise for our hosting at Metro.co.uk.

As I am running a network, I don’t want other users to have to worry about caching and its quirks. The nice thing about Batcache is that it doesn’t cache anything when you have cookies from the domain so anyone who is writing can see their changes immediately. I think this and its simplicity is probably the key advantage over some other methods of caching.

The other thing is that if you have root access to your server then it is reasonably straight forward and I thought that I would share how I did this here in case anyone else is interested.

First install all of the libraries that you need to run memcache and access it via PHP, it took me a few times to get this right so there might be a few libraries which are extra below.

sudo yum install memcached
sudo yum install php-pecl-memcache
sudo yum install zlib-devel
sudo yum install php-pear
sudo yum install php-devel
sudo yum install libmemcached-devel

This is the compiler I used for building

sudo yum install gcc

This will build the libraries that PHP needs to be able to access Memcache

sudo pecl install memcached

Now make sure that memcached will start on startup

sudo chkconfig memcached on

I also enabled memcache session handling but haven’t turned it on.

Enable memcache session handler support? [yes] : yes

I didn’t do as it suggested below as every time I did I got errors saying that it had already been registered.

You should add "extension=memcached.so" to php.ini

I had a look at the config and tried to lock it down to localhost only but it kept on throwing errors when I did so I left it, as all of my ports are locked down via the Amazon Firewall this shouldn’t be an issue.

sudo vim /etc/sysconfig/memcached

Now made sure that it would come back on startup.

sudo chkconfig memcached on

Now I followed these instructions for Batcache installation once I had downloaded it.

Upload advanced-cache.php to the /wp-content/ directory

Add this line the top of wp-config.php to activate Batcache:

define('WP_CACHE', true);

Tweak the options near the top of advanced-cache.php, I set mine to cache for 60 minutes after being hit twice in 10 minutes.

var $max_age =  3600; // Expire batcache items aged this many seconds (zero to disable batcache)
var $seconds =    600; // ...in this many seconds (zero to ignore this and use batcache immediately)

Optional Upload batcache.php to the /wp-content/plugins/ directory. As I ran Multisite I put this in the /wp-content/mu-plugins so it would be there for all of the sites in my network.

We have already installed the PECL memcached extension so you just need to add Ryan’s Memcached backend 2.0 to wp-content.

Test by reloading a page in your browser several times and then viewing the source once you have cleared any cookies from the domain you are hitting. Just above the closing tag in the head you should see some Batcache stats.

generated 524 seconds ago
generated in 0.374 seconds
served from batcache in 0.002 seconds
expires in 3076 seconds

Now feel free to tweak the settings around cache time to a period where you will reduce the load. Remember this is full page caching and you shouldn’t see it if you are logged in.

SEO Impact

The best part of installing Batcache has been the impact on SEO, below are my crawl stats from Google and you can see that the average time spent downloading a page flatlines around the beginning of December after installing Batcache. The really interesting thing is that the pages crawled per day went up at the same time. I think this is because Google now thinks that that server can handle a much greater load so sends it its bots on a more regular basis. Can’t say that this has had a massive impact on my SEO entries but it is a really good sign of the impact that you can have with a proper caching strategy.

Google Crawl Stats

Super fast and simple caching, if its good enough for WordPress.com its good enough for me. The below articles helped me along this journey.

Music to write this code to

Nothing beats repetitive beats to get down on the command line and Sasha playing at DC-10 for Circo Loco knocks that out of the park.