Launching our new DevChallenge

Here at TransferWise we like people who are not afraid of a good challenge. My colleague Tariq came up with the idea that instead of just showing a simple hidden message for the engineers in our HTML source or JS console, we should talk to them - ask them questions, challenge them. I instantly loved the idea and on the first Orange Wednesday (say what? I’ll tell you later) started working on a prototype. And now it is out there on transferwise.com, hopefully calling the hacker minded to solve all its puzzles!

Screenshot from TransferWise.com

I wanted the simplest prototype imaginable, but at the same time I wanted to make it hard to crack - not exposing any hints or data to the frontend so the devs will actually have to solve the tasks in order to proceed. This is why I decided to go with a simple express app in the backend and requirejs on the frontend. Created a simple object as the channel between the app and the user as it is not possible to read what a user types in the console but I wanted something very similar.

To have the challenges and the server app separate and to invite the community to play with and improve on our code I created a public repo for the server and a private one for the final app. This private repo mainly contains the challenges and some lines of code to make it work with https on the live site.

The working prototype was ready in a couple of hours. I added a basic challenge, named the browser part front.js and the backend counterpart back.js and set up requirejs to load the frontend plus wrote a simple endpoint for validating the response. The trickier part was the requirejs setup, but by tricky I mean half an hour tops. In this first version everything was in the same repo, testability was quite bad and data was mutable from the frontend. As I was adding new challenges I always hacked my own app by feeding it simple data and solving the tasks on that instead. So I needed a solution for the data and a separation of the server and the challenges.

First I added a session and stored the data in there, so now the data is generated randomly and never goes back from the frontend to the backend. Then moved the server to an npm module and refactored it to make it testable and simple.

The real problem was to make it run with CORS on https. As it turns out jQuery is quite anal about sending cookies between domains. So we needed Access-Control-Allow-Origin, but not with the mighty * solution. In the end the trick is that I figure out the origin domain of the request from the Origin or Referer headers and simply send it back as the only allowed origin. This works as a charm.

app.use(function(req, res, next) {
    let allowOrigin = req.header('Origin') || req.header('Referer') || "https://transferwise.com";

    res.header("Access-Control-Allow-Origin", getHostName(allowOrigin));
    res.header("Access-Control-Allow-Credentials", "true");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
});

Success!

After making it work with https and CORS it was quite easy to deploy to Heroku and integrate with our site, but there was a funny random collision between requirejs and one of our third party plugins on the live site on the function ‘define’. I am not proud of the solution, but it needed to be done fast… Typical excuse eh? The pressure was only in my mind, but it was enough. So let’s download requirejs source, rename the define inside it to something else and minify this version. This introduced false dependency is not something I’d like to keep in a production code, so this has to be solved in a more future proof and elegant way as soon as possible.

Writing challenges was quite easy with this setup, I also got a lot of help and feedback from colleagues. I think as a first version we have enough of those, as people keep solving them we might put new ones out. As the code is open source anyone can create challenges and host them on any server to challenge devs - I’d love to see someone using this framework. I promise I’ll go and try to solve your challenge if you host it!

To see how it works now, I created a simple demo app which works the same way as its big brother just contains only one challenge and is not saving winners email addresses.

Repos to check out:
https://github.com/transferwise/dev-challenge-server
https://github.com/transferwise/dev-challenge-example

Ah yes, and what are Orange Wednesdays? It is a dedicated half a day we have in London for new ideas and fixing stuff you always wanted to fix but it was never part of any project. Pretty cool stuff, thanks Keyvan for bringing it in!