Welcome! We're excited you've decided to talk with us about a position on our engineering team.
The purpose of this exercise is so we can have a conversation about your technical background and abilities. We think that asking you to code on a whiteboard during an interview isn't a great way to have a conversation. And even if we sit down and pair during an interview it's a higher pressure situation than it could be.
Instead we ask that you read these instructions and do at most a few hours of work, on your time, to complete the exercise. During the interview we'll talk about decisions you've made, the resulting application, and how you might change it given different circumstances.
Below are two sections:
- Instructions: the problem we'd like you to solve along with expectations we have about your solution.
- Logistics: constraints around the problem, and how we'd like you to communicate your solution to us
We're starting a new application and we need to store students! We'd like to be able to do the following with the students:
- Create a student
- Retrieve a particular student by unique identifier
- Search students in the system
1. Format
The application should accept and produce JSON with appropriate content types.
2. Data
The student record has the following fields:
- An
id
that uniquely identifies the student. This will be provided by the server when the student is created and must not be changed after creation. - Either an
email
or ausername
must be non-blank, and whichever (or both) are defined the value must be unique within that field. Additionally, theemail
field should contain a superficially valid email. - A
first_name
andlast_name
; thelast_name
is required to be non-blank. - A
display_name
, which if not defined at creation should be the first name and last names joined with a space. - The
created_at
datetime when the student was added to the system, which should be assigned by the system when the student is created. It should be formatted 'YYYY-MM-DD HH:mm:ss' -- for example '2016-11-08 22:18:03' for 'November 8, 2016 at 10:18:03 PM'. - The
started_at
date of the student started at an institution; if not specified it should be the date the student was added to the system. It should be formatted 'YYYY-MM-DD' -- for example, '2016-11-08' for 'November 8, 2016'.
3. Search
The students may be searched by the following fields:
name
(which is a partial match against any of the first name, last name, and display name)username
(partial match)email
(partial match)started_after
(date formattedYYYY-MM-DD
that will return students who started on or after a particular date)
If multiple fields provided any returned records must match all of them -- that
is, you should treat them as an AND
. For example, the following query would
find students with 'Jen' in their name who started after 'November 15, 2016':
GET /students?name=Jen&started_after=2016-11-15
4. Routes
The routes you should use are:
- Create a student:
POST /students
- Search students:
GET /students
- Retrieve a student:
GET /students/{id}
(where{id}
is the value assigned by the server) - Health check:
GET /service/health
should return a successful HTTP status
5. Other thoughts
During our discussion we'll talk about your code and decisions you've made. We may also challenge assumptions in the problem, or add requirements. For example:
- How might student data be changed?
- What if we wanted to create many students at once?
- What if we wanted to assign each student to a school? More than one school?
- What if we wanted to track whether the student has logged in?
1. Language
Please use Perl to solve this problem. You may use modules from CPAN in your solution, but be prepared to explain why you chose the modules you did.
2. Runtime
Your solution must be runnable via Docker. There's a starter
docker-compose.yml
file in this directory you can use to run your solution
with a Postgres database and Redis key/value store. When we run your
application, we'll check out your GitHub repository and run docker-compose up
and
expect it to run. If you're familiar with Docker and Docker Compose, you're welcome
to change the docker-compose.yml
and/or Dockerfile
, but you will not need to change
those files to complete this exercise.
Note that your solution does not have to use either Postgres or Redis to store its state -- we'll restart your service every time we check its output. However, if you'd like to use a persistent store for state, you should use one of those two options.
If you use CPAN modules, you should use a cpanfile
to declare them. There is
a starter cpanfile
in the app
directory, and the Dockerfile
uses cpanm
to
install the modules listed in that file. When you run docker-compose up --build
, the
container for your service will be built and ready to go, including installing all module dependencies from CPAN. If and when you change your cpanfile
,
you will need to re-run docker-compose up --build
to install any new modules that you've defined.
If you use a Postgres database, there is a starter app.sql
file in the sql
directory.
Modify this to set up your desired database schema. To recreate the database you will
need to reload and refresh the Docker volumes with docker-compose up -V
.
If you use a Redis cache, you can wipe / recreate the cache with docker-compose up -V
, similarly to the database schema.
If you need to change the command that is used at start up (currently CMD [ "perl", "app.pl" ]
),
change the CMD
that is used by the Dockerfile
.
The example hello world app in this repository uses Mojolicious,
but please feel free to use whatever framework / approach you'd like. The example is in a single app.pl
file, but you're welcome to restructure
the application however you feel is appropriate. If you use the example, then once you start things
up with docker-compose up
you will be able to hit the test GET /
endpoint at http://localhost:8080 and see "Hello, World!".
Remember that every time you change the Perl code, you will need to re-run docker-compose up
for it to refresh your changes.
3. Testing
Unit tests are encouraged but not required; we'll understand if you spend your time on the solution rather than the tests. Especially because we provide a test harness, see below. It is likely a good idea to use the test harness to test your code early and as you go, maybe try approaching the problems in parts; create a student, get a student, find students.
There is a exercise/
directory in this repo with a script that you can use to
exercise your solution with Docker. All you'll need to do is build it:
$ cd exercise
$ docker build -t turnitin-check .
And then point it at your solution, passing in the top-level URL to a running server:
Docker for Mac and Docker for Windows
$ docker run --rm turnitin-check http://host.docker.internal:8080
Docker for Linux
$ docker run --network="host" --rm turnitin-check http://127.0.0.1:8080
If everything is working you should expect to see output like this:
test_can_fetch_created (__main__.VerifyCreate) ... ok
test_can_provide_display_name (__main__.VerifyCreate) ... ok
test_fail_duplicate_email (__main__.VerifyCreate) ... ok
test_fail_missing_email_or_username (__main__.VerifyCreate) ... ok
test_fail_missing_last_name (__main__.VerifyCreate) ... ok
test_fills_in_date_fields (__main__.VerifyCreate) ... ok
test_generates_display_name (__main__.VerifyCreate) ... ok
test_fetch_invalid_id (__main__.VerifyFetch) ... ok
test_empty_results_with_records_and_no_match (__main__.VerifySearch) ... ok
test_multiple_match_any_name (__main__.VerifySearch) ... ok
test_multiple_matches_started_after (__main__.VerifySearch) ... ok
test_no_criteria_is_invalid (__main__.VerifySearch) ... ok
test_no_match_started_after (__main__.VerifySearch) ... ok
test_single_match_first_name (__main__.VerifySearch) ... ok
----------------------------------------------------------------------
Ran 14 tests in 0.106s
OK
1. Timeframe
You should take a maximum of three hours to complete this exercise. We want to be both respectful of your time and fair to other candidates who might not have a whole weekend to work on it. It is a complex task, we are looking to see how you approach the problems. Try to pass as many of the exercise tests as you can, and be able to talk about the tests you didn't get to.
2. Git
You will need to use git for this exercise. To get these instructions and a repo with test scripts do the following:
- Create a Github account if you don't already have
one. For the examples below we assume a user
pusheen
. - Clone our repository:
# Using ssh
$ git clone [email protected]:turnitin/perl-coding-exercise.git
# Using https
$ git clone https://[email protected]/turnitin/perl-coding-exercise.git
3. Remote
Once you are done you can put your solution in your own repository by adding it as a remote and pushing to it.
- Create a new repo via the github UI, let's assume you call it
perl-coding-exercise
to mirror ours. - If possible use a private repo. If you've run out of private repos on GitHub then no worries, we'd just like to make sure that every candidate's work is their own.
- Add your repo as a remote and push:
$ git remote add myrepo [email protected]:pusheen/perl-coding-exercise.git
$ git push myrepo main
4. Access
Give these GitHub users read access to your repository:
- janinedog
- omgwagon
- mauton
5. Notify us
When you're ready, please send your repository address to your recruiter contact. If you have any questions about this exercise or if you're running into problems, don't hesitate to reach out to your recruiter and they will pass the message on to the engineering hiring team.
6. Resources
Here are some resources that may be useful:
- Docker for Mac or
Docker for Windows should
help you get Docker installed if you don't already have it. (If you're using
a Linux desktop you can just
sudo apt-get install docker
-- lucky you!) Another resource is Docker Machine which walks you through installing Docker on a VM running on VirtualBox; you may prefer that if you've already got VirtualBox installed. - Docker Compose -- This should already be installed if you use the Docker for Mac/Windows options, but if not it's just a binary you can install.