skip to content
Ollie Bytes

Hell is a Hackernews thread

I once joked during a job interview that if you ever wanted to find reasons not to use a technology, Hackernews was the place to go

Because despite a well-intended set of guidelines, I tend to find a fair amount of vitriol.

Take for example, the recent announcement of FastAPI 0.100.0 release notes. Personally, i’m a big fan of FastAPI. It’s my go-to web framework to develop APIs. While I might wish for a reference section in the docs, they’re nevertheless outstanding. In my opinion, there’s no doubt that the effort put into the documentation has greatly contributed to its rapid adoption and rise to Python’s No.1 web framework.

What does Hackernews think?

Not so positively it seems. Let’s break down the main threads (with a little help from GPT).

  1. Negative A user questions FastAPI’s non-standard versioning despite its wide production use.
  2. Negative Litestar is suggested as a faster, better-maintained alternative to FastAPI.
  3. Neutral A user wishes for Django to take async programming more seriously, reflecting on the Python ecosystem’s resistance to change.
  4. Positive A user is excited about FastAPI’s performance improvements and wonders about cases where Flask might be preferable.
  5. Negative Despite experience with various Python frameworks, a user now prefers Node.js with TypeScript and PostgreSQL for backend development.
  6. Neutral A user questions the advantages of FastAPI over Django Rest Framework.
  7. Positive A user is pleased with the trend of integrating Rust in Python libraries and tools, and has switched to using FastAPI and Pydantic 2.
  8. Negative A comment points out FastAPI’s large number of pull requests and unresolved issues. (18 unresolved)
  9. Positive FastAPI’s quick support for Pydantic version 2 is appreciated by a user.
  10. Negative Despite liking FastAPI, a user finds its API documentation lacking and suggests a need for improvement (Not unreasonable to be fair).

Even when you delve into the discussion under a positive comment, the discourse swiftly turns contrary.

  1. Negative The user lists several issues with FastAPI, including a memory leak, excessive data validation, lack of API documentation, need for ORJSON for maximum serialisation performance, and log format settings not being respected. They acknowledge that FastAPI is not a bad framework but needs time to mature, similar to Flask.
  2. Negative The user points out that the performance increase in Pydantic v2 may be due to Pydantic v1 being slow. They also mention that despite using Rust, Pydantic v2 is still slower than pure Python in some benchmarks.
  3. Neutral The user is concerned about FastAPI being a project with a bus factor of one and being too in flux, suggesting that they prefer using more stable technologies.
  4. Neutral Coming from Django, the user finds adding authentication to a FastAPI project cumbersome and notes the potential regret of not choosing a full framework as projects grow.
  5. Negative The user expresses skepticism about the praise for FastAPI’s performance improvements, noting that it means little without context or comparison points.
  6. Neutral The user suggests using gevent to avoid replicating every library for async I/O.

The Hackernews pattern of discourse

In general, I have this perception that a typical Hackernews thread goes something like this:

  • Disagreement with the linked article
    • Disagreement with the disagreement, leading to nuanced conversation
  • Suggestions of newer frameworks
  • Suggestions of more mature frameworks
  • Complaints that mature frameworks aren’t adapting quickly enough
  • Complaints that newer frameworks are too new
  • Generate criticism of the ecosystem (Python)

But really, I think this comment in a perfect microcosm exemplifies the kind of unchained conjecture you can expect to read on a typical thread. I present a reply too:

‘Did not take long at all to see Pydantic version 2 support. Nice!’

I still can’t stand Pydantic’s API and its approach to non-documentation. I respect the tremendous amount of hard work that goes into it, but fundamentally I don’t like the developer experience and I don’t think I’ll ever feel otherwise. I use it because my coworkers like it and I’ve learned its advanced features because I had to in order to get things done, not because I like it.

I would love to see a FastAPI alternative still using Starlette internally, but using Attrs + Marshmallow + Cattrs + Apispec instead of Pydantic. It would be a little less “fast” to write a working prototype, but I’d feel much more comfortable working with those APIs, as well as much more comfortable that my dependencies are well-supported and stable.

The problem of course is not that gluing those things together is hard. The problem is that now someone has put untold hundreds of person-hours into FastAPI, and replicating that level of care, polish, bugfixes, feature requests, etc. is difficult without putting in those hundreds of person-hours yourself.

Classic Hackernews.


Scraping Hackernews:

Incase you’re interested in scraping a Hackernews post yourself, here was some quick and dirty code I wrote

import requests
import pandas as pd
import time

def fetch_comments(id, parent_id=None, level=0):
    response = requests.get(f"https://hacker-news.firebaseio.com/v0/item/{id}.json")
    time.sleep(0.1) # Lazy throttle
    data = response.json()
    comments = []

    comments.append({
        'id': id,
        'parent': parent_id,
        'level': level,
        'text': data.get('text', '')
    })

    if 'kids' in data:
        for kid_id in data['kids']:
            comments.extend(fetch_comments(kid_id, id, level + 1))
            
    return comments


post_id = 36635523 
comments = fetch_comments(post_id)

df = pd.DataFrame(comments)

Getting the level 1 comments


df_comments_l1 = df.loc[df['level'] == 1]
for i, (cid, t) in enumerate(zip(df_comments_l1.id, df_comments_l1.text)):
    i += 1
    print(f"{i=} - {cid=}")
    print(t)
    print(f"########################################################")

Delving into the comments under a comment

df_subset = df.loc[(df['parent'] == 36638163.0) & (df['level'] == 2)]
for i, (cid, t) in enumerate(zip(df_subset.id, df_subset.text)):
    i += 1
    print(f"{i=} - {cid=}")
    print(t)
    print(f"########################################################")