March 19th, 2012

Hacker Time – the first 3 months

Over 3 months ago we started the “Hacker Time” initiative (see here) and now it is time for a recap what’s happened so far.
From the outset, people outside of the engineering department were very forthcoming in suggesting ideas for Hacker Time projects. While it was great to see so much interest, the essence of Hacker Time is that engineers create the projects that they’re personally motivated to develop. (It’s absolutely key to keep things separate from the general backlog!).
Once that clicked, our engineers started initiating and working on their passion projects. Hacker Time has found momentum; currently around 50% of our engineers (who have been in the company longer than 6 months) have a project they are currently working on. These projects range from doing remote online courses to participating in competitions to cool hacks (which sometimes start at “Hack Days” and are then continued within Hacker Time).
Some of the highlights are:

  • Instasound lets you record your sounds, apply a range of filters in real time, and instantly share to SoundCloud – video
  • Large Hadron Migrator – a tool to migrate database tables online without downtime
  • HyperSpec – HyperSpec provides a Ruby DSL for testing HTTP APIs from the “outside”
  • Story Wheel lets you record a story around your Instagram pictures and share it on the web as a nostalgic slideshow.
  • Massive Site Map – a ruby gem to build painfree google sitemaps for webpages with millions of pages. Differential updates keep generation time short and reduces load on DB.
  • KDD Cup 2012 – a team of 5 SoundCloud engineers is participating
  • Some engineers are taking online courses from Stanford, e.g. the machine learning online course

We’ve decided not to track the aggregate hours spent on Hacker Time projects, but to leave it to the teams to organize. It’s an approach that’s working well, because it reinforces the spontaneity and informality that this kind of initiative needs in order to be credible.

So what about the 50% of engineers that haven’t yet worked on their own Hacker Time project? Well, no big surprises here, there are two main reasons: Either they feel they have don’t have enough time or they haven’t found a topic yet.

Overall we’ve seen the initial concept successfully initiated and it’s begun to get into full swing. We’re really pleased with the output so far and will have more updates soon – some engineers will present their projects in more detail!

Alexander Grosse
March 4th, 2012

How we do… Retrospectives

SoundCloud started its agile journey with Scrum and eventually moved to an approach based on Kanban (more on that in one of the next blog posts). Regardless of the methodology we follow, though, we strongly believe that the most important tools an agile team can use to practice continuous improvement are retrospectives.
You might ask why we consider the retrospective to be the most important one? We think it is key to constantly learn and to improve based on our experiences. The best way to achieve this is to look back at our work in regular intervals and to give every team member the opportunity to give input on the past work – and to make sure we have actually improved something. This is done by reviewing action items from the last retrospective as the first step.

What kind of retrospectives?

The most frequent retrospective meeting is done after each iteration – in our context this means every two weeks. Every three months we do a big retrospective meeting with all of the engineers in one room to find common patterns in our development department. One major result of these big retrospectives is our “hacker time” project – see here for more details.
The length of the retrospective depends on the size of the team and the time between retrospectives. We usually schedule 30 minutes for the short one after each iteration and 90 minutes for big ones.

How is the retrospective done?

Pre-requisites:

You need only a few things to do a good retrospective – you can even do one only with Google Docs – but it works best with the following things:

  1. Post-its in 3 different colors (optimal is green, red, yellow)
  2. Enough pens so everybody has one – rather use big pens than ball pens so the content is short and everybody in the room can read it
  3. A big wall to put the post-its on, a whiteboard will do
  4. And someone to facilitate the retrospective, in Scrum it should be the Scrum Master. Really experienced teams do not necessarily need a facilitator. The facilitator keeps track of the time and leads the grouping of items.

The procedure

1) You put three columns on the wall with the title: less – same – more. Ideally you label each columns with one of the three differently colored post-it’s (red= less, green=same, yellow=more).

So, all items put under “less” are things which either should be done less or should be stopped completely.
“Same” is obvious, and “more” are items which should be done more or should be started.

Why? This scheme avoids the bad/good categories often used and allows an open communication where putting a sticker somewhere does not mean something is “bad” – it allows a more nuanced conversation.

2) Collecting items: everybody gets around 5-10 minutes to write down their points. During that time no discussions are allowed, it is only about collecting items.

Why? Everybody can give their input, often some people tend to dominate a discussion and the other people are not heard as much. This approach enables everybody to give their input.

People are free to put post-it’s on the wall (on the three columns) anytime. Latest after 10 minutes or when nobody is writing anymore the next phase starts.

3) Group items

The facilitator starts grouping items into clusters to find common themes. Usually there always are topics which a lot of people mention. Every item is read out to the group and if everybody agrees put to a cluster.

4) Find good names for the clusters

This is the final test if the grouping made sense – if you cannot agree on a name the cluster has too many different items and has to be split.

5) Vote

To identify the most pressing issues (number of post-its in a cluster is also a pretty meaningful indicator) everybody gets three votes and can give one vote to three different clusters. The top three items should be addressed during the next iteration.

6) Discuss

If there is time left start discussing the clusters starting with the one which got the most votes. Try to get action items out of it.

7) Action-items

A retrospective tends to be useless if it is “just talk” and no actions follow. So the key ending of a retrospective is to to collect action-items to work on at least the top voted clusters. The next retrospective starts then with a review of the action-items from the last retrospective.

We have been doing it that way for the last 8 months with pretty good results. There are around 8 engineering teams at SoundCloud and 45 engineers overall. The key challenge is here to continue doing retrospectives and to follow up on the action items.

Alexander Grosse
December 9th, 2011

Stop! Hacker Time.

At SoundCloud we like to invent new ideas. But we’re not adverse to implementing really great tried and tested ideas like the 20% time concept made famous by Google.

We’re calling it Hacker Time. We’re still very much in start-up mode so we’re keen to nurture the spirit of hacking. We’ve been testing out Hacker Time for a few weeks now and we’re excited about its potential, from industry-changing initiatives like “Are we playing yet” to unusual passion projects like the “Owl Octave”.

Why we’re doing it

When I arrived at SoundCloud I gathered all the engineers and developers in a room and asked them what they wanted more of. This was one of the top requests:

Like every other fast-paced tech company out there, the everyday demands of development leave little time for pet projects or invention outside of the roadmap. But we shouldn’t let great ideas slip away: it’s wasteful of talent and it’s frustrating for developers themselves. Hacker Time is an attempt to keep both the ideas and the employees focused on making the product even better.

I don’t think there’s a single developer at SoundCloud who isn’t a sound creator. Or at least they quickly become one once they’ve joined. We’re a company of electronic music producers, acoustic musicians, field recordists and social sound fanatics. Of course we’re developing the product for 9 million people, but we’re also developing something we want to use ourselves.

Ironically we’re not trying to invent anything new by initiating Hacker Time. Aside from Google, we’ve been watching the Atlassian experiment and are taking a similar approach; we’ll start with a simple set of rules and adapt to what works best for us. Like Atlassian, we’ll be blogging about our experiences too.

Which projects are engineers allowed to work on?

We’ve compiled a list which is not meant to be restrictive but rather should be a guideline:

  • Pet features/improvements that never made it onto the roadmap
  • Apps based on the Soundcloud API
  • Hack Days
  • Anything for SoundCloudLabs.com
  • “this always annoyed me” bug-fixes or architectural improvements
  • Integration of some technology-du-jour with Soundcloud
  • Contributing to OSS used at Soundcloud
  • Other cool projects using SoundCloud
  • Conferences
  • Writing Blog Posts, Technical Articles

How will time be allocated?

The big decision is how to allocate time to these projects. We don’t want to cannibalize valuable product development time but we do want to give people as much freedom as possible,

There are lots of approaches out there – from reserving one day a week, accumulating time to set aside whole weeks or handling that time like vacation. We’ve decided to put the decision-making in the hands of each team, allowing them to allocate Hacker Time according to each team’s workstyle. It’s an experiment, and something we’ll be reviewing in the early stages.

Demo it!

We’re pretty disciplined about demoing work to the whole company. Hacker Time projects will be included in the demos (which happen every 2 weeks at SoundCloud), giving developers a chance to showcase their projects and sparking interest in their hacks from everyone in the organization.

Watch this blog for further reports!

Many thanks to

Atlassian for blogging about their experiences

Simon Stewart from Google
Jim Webber from Neo4J
Jan Lehnhardt from Couchbase
Stefan Roock from it-agile

for sharing their experiences with us.

Alexander Grosse
November 21st, 2011

Front-end JavaScript bug tracking

Proper and effective error tracking is a common issue for front-end JavaScript code compared to back-end environments.

We felt this pain as well and experimented with different solutions over the past months on the SoundCloud Mobile site.

Analytics

The first approach we had was to track errors with Google Analytics. Their library permits to fire custom events and whenever an ajax error would occur, we would log it.

The biggest benefit of this tool is to monitor the stability of the site and its evolution in longer periods as you can easily go back a few weeks or months to see which events were triggered. Also, it is easy to implement – almost a one-liner!

The drawback, at least for Google Analytics, is that this tool is not meant to track bugs. There is no way to add custom data to these events to get more insight about why and how an error happened, it also doesn’t work in real-time, and you obviously want that when you debug.

So we kept Analytics in place for a long-term view, but took a look at other options for real-time and in-depth tracking.

Airbrake

In our pursuit of getting more insight, we decided to take a look at Airbrake because we were already using it to track back-end errors on our main site.

Our mobile site runs on Node.js, the first thing we did was to integrate an existing plugin for it to handle error tracking on the back-end as well.

Looking a little further we found a front-end notifier, which would catch errors that would fire on window.onerror, but there was no way to report any custom errors.

We decided to take a day to hack this on our own since their API is public and easy to implement.

The benefits of Airbrake were instant. We could see what triggered which error, how, why, in which context, which browser, etc… in real-time!


It also counts errors, which can help you prioritize and include fixes in your roadmap.

However, the lack of filtering, grouping and custom sorting made it difficult to work with. There was also no sense of time or progress, as everything just gets dumped into a single list ordered by time. We needed something a little better than that.

BugSense

That’s when our Android team showed us their BugSense implementation.
BugSense seemed to address all of these issues we had with Airbrake: grouping is more effective, searching and filtering is possible, charts of errors are drawn as well.

There is one more benefit over Airbrake… JSON. No need to convert objects to XML strings anymore!

If you are interested in our BugSense notifier you can find the source on github.

Conclusion

There is still a lot of work needed to make front-end JS debugging as easy as it is for regular back-end environments.
For example, stack traces today aren’t that useful, because of anonymous objects and minified code, but hopefully browser vendors will tackle these issues soon. Maybe Source Maps could be the first milestone in this quest.

At SoundCloud, we will continue to use a combination of these tools because of the different strengths outlined above, but there are also other tools we didn’t try out yet like getexceptional or errorception. If you have tried these, or if you have any suggestion on this subject we’d like to get your feedback in the comments below.

Happy debugging!

Yves
November 9th, 2011

SoundCloud launches the HTML5 Audio Improvement Initiative

We at SoundCloud want to build the best sound player for the web, and we want to do that using the Open Web standards. While working on the native audio features on our mobile site and new widgets, or even as an experiment on the main site, we have discovered that the HTML5 Audio standard is not equally well implemented across all modern browsers and some decisions can be made that would benefit the web audio users and web developers alike. Soundcloud launches the “Are We Playing Yet?” project, which aims to raise the awareness about the state of HTML5 Audio implementations in the web browsers.

AreWePlayingYet? 2014 A pragmatic HTML5 Audio test suite

We have decided to help the parties involved and collect the issues in one place, document them, provide the code and add interactive tests that will show the implementation progress. We understand how the software development works, and that a few iterations are needed until something is fully done. We hope ”Are We Playing Yet?” can function as a handy development and quality monitoring tool.

Issues - soundcloud/areweplayingyet - GitHub

“Are We Playing Yet?” was started by SoundCloud but it’s open to all companies and developers who care about the state of HTML5 audio and want to build applications based on this Web standard. You can get the project source on GitHub, contribute tests and fixes via the pull requests or Issue Tracker, and connect to the people involved via @areweplayingyet on Twitter.

matas
October 27th, 2011

Velocity Europe Birds of a Feather Meetup

With excitement building for the Velocity Conference in Berlin, we are happy to announce a pre-event meet up on Monday, November 7 at the Betahaus in Berlin.

The event will be a great opportunity for visitors of the Velocity conference to beat the jet lag with a day of networking and relaxed sessions and talks. To get everyone in the Berlin-state-of-mind, the evening will be capped with a party.

Berlin has quickly established itself as one of Europe’s main technology hubs. There are a lot of web companies in Berlin which are part of a constantly growing and vibrant local web community. The location for the BoF, Betahaus, is a shared-office innovation-inducing place that is used by small and large startups as well as established organisations.

Participation is free for Velocity Europe attendees. Non-Velocity attendees can pay 50 Euros to take part in the BoF event. The evening party is open to all and does not require registration.

The pre-event will be organized as an open space or unconference with on-spot planning of the sessions. The event is limited to 150 participants who must register in advance. If the event is not sold out in advance, participants can show up at the door.

At the Betahaus, we have 2 larger rooms and 4 smaller meeting rooms. We will hold presentations in the larger rooms and hold discussions in the meeting rooms. The meeting rooms for the discussion can hold up to 10 people. Anyone interested in speaking at the event can contact Eliot (eliot(at)soundcloud.com)  to submit a discussion topic.

The day will close with an open party . Event participants will enjoy a dinner buffet & free drinks at the after-event party.

You can register for the event here: http://veubof2011.eventbrite.co.uk/ (free for Velocity attendees / 50 Euros at the door for non-Velocity attendees)

Outline agenda (subject to change):

  • 13:00: Arrive at Betahaus
  • 14:00: Event starts with welcoming remarks and the session planning
  • 15:15 – 16:15: Sessions 1 & 2, each 30 minutes
  • 16:15 – 16:45: Coffee break
  • 16:45 – 17:45: Sessions 3 & 4, each 30 minutes
  • 17:45 – 18:15: Wrap up
  • 18:30: Dinner buffet for event participants
  • 19:30: Open party

Event location:
Betahaus
Prinzessinnenstraße 19-20
10969 Berlin
Betahaus on Google Maps

 

 

Eliot
October 14th, 2011

SoundCloud Signs Apache Corporate Contributor License Agreement

We just signed the corporate contributor license agreement (CCLA).
SoundCloud always was big on open source – we nearly exclusively use open source software in our company and use a lot of Apache projects like Hadoop, Solr, Flume, Zookeeper and Cassandra on our large scale production site.

As SoundCloud is using a lot of Apache projects and started to contribute to project we decided to sign the CCLA and enable all our developers to contribute to Apache projects even during work time if that project is used by SoundCloud.

The first project we will commit code to is Flume, we hope there are several more coming.

Apache, keep up the great work and we will support you wherever possible!

Alexander Grosse
September 12th, 2011

Mobile: Unit Testing

When we started the Mobile project early 2011, unit testing JavaScript was one of the goals to tackle on the technical side. The history of custom JavaScript code at SoundCloud up until then rarely included unit tests, so providing references and the necessary ground research was important for both the project at hand as well as for other projects at SoundCloud.

This articles aims to provide an overview of the tools we use, what worked well and what we need to improve.

Tools

When we started the Mobile project, there were just two developers on the team, Matas and Jörn. With Jörn already maintaining and supporting QUnit for three years, this particular choice was an easy one. If you haven’t yet heard of it: Among available unit testing frameworks, QUnit is among the most popular ones. There’s a comprehensive tutorial over at ScriptJunkie.

As we were building an API client in the browser, mocking API requests was really important for us. We didn’t want to depend on the API being available, both to be able to work offline and to not depend on data that changes all the time. At the start of the project, jQuery 1.5 and its ajax extension points like custom transports weren’t available yet, so we went with mockjax, a library adding mocking on top of jQuery’s ajax module.

To run tests in continuous integration systems (at SoundCloud, on Jenkins), we looked at quite a lot of options. Jörn has some slides that give an overview of that research. Other teams at SoundCloud use Selenium, which wasn’t an option for us due to the lack of support for Chrome or Safari (which is still a work in progress). In the end we went with PhantomJS. PhantomJS is built on top of Qt-WebKit, provides a reasonable browser-like environment and enough API to run our unit tests and report back results.

We considered using TestSwarm to distribute running of our unit tests to regular desktop browsers as well as mobile devices. The lack of a Jenkins-TestSwarm plugin (now actually available) as well as tools for managing VMs, browsers, simulators and emulators (or even managing mobile devices) was enough of a hurdle that we skipped this. Until we get this in place, we won’t know how many bugs we could have catched earlier with this additional setup.

The Good

QUnit does a pretty good job. The few small issues we encountered were swiftly fixed upstream. We ended up customizing the module-method quite heavily, mostly to integrate Mockjax. Overall, Mockjax also did a pretty good job, once we figured out a pattern that worked for us. Here’s a typical module-call for testing Backbone Views and Models that fetch their data from the API:

module("user", {
  "/users/183/tracks": "/fixtures/forss-tracks.json",
  "/users/183/playlists": "/fixtures/forss-playlists.json",
  "/users/183/favorites": "/fixtures/forss-favorites.json",
  "/users/183/groups": "/fixtures/forss-groups.json",
});

We still call the module-method with the module-name as the first argument. The second argument can contain setup- and teardown-properties, just like QUnit expects it. In addition, we pass url-mock pairs, which are passed on to $.mockjax. In addition to those, we define a catch-all to make sure that no test ever ends up calling the actual API. And we have a global timeout for each test to ensure a broken async test never prevents the suite from finishing.

var testTimeout;
module = function(name, mocks) {
  QUnit.module(name, {
    setup: function() {
      if (mocks) {
        if (mocks.setup) {
          mocks.setup.apply(this, arguments);
        }
        $.each(mocks, function(url, mock) {
          if (/setup|teardown/.test(url)) {
            return;
          }
          if ( $.type(mock) === "string" ){
            $.mockjax({
              url: "/_api" + url,
              proxy: mock,
              responseTime: 1
            });
          } else {
            $.mockjax($.extend(mock,{url: "/_api" + url}));
          }
        });
      }
      $.mockjax({
        url: "/_api*",
        responseTime: 1,
        response: function(obj){
          var message = "Mockjax caught unmocked API call for url: " + obj.url
          if (obj.modelType) {
            message += ", from component " + obj.modelType;
          }
          ok( false, message );
        }
      });

      testTimeout = setTimeout(function() {
        equal( true, false, "test timeout (5s)" );
        // could involve multiple stop calls, reset
        QUnit.config.semaphore = 1;
        start();
      }, 5000);
    },
    teardown: function() {
      clearTimeout(testTimeout);
      $.mockjaxClear();
      if (mocks && mocks.teardown) {
        mocks.teardown.apply(this, arguments);
      }
    }
  });
};

The problem with this design was the lack of a $.mockjaxClear(url) method – you can’t remove an existing handler or replace it (mockjaxClear(index) is supported, but didn’t help us). We needed that to test error conditions, for example, when the API returned a 404 when asking if a particular track was a favorite of a user. In some cases, we could just mix it with other mocks. In other cases, we grouped these tests into a separate module-call (with the same name):

module("user", {
  "/users/183/playlists": {
    responseStatus: 500,
    responseText: "servererror",
    responseTime: 1
  }
});

With that, we did the regular tests in one place, the error conditions in the other.

The Bad

An interesting QUnit feature, inspired by Kent Beck’s work on JUnit MAX, is its built-in reordering. It basically records the results of one test run in sessionStorage, then looks at those results during the next run. If a test failed before, its scheduled to run first. All that happens without changing the order of the result output. If it works, you can get the relevant test results much faster then for regular sequential runs, as its likely that tests that failed before will fail again, while passing tests are a lot less likely to start failing.

The problem with that reordering for us was that with all the asynchronous tests in our suite, sometimes tests would have side effects on other tests. As long as they ran in a fixed order, those effects weren’t noticeable. Instead of addressing the actual side effects, we ended up disabling the reordering. Its on the pile of chores to still address.

Overall, the unit tests did a good job, though its not quite clear how much value they actually provided. Most bug reports are about visual issues, sometimes small glitches, often enough device specific issues. As a mobile web developer, Android, or Andy as we started to call it, becomes kind of an IE6. It gets updated only with the OS, the OS isn’t updated, so we’re stuck with this browser that was okay a year ago, but is a real pain today. On Android 2.1, you even have the same issue as on IE6: HTML5 elements like ‘header’ or ‘article’ aren’t styled. At least on IE6, there’s a workaround…

Anyway, the other category of bugs were reported much less frequently, and unit testing didn’t help there either. We learned that client-side error logging is extremely valuable. Tools like Airbrake and Bugsense still have a long way to go, but writing a single-page web application without logging of client side errors means you never know about the thousands of errors your users get to see. Expect another post on that topic.

The Ugly

As long as mockjax did its job, we were happy with it. When it didn’t, we had to look at the source, and we weren’t happy anymore. The whole thing is quite a mess and in dire need of some good refactorings. Still, in terms of features, alternatives like jQuery 1.5 custom transports or sinon.js just aren’t on par, so we stuck with mockjax.

What we now mostly gave up on is PhantomJS. The Jenkins-job that ran our QUnit tests using PhantomJS is currently disabled, as it kept failing for months. We spent overall several days trying to find the source of the one failing test, giving up at the end. We still don’t know why it was failing, and there were several hurdles that made it difficult to debug:

  • It failed only on our Jenkins server. Running the tests locally, using the same PhantomJS version, worked fine. The difference was the enviroment, with mostly OSX running on developer machines, but Debian Lenny on the Jenkins box. Sure, that’s a problem, but the point of the tool is to provide a browser-like enviroment, it shouldn’t matter what system its running on.
  • We were stuck with PhantomJS 1.1, even after 1.2.x was out for several months. While we could adapt to the completely backwards incompatible API changes from 1.1 to 1.2, we didn’t find any way around PhantomJS just crashing on our testsuite, with no useful output. If you’re interested, you can find the debugging process somewhat documented on this Google Groups thread. Even debugging with gdb proved to be a waste of time. The unhelpfulness of PhantomJS when failing to load a page is stunning.

So as nice as PhantomJS is, the combination of not being able to upgrade and not being able to fix the existing build forced us to abandon it. TestSwarm is a lot more interesting now with the existing Jenkins plugin. And with Chrome support upcoming in Selenium, that is an attractive short term solution as well.

Epilog

As you can see, this story isn’t over yet. It seems to share a common theme with other developer tools, be that editors, bug tracking or testing tools: most of them do their job, but we aren’t satisfied with any of them.

What are your experiences? What tools would you like to see improved, replaced or invented?

Jörn
September 2nd, 2011

Hack Your Way to Berlin

Berlin is playing host to the oh-so-sold-out JSConf.eu conference happening October 1-2. Some members of the SoundCloud engineering crew well be on-site and we thought it would be great if you could come too.

We have an extra ticket for the JSConf.eu conference that we are going to raffle away in a hack competition. The rules of engagement are simple: use this podcast on the periodic table from Roman Mars and build a page that presents it in an awesome way. Be it with a very creative playback method, aggregating content around the sound or building the nicest design we’ve ever seen. The only constraints are that you use JavaScript and that the page is served from GitHub Pages. We want to see the most interesting and incredible page that you can build to play this track.

The page with the most creativity and “pop” will win the JSConf.eu ticket. We will get you from where you are in Europe to Berlin for the conference and find a nice place for you to sleep.

Send your entry to eliot@soundcloud.com by 23:59:59 Central European Time on September 18.

http://jsconf.eu/2011/

Contest requirements:
- You reside in Europe
- You are at least 18 years old
- You are allowed to travel to Germany
- You want to come to the JSConf.eu conference

tl;dr:
- Build awesome page with JavaScript highlighting this track
- Deploy to GitHub Pages
- Send a link to your page & repo to eliot@soundcloud.com until September 18
- Win JSConf.eu ticket, flight & accommodation for 3 nights

Judging will be done by SoundCloud staff by September 22. Have questions? Send them to eliot@soundcloud.com

Eliot
August 28th, 2011

SoundCloud Hits the Road

A quick virtual heads-up for developers, hackers and coding enthusiasts in the Berlin-area: SoundCloud is very happy to support the upcoming Hack and Tell event at the C-base bar on Tuesday, August 30. The Hack and Tell is a monthly meet up where participants have the chance to get on stage for five minutes present their personal or professional projects. For those not presenting, it’s a great evening to mix it up with Berlin-based hackers, meet some members of the SoundCloud development team and eat copious amounts of pizza. You can get a full rundown of the event here.

Berlin’s too far or you don’t like pizza? Here are a handful of the coming non-pizza-centric events where you can catch SoundCloud engineers offline:

Vision Sound Music
London, September 2-4

SF MusicTech
San Francisco, September 12

Mobile TechCon
Mainz, Germany, September 12-14

Golden Gate Ruby Conference
San Francisco, September 16-17

JSConf.eu
Berlin, October 1-2

Goto Aarhus
Aarhus, Denmark, October 13-14

Paris Web
Paris, October 13-15

Velocity
Berlin, November 8-9

Eliot