It's also kind of stupid in my opinion because of the way they pick the question. Usually they'll find the most interesting leet code style problem that their company had to solve one time in a blue moon and make that their test problem.
71 karma · joined July 4, 2023
It's also kind of stupid in my opinion because of the way they pick the question. Usually they'll find the most interesting leet code style problem that their company had to solve one time in a blue moon and make that their test problem.
Yeah thanks for the tip. I'm not taking it personally. But I still do consider this practice rude and inappropriate. It's just the rudeness is not directed at me personally but at everyone in general.
It still will reflect on the company regardless of whether anyone takes it personally.
Good tip. They already said in the rejection letter no feedback will be given.
> For a senior dev, I would expect tests even if I didn't ask for them explicitly.
Several people mentioned this. You're definitely onto something here.
The thing here is that the code is mostly IO, not amenable to unit testing. For most web applications that has a lot of logic moves from database to web app to client to route, not much can be unit tested.
Typically the best pattern to follow here is to segregate as much logic away from IO as you can and move it into pure functions. That's what utils.py is. But you'll see utils.py is so small there's not much there to be worth testing. (Dependency Injection and mocks is a more popular pattern for unit testable code but it makes things much more complicated and you end up writing a lot of mock code and the unit tests end up testing mocks more then the actual logic itself)
The fact that utils.py is so small is an indicator that this web app is mostly an IO app and you have logic on the database dependent on logic on the server and vice versa which takes a lot of time to write "integration tests" for. Integreation tests are excessive imo for a takehome project.
Believe it or not excessive adherence to unit tests is actually a quality of junior engineer. They aren't knowledgeable enough to practice nuance and to see when unit tests become completely pointless.
But this is probably just my opinion. I think it's quite likely a lot of (senior) reviewers are following your train of thought.
You can spin the code up with "docker-compose up --build"
You and everyone else can then verify your statements on my project by running this:
docker exec -it flask poetry run http localhost:5000/api/abc/
docker exec -it flask poetry run http localhost:5000/api/xyz/
docker exec -it flask poetry run http localhost:5000/api/xyz/
docker exec -it flask poetry run http localhost:5000/api/xyz/
(By the way the response on these calls actually returns the current count of the specific route for easier testing. You can see whether your hypothesis is real by watching the results unfold on each request.)Then run this:
docker exec -it flask poetry run http localhost:5000/stats/
and the results are: [
{
"count": 3,
"path": "xyz"
},
{
"count": 1,
"path": "abc"
}
]
Which is correct. Which means You are wrong sir. You did not test my code. Literally, I just ran this in my code, you can try it if you have the time.I mean you're not the only person to make wrong assumptions on this thread. I'm getting the sense that reviewers just take a quick look and make rash judgements and then throw it into the trash. I'm betting it's not too far off from what the reviewer did for me.
You didn't run the code, which is fine, it's not expected of you. But shockingly, I think it's quite likely the evaluator at the company didn't run my code either and that is not expected or appropriate imo.
I think it's safe to say that This thread in general and your response included is becoming an accurate representation of typical shenanigans that happen for take home projects.
(Your mistake here fine, not a big deal, you're not evaluating me just giving me advice so getting things wrong is understandable in your case)
>- requirements.txt is the standard for specifying deps, right?
poetry is a new thing. I just tried it for this project it replaces requirements.txt with pyproject.toml
>- making a README explaining the project might be good
I did. It's documentation.md. I didn't title it README.md because I wanted the front page on github.com to show the takehome instructions rather then my docs on it.
>- tests?
This was addressed in the documentation.md. I was given 4 hours to work on this problem so I just used manual tests.
>- building the flask application via a function is a bit more testable
Flask is an IO app. It's inherently not testable via unit tests because it's a server. You'd have to build integration tests around an entire server which is huge overkill for this project. Testable logic is usually pure and stateless, that's located in utils.py. It's ok this is good criticism.
You can monkey patch or make your code 10x more complicated with modules that accept mockIO for dependency injection but I'm actually against this style of programming as it over complicates the code with little benefit.Trying to test Flask by mocking everything out is basically just testing the mocks. Better to do this haskell style and segregate IO from pure functions (see utils.py). IO can't be unit tested.
Not many people understand the "testing" philosophy I'm following here so this is good feedback. I should sometimes just follow what's popular but ultimately not the "best" way in my opinion.
>- - output not sanitized for /test endpoint
this I don't get? Sanitized? The http request returns typical python structs, which are automatically converted to Json by flask/quart.
>- did you have to define your own Json type? Is that complete? Where’s null?
Nope don't have to, type checking is optional in python, but why not? You have to do this to get correct type annotations to everything. You're right about this missing Null/None. It all still type checks.
>- generator in util could have been pulled out probably? It’s created every time
Why pull it out? A generator is cheaper then creating a list from a list comprehension every time. instead of storing every value in a list and iterating through it, I just iterate through a generator. Saves memory and runtime cost is still O(N) regardless.
>- - url generate function should probably template hostname — that’s more important than host, most of the time for running in different environments
Sure but the context is a takehome project and it's running in docker-compose as specified by the directions. I mean yes, I can make that utility function more general for sure.
>- trailing slashes matter in flask, evidently, and every request without one gets redirected (test suite would have caught this)
I'm aware of this, it's not a mistake. I left it in as valid. The redirect does not get counted so api/xxx/ is equivalent to api/xxx
- on the usage of redis, I wonder if scanning + in-memory aggregation is better… zincr/zrank/zrevrange is good, but you’ll have to hold all data in memory (and receive it in one large response) and logN anyway, might as well do it simply with a set with a dynamic prefix and scan while building the output data structure as you go.
Your way is NlogN sorting and constant time inserts. The current way is zero cost sorting and logN inserts.
LogN is blazing fast, while nlogn can get slow if N gets too large. See this for relative visualization: https://i.stack.imgur.com/osGBT.jpg. Given the picture I would say my way is better. .
>- - do your API endpoints return JSON?
Yes this is the default. If you return anything that's equivalent to that JSON type I defined in utils.py, flask will automatically return serialized json.
> - error handling around points of failure like redis
Flask/quart runs under the hypercorn server (see the dockerfile). Additionally If a handler throws an exception the user automatically gets a 500 error from flask it's handled exactly as you would expect an http server should handle it. The server does not crash.
>— your app goes down if the connection is flaky right?
No it does not. hypercorn remains running if the python worker crashes. But of course if hypercorn itself goes down then it's done. This of course can be mitigated by systemd but that's overkill for this project.
>- zrevrange is deprecated now btw
Yeah your right. My mistake. Still works though. So it's marked for deprecation, but not actually deprecated yet.
>-healthz endpoints?
>-metrics?
>-tracing?
>-error reporting (ex. Sentry)
For a four hour take-home project? These things weren't even in the spec and how these things and if these things are implemented are extremely variable per company across the industry.
You know overall your post has been enlightening but not in the way you think because we are clashing on most of these points. I can see how developers can literally disagree on everything and how code reviewers can make a ton of assumptions and have a ton of arbitrary opinions. Not to mention varying levels of experience and what experience for that matter causes them to make completely different judgement calls. Don't take this as a slight, I'm sure if I was in your position I would be reviewing your code in the same way and you'd see me in the same way as well.
I'm starting to think take home assignments are just bad. Even worse for evaluating programmers then algorithm interviews because there's so many biased variables here that programmers are just clashing on. Maybe it's a good filter for finding programmers who view the programming world in the exact same way they see it.
That's why I posted my code. Was wondering if there's something fundamentally wrong here.
Just wondering though with 4 failures in a row, I think there is a good chance something is wrong with my code so I wanted to make sure with this post.
I've been doing the grind for years, but this is the first time I had so many take home projects.
Additionally, I never gotten hired by doing a good take-home either. Which is weird.
Additional question: What's your guys take on take-homes: Anybody ever successfully get hired by doing well on a take-home?
Thanks! They requested me to spend only 4 hours on it.
> I can't see the restriction on number of path segments in the code ("/api/ followed by 1 to 6 path segments") and they might have insisted on tests while not mentioning tests.
the segment amount is restricted via a default value in utils.py:
def generate_test_paths(test_path_amount: int,
segment_amount: int = 6, #here!
string_pool_amount: int = 3,
string_length: int = 3) -> List[str]:
The segment_amount gets inserted into this lambda: segments = (segment_generator(random.randint(1, segment_amount)) for _ in range(test_path_amount))
which generates a list of strings from a pool of pre-selected strings.Perhaps this section isn't clear?
>Nobody can tell what "production readiness, and code structure" might mean for them.
yeah I hate that.
People are biased, and I'm biased as well, so I hope other people can see past it and see what's wrong with my code.
I'm not exactly the most successful socially saavy engineer with a bunch of industry connections. I'm more just a lone wolf programmer. I like to think I'm really good, but guess not?
In the realm of program design and organization, we find ourselves lacking a comprehensive theory. We lack a framework that determines the most optimal way to compose logic and structure code. To embark on this quest, we must first formally define what it truly means to be "more optimal." While theories for achieving optimal performance exist, we lack a cohesive theory that maximizes modularity, allowing code refactoring to be a seamless process of shifting and recomposing existing modules.
To establish a clear definition of optimality, we require precise and formally defined primitives that symbolize logic. With these foundations in place, we can subsequently develop a formal definition of what constitutes optimal program organization.
Once we have this definition, the next step is calculation. Given these logical primitives, how can we determine the most optimal arrangement of our programs? Calculation becomes possible only when the objective and all the relevant primitives are formalized within a symbolic language.
What lies at the heart of this matter is the potential elimination of the concept of "Design" as we know it. Nobody "Designs" the shortest distance between two points, instead, we calculate the shortest distance between two points with a theory of geometry. This is what I'm talking about: A future where we "calculate the design of programs."
"Design" is merely a label we attach to areas where we lack a formal theory. It represents the wild west of programming, where the absence of an understanding of optimality leads us to rely on haphazard guessing and intuition. This partially explains why the field of IT appears to continuously expand in complexity without ever converging on the perfect technology. Front-end development, in particular, suffers from this uncertainty, as nobody knows for certain if the current status quo is truly superior to previous approaches.
In short: A designer is just someone who makes lots of random guesses. That is the essence of design.
Category Theory, in its current form is mostly impractical. This is not that far from the area that calculus once occupied when it first was discovered. I also cannot definitively claim that category theory will, in the far or near future, provide us with a theory that enables program design through calculation. However, if any theory comes close to achieving this feat, it would undoubtedly be category theory. By formally defining a high-level perspective of logic using only a handful of primitives, it holds the potential to revolutionize our approach by taking the first Required step in developing a formal theory around this topic.
Perhaps it will prove to be futile in the end, or perhaps your reservations will be remembered alongside those who opposed progress throughout history. Recall those who dismissed complex analysis or calculus during their early stages as impractical and useless endeavors. I implore you to exercise patience and observe rather than waste your breath on something you may profoundly misjudge."
Second: implementation is harder then design. If AI can do implementation it can for sure do design. And it is demonstrably true that chatGPT can do design just as well if not better than implementation.
The US is deploying an entire chain of military forces around china. Literally. From Japan all the way through taiwan to the Philippines we are boxing china in with massive amounts of deadly firepower.
Any equivalent action against the US would launch mass hysteria among the public. As much as I don't agree with certain things china does.. they are showing incredible restraint here. The US media is pulling off the most successful propaganda campaign I've ever seen... Literally characterizing this action as something that's self defense and necessary for world peace.
Self defense is a complete joke. it's 100 percent military aggression by the US. All in the name of economic and military power.
Pick your poison: too little money at dealing with nonsense, or too much money on simple shit such that you spend all your time creating more and more layers of nonsense.
Yeah all that brain power is deployed to create technology that is barely used ever. It's just there to scare other people away.
That's the current state of technology right now. On one hand in defense you have people cooking up technology that will never be used.
On the other hand you have the entire IT industry cooking up technology to make simple things 1000000x more complex.
It's like humanity has run out of shit to do and the money is being directed towards useless busy work.
Software design doesn't have a formal theory that defines optimal design and there isn't any empirical evidence either.
We know the shortest distance between two points because we have a formal theory that defines it. Because software design has no such thing, everyone is making shit up.
Doesn't matter how intelligent you are. If the thing that's being designed can't be quantified it's the wild west. Those brains are focused on optimizing things we have no idea how optimize and things we can't even measure.
Complex missile systems? That's nothing compared to my vm running kube, running docker, running nextjs react emulating x86 and running kube again. Layers of more software surrounding software is the next big thing. Also microservices and LLMs.
But this is only true for certain languages. Overall most type systems are not Turing complete and therefore not suited for general computation
Ironically the separation of state away from logic is why backend web development tends to be easier than say game development.
Not exactly sane. Go is one of the most clunkiest awkward languages I've ever worked with. The only benefit is that it's somewhat easy to learn.