Your product owner has an idea for a new sort of wiki. The wiki will be very simple, providing a short summary about the subject and then a list of links to related resources below the summary; the summary and the links will be user generated, all via an API you will build.
Your designer has provided a mock for how a page should render. This example shows the “Space” page with content and three cards that when clicked take you to that page: one an internal redirect, one to a page that doesn’t exist (shown in red), and a normal link.
Your designer, tech lead and product owner are at a retreat today, but they want you to build the first version of the system while they are away. They are most interested in the problems you encounter while building this so they can work out where to focus their attention next.
The first iteration will allow creating pages, adding links to pages, deleting pages, redirects, and rendering pages. Your server should run on port 8142 and respond to the HTTP requests described below. It will be run on a production environment.
- /api/page/Space
-
- Creates page ‘Space’ with the text provided in the request body.
- /api/link/Space/to/NASA
-
- Adds a link on page “Space to “NASA”. It is possible for a link to NASA to exist, but for the page NASA to not exist. It is not possible to add external links.
- /api/redirect/Unicorn_whale/to/Narwhal
-
- Register that the page Unicorn_whale should redirect to Narwhal.
- /api/delete/Narwhal
-
- Deletes the page Narwhal. Any pages that are linking to Narwhal will continue to be shown but will now display as red links.
- /wiki/Science
-
- Render the page Science (see mock) given its existing state. When links are external or missing that should be visible in the UI. If the page accessed is a redirect, it should redirect and display that page.
Note: There should be no need for additional endpoints to fulfill the specification, but if needed you should feel free to add additional other endpoints underneath the /api/ path.
Include setup instructions to run on a Mac/Linux laptop when complete. To complement your implementation, write about the problems you encountered while building this. This might include a list of assumptions you made during the implementation, problems you had to overlook, and shortcuts you took that you will need to discuss with your designer and product owner when they return. You should aim to spend 2-4 hours on the task.
Approach
Laravel powers the application. It’s easy to configure. It supports many DB options. It is flexible in that there are many different ways to run the application. One of those ways is to run it via virtual environment. I typically go this route because it’s self-contained and portable.
In the interest of faster turnaround, I decided to skip setting up the virtual machine and simply serve the site via artisan. It has the added benefit of allowing port specification. I’m using SQLite DB because it’s portable. It is fine for early-stage development. As the project scales it becomes less fine.
Foundation takes care of the responsive grid. This was not asked for specifically, but the job posting mentioned this was important. I usually mix that with SASS and some task runner, but the short deadline prevented that from happening.
Criticism
Now is the part where I’m supposed to talk about what I discovered after working for “2 to 4 hours” architecting and building the application. I’ll do that below. In the meantime, I’ll complain a little about this type of coding exercise 🙂
This is a flight of fancy project. It’s large in scope and open-ended to the point of obnoxiousness. It’s a full web application that, upon submission, must run in a “production environment” whatever that means.
They’re requesting front-end functionality, styling against a PDF mockup, a web portal, and a back-end API. They’re specifically asking for five API endpoints, any one of which would take several hours to do at a real job.
In addition, they’re also requesting a few specific DevOps tasks including handling custom HTTP ports and performing non-trivial 301 redirects. On top of that they expect a full write up of architecture, setup, how-to, assumptions, questions, and discussion of my journey of discovery catered to several different audiences: developers, project managers, and the product owner.
In reality, if I assigned the same task on-the-job, I would spend the “2 to 4 hours” doing one of the following:
- figure out what language, framework, and DB to use
- analyze the ask, think critically about it, and write up preliminary documentation and questions
- code up a throwaway POC to get my hands dirty and figure out the major problems to be solved
Instead I chose spend a day and a half doing all three. I’ve been burned abiding by time constraints in the past. Also, the application isn’t great, but it’s also not throwaway. I’m aware that senior developers will be judging my work.
This annoying coding exercise has no basis in reality.
Okay, back to it then.
Discussion
The best way to learn a project and discover the problems you will face is to dive straight in. The big unknown here are how non-existent links work. The comps show these with text in red. Where do they come from? I assume they carry over from related links and redirects: actions taken on other pages. So that is how I coded the application to work. If an article is set to redirect or link to a non-existent article, I create the article using the slug provided. It’s told that the application isn’t aware of these, but the comps imply the opposite. I’m not sure how a non-existent article has a custom thumbnail otherwise. This may be all wrong, but it’s okay. We’ve learned some things along the way.
It took a little time to realize that I shouldn’t be using IDs as currency to relate data as I move about the application. I started with an ID approach, the introduced GUID slugs, then confused everything by mixing and matching them before I stepped back and realized slugs are king in this application. So now they are the source of truth moving forward.
Articles contain a canonical_article DB field that controls whether body or author fields are ever rendered. If the field is set to another article’s slug, the link will reflect the path to the canonical article. As such, clicking the link takes the user directly to the canonical article, thus the body and author fields of the “placeholder” related article will never be rendered. Users can still edit the placeholder to their liking and if the canonical_article field is ever unset in the future, it will act as a regular article.
There is no 301 redirect to the canonical article. I wasn’t initially sure if that was needed or part of the scope of this exploratory work. It has become clear as I’ve worked on the project that the redirects may be necessary to address the problem of stale pages with improper links. This will only get worse as the project scales. Redirects would address the problem by invoking just-in-time decisioning at the stateless (stale-free) server level. There may be other novel approaches, but 301s are a time-tested approach.
I’ve implemented soft deletes. This is usually preferable to actually deleting records from the database. The published field acts as a toggle for articles when enabling on creation and disabling on deletion.
I’m not sure if a related link that redirects to an unpublished “non-existent” article should show in red font. Currently it does not. It could work but I’d have to make DB querying a little greedier. This would take a little time, may hinder processing speed, and would add complexity to the site.
Here are proposed next steps:
- create a front-end form layer as a user interface to the API so that users can create/edit the new summary articles and related links more easily and intuitively:
- fields include: slug, title, body, thumbnail, canonical_article, published
- author field should be populated with the logged-in user
- image upload functionality has not been implemented, need to do that and save the path to the new asset in thumbnail field
- in the interest of time, some functionality currently exists in views: this functionality should be extracted
- controller methods are too fat and have code smell from too many nested blocks – these need to be refactored
- clean up the styles: the CSS already feels unwieldy – we should update to SASS, modularize, and employ a task runner
Conclusion
In the end, I published the application to a private BitBucket repository and shared it with two reviewers. I was told the code was great, but otherwise the reviewers had no feedback.
I really appreciate good feedback when I put in the effort, but it doesn’t happen as often as I would like. When I checked back to see if they had viewed my solution – yes, I am able to do this – I found out the reviewers never bothered to review my code.
Lame.
But you’re in luck. The repo is now public. You can view it here!
In the end I got a job at Gartner a month later. So that’s a pretty incredible silver lining.