We’ve recently begun to open up access to the StackShare API to community members to build cool apps using StackShare data. Alexis Santos decided to build a small trivia game that let’s you guess which tool is being described: http://stackshare.alexisjsantos.com (source code here).
We liked the game so much we decided to rebuild it and launch it as Tool Time.
Here’s how the game works:
We’ll show you 4 tools, and then the words developers use to describe one of them. Not the marketing fluff or something straight off their home page. Actual user feedback. You need to pick which tool is being described. The quicker you answer the more points you get. But get it wrong, and you’ll get points deducted.
Score high enough and you’ll be included in the all-time leaderboard with a link to your GitHub profile page.
We’d love your feedback- feature requests welcome!
How we built it
The game started as a two day project. Initially, we thought we could finish the “quiz app” (which is what we called it initially) in less than two days since the first prototype by Alexis only took less than a day to finish and we also had the source code for it. But we were slightly too ambitious— like many software development projects, we underestimated the complexity involved in building a scalable game app while providing a seamless experience for existing StackShare users and new ones. When it was all said and done, we ended up creating a great example of an app that uses many of the latest and greatest tools. This also marked the first application we’ve built using our own publicly consumable API.
Overview
The application is composed of three components— a database to authenticate users and save scores, Redis for caching application-specific temporary data, and a fast frontend built in React- all as part of a single page app that requires no reload. We serve up a JSON payload to React via Rails that acts as a proxy to our newly released API. This allows us to take advantage of all the caching we built for the API without worrying about scaling our production database for the main app.
User Session Handling
The biggest challenge was sharing our visitor’s browser sessions across the http://stackshare.io and http://tooltime.stackshare.io sites, what at first seemed like it should have been a trivial task. We wanted a low-effort and low-friction way to approximate a single-sign on service that allowed us to keep scores for users and maintain a leaderboard.
The problem was that we’d never served anything off a sub-domain before. And so our cookies were bound to the stackshare.io domain. As the majority of people do, we tried to look for a solution on StackOverflow. The best solution we found was the following:
Leanstack::Application.config.session_store :cookie_store, key: '_leanstack_session', domain: :all, tld_length: 2
While this seems straight forward, it quickly caused our application to crash. We now had 2 cookies: one for stackshare.io
and one for *.stackshare.io
. Which meant the browser became confused about which session to use. It got worse too because the http://tooltime.stackshare.io app was using a newer version of Rails, so was using the newer JSON cookie format At this moment, you might think, “Clear the session!” Yes! That was also our first thought. But, when we recomposed ourselves out of the rage of all the errors we saw in the terminal, we recognized the problem in doing so— there’s no way we can expect existing users to clear their session. There is no way to remove the cookies on the client’s browsers but we can change the session key from the backend and hence prevent session conflicts due to the previous incompatible cookies. So we took the opportunity to do some long overdue housekeeping since our rebrand, and changed the session key from _leanstack_session
to _stackshare_session
.
Generating Quizzes
The next step in creating a quiz game, of course, is generating quizzes. Since quizzes have a very short lifespan and each quiz can be thrown away without too much consequence, we decided to use Redis as a temporary data store. Every time we generate a quiz, we randomly choose one of the options as the answer and store it in Redis. This way, we can build a dedicated endpoint for submission without exposing any “sensitive” data in the frontend (unlike all of the BuzzFeed Quizzes). The right answer is returned as JSON and expired every time a user submits to prevent Hackers like ourselves from inspecting the traffic and pinging the endpoint externally. Thanks to Redis, we now have a fast and cheap temporary data store.
Keeping Score
The most difficult thing we faced was tying a user’s score from before sign in to after sign in because the session of the user changes once he or she signs in. The main login happens on the main site and it does not share the Redis cache with the quiz app and hence does not have access to the scores stored in the quiz app. Initially, we thought about passing in the current score of the user with the sign in but that introduced the problem of potentially malicious users. Assuming the worst case scenario, we tried to find a solution that is more secure. Since the score is stored in Redis using the session_id
as the key, we decided to pass back the session_id to a remote sign in url and have the main app pass it back to our callback URL in the quiz upon redirect. This way, we can fetch the data from Redis and persist it in the Postgres database attached to the main app and all subsequent scores will then be persisted directly into Postgres.
Frontend and React
With the backend problem solved, we then had to focus on providing the users with a beautiful and seamless UI/UX; this is a game after all. We wanted to create a single page app that seamlessly integrates with our backend but also makes it easy to integrate custom UI components. React was our first choice for the front end because we knew we wanted it to be a single page app and that there wouldn’t be too much logic outside of feeding the front-end data from our API. The StackShare main app has been quietly converted from traditional HAML into a number of React components that power different parts of the site. The easy interface of React and its seamless integration with other JavaScript libraries makes it pleasant to work with in comparison to something with a greater learning curve like Angular or Backbone. In our application, we display four tools as one question and React allows us to reuse the same tool card components and easily manage the state and events. For example, as soon as a user clicks a tool, we must set the state for submitted equal true
to prevent the user from attempting to submit again. React allows us to easily maintain the component behaviour so we can focus on designing a better experience for users.
Final Touches/Scalability
In order to make sure our application works well under stress, we decided to serve assets via CloudFront and load test the app using Loader.io. To cut down response time even more, we decided to fetch our tools asynchronously in the Gem we wrote for the StackShare API (currently in private beta) using threading. We’re reasonably confident that the new app can handle any potential traffic spikes. This is also a good opportunity for our team to find out how reliable our API performs in production before we release it publicly.
Summary
A two-day one person project, unexpectedly, evolved into a 1.5 week, all-hands on deck project; however, it allowed all of us to play with and understand all the different components of our stack. In addition to having the chance to play with a few of the latest tools, we also used this fun “little” game as an opportunity to bond as a team and refine a best set of practices in launching and scaling new services and products. We hope you enjoy the game that we have created for developers by developers and we look forward to hearing your feedback, congratulations, and criticisms ;)
P.S. Best for last, here’s the stack behind Tool Time: http://stackshare.io/stackshare/tool-time
-Tool Time Tim