Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Popescu Alexandru Interview #4

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

popescualexandru9
Copy link

@popescualexandru9 popescualexandru9 commented Jan 28, 2025

Instructions

Provided Coding challange description

Overview

You have been hired by a company that builds a app for coffee addicts. You are
responsible for taking the user’s location and returning a list of the three closest coffee shops.

Starting point

Fork or clone our ruby starter project: https://github.com/Agilefreaks/ruby-interview-starting-point

Input

The coffee shop list is a comma separated file with rows of the following form:
Name,Y Coordinate,X Coordinate

The quality of data in this list of coffee shops may vary. Malformed entries should cause the
program to exit appropriately.

Notice that the data file will be read from an network location
(ex: https://raw.githubusercontent.com/Agilefreaks/test_oop/master/coffee_shops.csv)

Output

Write a REST API in any framework you wish (excluding Rails) that offers the posibility to take the user's coordinates and
return a list of the three closest coffee shops (including distance from the user) in
order of closest to farthest. These distances should be rounded to four decimal places.

Assume all coordinates lie on a plane.

Example

Using the coffee_shops.csv

Input

47.6 -122.4

Expected output

Starbucks Seattle2,0.0645
Starbucks Seattle,0.0861
Starbucks SF,10.0793

@popescualexandru9 popescualexandru9 changed the title Develop Popescu Alexandru Interview Jan 28, 2025
Copy link
Member

@pirvudoru pirvudoru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the solution. Please take the time to answer the questions. No need to do code changes.

shops_with_distances = coffee_shop_finder_service.closest_shops(lat, lon)
format_response(shops_with_distances, lat, lon)
rescue StandardError => e
handle_error(e)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does success returns a plain/text and failure returns a json?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey! I'll answer here for 2 other comments as well to keep the conversation in one place since they're all related.
It is actually a mistake. I intended to use application/json everywhere, but while debugging the specs for CoffeeShopController I tried multiple approaches and forgot to roll-back this change 😅

Related comments: Why set it here as json, and override it on line 20? , Why did you choose this content type?


private

# Format shops into "Name --> distance <-- (user-lat, user_lon)" strings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this format? What's the intended audience?

Copy link
Author

@popescualexandru9 popescualexandru9 Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format from that comment is outdated as well, it actually returns:

Coffee shops nearest (user_lat, user_lon) by distance:

Distance <--> Name

I meant to match the __Expected output__ in a more readable format

end

before do
content_type :json
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why set it here as json, and override it on line 20?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

end

get '/closest_shops' do
content_type 'text/plain'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you choose this content type?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when /Invalid CSV/ then 400
when /Failed to fetch CSV/ then 502
else 500
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any other alternative considered? Can you share the pros and cons of different approaches?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes so I choose this approach because it was simple and fast to implement for this particular coding exercise.

It's not a scalable solution though, other better approaches I think would be:

  • custom exception classes - similar to what I've done in CoffeeShop model class
    • I think this would've been the best approach since it provides type checking, it's easy to add metadata to errors and you can create some sort of logical hierarchy for errors which is also more readable. Also framework agnostic
    • The drawback on speed ( initial class setup ) is minimal
  • using a framework-specific handling
    • Biggest con is that I'm not familiar with Sinatra

def handle_error(error)
status_code = case error.message
when /Invalid CSV/ then 400
when /Failed to fetch CSV/ then 502
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

502 is not correct here according to industry standards: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502

"The HTTP 502 Bad Gateway server error response status code indicates that a server was acting as a gateway or proxy and that it received an invalid response from the upstream server. This response is similar to a 500 Internal Server Error response in the sense that it is a generic "catch-call" for server errors."

Our server does not act as a gateway or proxy, although it requests another server. In our case, the remote CSV acts as a database

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you. I treated the remote CSV as another server, so 502 made sense in my head, but if it mimics our said DB, code 500 would make more sense 🧠

# Handle errors with appropriate HTTP status codes
def handle_error(error)
status_code = case error.message
when /Invalid CSV/ then 400
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

400 is not correct here according to industry standards: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400.

"The HTTP 400 Bad Request client error response status code indicates that the server would not process the request due to something the server considered to be a client error. The reason for a 400 response is typically due to malformed request syntax, invalid request message framing, or deceptive request routing.

Clients that receive a 400 response should expect that repeating the request without modification will fail with the same error."

It's not requester fault that the server's data is invalid.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requester may not modify the request and stop receiving 400 at later times

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea correct this is a mistake. The fact that our DB data is invalid is not on the client side.
I should've use code 500 or 422 Unprocessable Content - "status code indicates that the server understood the content type of the request content, and the syntax of the request content was correct, but it was unable to process the contained instructions."

Float(str)
end

# Handle errors with appropriate HTTP status codes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't tell more than what the code tells. Comments have a specific purpose throughout the code. You can check this paper for when comments might be a good idea: https://stackoverflow.blog/2021/12/23/best-practices-for-writing-code-comments/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be deleted as well: # Validate CSV row structure 🗑️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants