My Favorite Technical Interview

In the software industry, some argue that technical interviews are broken, and I tend to agree. In my experience, a lot of technical interviews don’t test what you’ll be doing in your actual job. Instead of going through why they’re broken, I’m going to tell you about my favorite technical interview.

Table of Contents

The Interview

I can only speak from experience, and most programming jobs that I’ve interviewed for have been backend, Django, and Python focused. The interview focuses on those technical skills, but I think the principle of testing a candidate on what they’ll be doing day-to-day applies to any technical interview. I also think it’s important to do more than just technical interviews. Team culture affects morale in significant ways, and culture interviews should be done alongside technical interviews.

The interview goes like this:

Let’s say you work as a software engineer for a sandwich shop. You’re going to help build the website for the shop using Python and Django. Using a whiteboard, or online whiteboard program, we’re going to have you sketch out your solution to various feature requests from the product manager.

For the first week of the job, the product manager wants you to create a comment API for receiving comments on the website and displaying them. This API will need to save comments to the database and note the time of the comment. The email and the comment text will be in the request body.

At this point, the candidate should sketch out some code or pseudocode and explain what would need to be done in Django, such as a database schema migration and creating a model, serializer, and viewset.

# The class maps to a database table with each field being a
# column and each instance of the class being a row
class Comment(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    email = models.EmailField()
    text = models.TextField()

# The serializer transforms the database row into JSON
class CommentSerializer(serializers.Serializer):
    class Meta:
        model = Comment
        fields = ("email", "text")

# The viewset defines what HTTP actions and endpoints are available.
# A ReadOnlyModelViewset enables a GET for a list of comments and a GET
# with an ID in the URL for individual comments.
# A CreateModelMixin enables a POST to create a new comment.
# The URL might look something like this
# https://mysandwichshop.awesome/api/v1/comments/
# and this https://mysandwichshop.awesome/api/v1/comments/23423/
class CommentViewset(mixins.CreateModelMixin, viewsets.ReadOnlyModelViewset):
    queryset = Comments.objects.all()
    serializer_class = CommentSerializer    

During the second week, the product manager comes back with more feature requests. The product manager wants to be able to mark each comment as “read”. The comments marked as “read” should not be shown and comments more than a month old should be marked as “read”.

Once again, the candidate will write some code or pseudocode and explain what needs to be done on the backend to accomplish this.

# The class maps to a database table with each field being a
# column and each instance of the class being a row
class Comment(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    email = models.EmailField()
    text = models.TextField()
    # We need to add a new column to the table and give it a
    # default because the table already exists
    read = models.BooleanField(default=False)

# The serializer transforms the database row into JSON
class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = ("email", "text", "read")

# The viewset defines what HTTP actions and endpoints are available.
# A ReadOnlyModelViewset enables a GET for a list of comments and a GET
# with an ID in the URL for individual comments.
# A CreateModelMixin enables a POST to create a new comment and an
# UpdateModelMixin enables partial updates using PATCH
# or full updates using PUT.
# The URL might look something like this
# https://mysandwichshop.awesome/api/v1/comments/
# and this https://mysandwichshop.awesome/api/v1/comments/23423/
class CommentViewset(mixins.UpdateModelMixin,
                     mixins.CreateModelMixin,
                     viewsets.ReadOnlyModelViewset):
    # We need to filter out all read comments so that they don't display.
    # By making this the new queryset, the endpoint
    # won't return read comments.
    queryset = Comments.objects.filter(read=False)
    serializer_class = CommentSerializer

# We'll use a celery task scheduled to run once a day to accomplish this.
# The schedule isn't defined here, but we'll use celery's scheduler.
@shared_task
def mark_old_comments_as_read():
    # pendulum is a datetime library for python
    a_month_ago = pendulum.today().subtract(months=1)
    # This queries for comments that aren't marked as "read" and were
    # created more than a month ago. It then updates all
    # the comments it found to read=True
    Comment.objects.filter(created_at__lte=a_month_ago,
                           read=False).update(read=True)

Finally, for the third week, the product manager wants the ability to reply to comments. Similar to the original comment, we’ll need to know the time and whether the reply has been read.

# The class maps to a database table with each field being a
# column and each instance of the class being a row
class Comment(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    email = models.EmailField()
    text = models.TextField()
    read = models.BooleanField(default=False)
    # This is a nullable foreign key (one to many) to the same
    # "Comment" table. This allows a comment to be replied
    # to multiple times.
    reply_to = models.ForeignKey("self",
                                 null=True,
                                 on_delete=models.SET_NULL,
                                 related_name="replies")

# The serializer transforms the database row into JSON
class CommentSerializer(serializers.Serializer):
    class Meta:
        model = Comment
        fields = ("email", "text", "read", "reply_to")

# The viewset defines what HTTP actions and endpoints are available.
# A ReadOnlyModelViewset enables a GET for a list of comments and a GET
# with an ID in the URL for individual comments.
# A CreateModelMixin enables a POST to create a new comment and an
# UpdateModelMixin enables partial updates using PATCH
# or full updates using PUT.
# The URL might look something like this
# https://mysandwichshop.awesome/api/v1/comments/
# and this https://mysandwichshop.awesome/api/v1/comments/23423/
class CommentViewset(mixins.UpdateModelMixin,
                     mixins.CreateModelMixin,
                     viewsets.ReadOnlyModelViewset):
    # We need to filter out all read comments so that they don't display.
    # By making this the new queryset, the endpoint
    # won't return read comments.
    queryset = Comments.objects.filter(read=False)
    serializer_class = CommentSerializer

# We'll use a celery task scheduled to run once a day to accomplish this.
# The schedule isn't defined here, but we'll
# use celery's scheduler.
@shared_task
def mark_old_comments_as_read():
    # pendulum is a datetime library for python
    a_month_ago = pendulum.today().subtract(months=1)
    # This queries for comments that aren't marked as "read" and were
    # created more than a month ago. It then updates all
    # the comments it found to read=True
    Comment.objects.filter(created_at__lt=a_month_ago,
                           read=False).update(read=True)

You could expand the interview to include requirements such as refactoring “read” to be on a per-user basis, adding comment deletion for the owner of the comment, and having a voting system for comments.

The second part of the interview involves the candidate presenting a programming project that they’re proud of. This project can be from work or a personal one. Ideally, the candidate would be able to talk through the project from start to finish, what challenges they encountered, and why they’re proud of this project. I think it’s important to give candidates an opportunity to speak positively about themselves and, hopefully, show passion because passion tends to produce the best work. Follow-up questions become a significant aspect of this part of the interview to encourage the candidate to open up. Of course, these questions change from interview to interview and are more art than science.

Why Do I Think This Interview is Good?

This interview tests the skills the candidate uses on the job and encourages the candidate to show passion. I love listening to people talk about what they’re passionate about, and I think it provides insight into who they are as a person. I know that not everyone is passionate about programming which is why a crucial piece of the project portion is allowing the candidate to talk about a work project. Not everyone programs in their free time, and that’s ok. Most people will have a project they’ve worked on and are proud of.

If we gave the candidate a bunch of algorithm questions, they might provide some insight, but in my opinion, algorithm problems would not truly evaluate whether the candidate is right for a Python and Django job. While this interview is geared toward Django web developers, the principle of testing skills relevant to the job can be adapted to an interview for any job. Let’s say instead of interviewing for a web developer job, the job involves embedded development. Ideally, the interview would be designed around dealing with limited system resource, understanding microprocessors, or programming in C. In the end, using algorithm questions isn’t the issue. In fact, if the job calls for it, testing a candidate with algorithm questions might be the best choice. Not testing a candidate on what they’ll be doing day-to-day is the issue.

Good Interviews Make Me Want To Work For A Company

When interviewing at a company, you should be interviewing the company and your potential colleagues. If I’m interviewing for a typical Python and Django job, and the company gives me a bunch of algorithm questions, it does not make me want to work for the company and makes me doubt their hiring process. The best interviews turn into conversations between passionate people who care about the work they do. My first interview at my current company followed this mold, and I work with a lot of talented and passionate engineers. Before I joined, I had multiple offers, but the company’s interview process and the awesome conversations showed me that I needed to work there. We still use a similar version of the above interview, and in my opinion, we’ve been able to hire a lot of great people due to our interview process.

Steven Pate
Steven Pate
Founder

Senior Software Engineer who likes Python and has some social skills