Inspiration from the web
As will sometimes happen, on a forum I frequent, an old thread from 2006 surfaced where a heated discussion about probability had raged. This was a 1000+ post monster on a modified Boy Girl Paradox. One of the side arguments concerned The Monty Hall problem, and while reading I thought it would be a great subject for an interview exercise. If you’re not familiar with the Monty Hall problem, click here for background. While this exercise doesn’t require any knowledge of math or probability, a side affect of completion is a very clear illustration the solution.
I like to code interview exercises myself to get an idea of timing and to make sure there are opportunities for interesting discussion, I was thrilled with the results of this one, although a little long to complete, it took me through several interesting decision points and led to some techniques outside the usual textbook rails boilerplate.
I have split this article up into multiple posts.
Part 1 – Introduction and solution overview
Part 2 – Writing the simulator
Part 3 – Non persisted ActiveRecord
Part 4 – Wizards or Multi-page forms
Part 5 – Refactoring, Mixins and Rails Concerns
The Interview Task
So without further preamble, here’s the task:
Given the controversy surrounding the Monty Hall problem, write a web application which will illustrate the probability solution. Here is an outline of the required functionality
- Allow users to run large numbers of simulations and display the results
- Users should be able to simulate 10, 1000, 10,000 and 100,000 games.
- Users should be able to change the game parameters and simulate the problem with 3, 4, 9 or 100 doors
- Allow users to interactively play the door portion of Lets Make A Deal
- Allow users to see statistics of all games played
- Deploy your solution to a 3rd party server (Amazon, Heroku, etc)
You have 90 minutes to complete the task and to discuss your solution.
Too Much Of A Good Thing
So the first thing to note, the exercise is way too long, it requires 2-3 hours to complete it comfortably, and unless you like to hold your interviewees hostage for a day, I would recommend splitting it up. Solving #1 or #2 plus #3 is probably about 90 minutes in an interview environment. You can easily discuss the solution another 30 minutes afterwards and for me, thats far more valuable interview time than going through some API questions on the whiteboard. Another alternative is to ask the candidate to solve 1 part, then refactor and solve the others remotely.
In addition, depending on the skill level of your candidate, the deployment task may be beyond what can be completed on site. I would modify it for all but the most senior engineers, or candidates who went out of their way on their resume to state how much they just love deploying to Amazon, to skip it and just look at the completed version locally. However, if you have the time then deployment is a great test. I would recommend warning the candidate ahead of time so that they have an account set up and everything they need to deploy ahead of time. This is one of those tasks where even with no element of surprise, you’ll discover a lot about a candidate in their ability to finish and deliver code into production. I’ve come across Senior Engineers who are intelligent, skilled and creative, but completely unable to deliver finished projects to production.
Finally, make sure that there is no ambiguity or distraction in the problem. Spend some time reviewing the background and discuss the mechanism of how a game works:
So for the interactive game question:
- Prompt the contestant to pick a door
- Eliminate one of the doors which do not contain the prize – random preferred
- Allow the contestant to either keep their original guess or switch to the other remaining door
- Show the result
For the many game simulation question
- Each game should randomly pick a starting guess
- Eliminate all but one other door which either contains the prize, or if the original guess was correct, will contain a goat.
- Show the result of sticking with the original guess and switching to the other door
- Aggregate the results
Make sure the candidate really understands these pieces, and depending on skill level, discuss different methods of coding them. Remember, if you’re not interviewing a candidate’s expertise in probability, but in creating a coherent application.
Designing a solution
Like all exercises, there are a thousand different ways to complete this one. I picked some options I found interesting to pursue, and made some decisions along the way which turned out to be good and bad.
If you’re reviewing an interview exercise there should be plenty to discuss, but there should also be some absolute no-thought boilerplate rails code which gives your candidate the chance to show that they can recognize and code tasks which fit easily into the MVC pattern and Rails application organization. The full exercise contains enough code for a candidate to demonstrate both and explain their decisions.
To TDD Or Not To TDD
I truly believe in the value of automated testing, I am an habitual tinkerer and refactorer, so the only way I can function without going crazy is to have good tests with significant coverage. However, I’m not a fanatical TDD proponent, I take a pragmatic view that someone who can delivery high quality code with tests can do so however they want.
Lets look at the motivation for ‘pure’ TDD – it forces you to write tests first, therefore your code will have 100% coverage, be exceptionally readable, and Unicorns will sing choral arrangements of Vespertine in your honor. I don’t feel compelled to behave that way – I like writing tests, I understand the benefits and want to write them effectively so that I can refactor, in short, I don’t need to be cajoled into eating my vegetables and the kind of engineers I look for will feel the same.
Personally, I like to sketch out solutions with a few classes, write some method signatures, then write the tests. I am far more productive sketching in classes than I am in sketching in tests. Additionally, I sometimes code a console application, particularly if there is an interesting algorithm or other functionality which will eventually form part of my model. For the Monty Hall Simulation, I had the guts of the application running on the console with tests within about 15 minutes. All that was left was to move it into a web application.
I also like to get some results quickly and see an application hang together, so I will write tests and code some pretty hideous designs, then refactor them into something more beautiful. This is something which can be iterated very quickly with effective tests.
A Quick Solution Overview
We have 2 separate tasks to complete, though they are going to share some of the same code. I decide to create a very standard Rails MVC to handle the simulating large numbers of games, call the model Simulation, stub out the controller and view directory. I am not a huge fan of scaffolding as it produces a lot of code I’m not going to use, so I am going with the Luddite technique of manually creating my classes.
For the simulation statistics, I will create some scopes to handle the queries and use the index action of Simulation to hang the controller and view portion.
The unique Monty Hall gaming code will live in the Simulation model, so the rest of this task is fairly boilerplate. I find myself trying to make sure solutions fit in a classic Rails application layout unless there’s a really good reason not to. Sticking to patterns generally helps eliminate code smells and can prevent wild meandering you may later regret.
For the interactive game, as I am not interested in logging individual results, I decide to use a non-persistent class as my model, hand roll my own wizard/multi-page form. This use case is very distinct from the Simulation model, so I will create a Game MVC to group the functionality together. In reviewing how the game works, I will need the logic from Simulation, so for now I will hideously couple my Game model with Simulation, and later I will move the code out to a Concern which will be mixed in.
A Few Gems
I have a handful of gems which are part of every application I start. I may add more later, but I am always pretty sure I will be using the following
- Factory Girl
- Minitest Reporters
- Bye Bug
- Web Console
- Better Errors
- High Voltage
- Annotate aka Annotate Models
Your mileage may vary, but I always figure those will be part of any solution I code.
Regarding Bootstrap, I am possibly the worst UI designer you could ever meet, but a few minutes investment in Bootstrap turns a hideous prototype into something which looks tolerable. I used to hand wire Bootstrap in, but I found Bootstrap-sass cut the time down from 10 minutes to 0, so for any prototyping I just use the gem. I’ve been coding since the mid 90’s and I’m not sure what would be less believable to my younger self: that it takes 10 minutes to plug in a beautiful pre-canned UI, or that I would find that too long and look for a way to do it quicker!
Good Decisions, Bad Decisions
Just one sentence can be quite misleading in what is simple and complex. I decide to use a non-persistent class as my model, hand roll my own wizard/multi-page form sounds like a fairly rational decision, to be honest I wanted to have some code of interest as I went through the exercise, but whether this was a good decision or not, I tend to think not. We’ll get into the details of this later, and it makes for some interesting points, but my consolation at the end is that with my test cases these are very simple things to change.
In Part 2 we’ll look at the simulator in more detail.