21 Punkte von GN⁺ 2025-07-17 | 6 Kommentare | Auf WhatsApp teilen
  • Durch den aktuellen Trend in der AI-Entwicklung habe ich ernsthaft begonnen, Python zu lernen und zu nutzen, und bin inzwischen sehr zufrieden mit diesem Ökosystem
  • Python hat sich im Vergleich zu früher zu einer deutlich schnelleren und moderneren Sprache entwickelt, und ich spüre die rasanten Fortschritte, etwa bei der Performance durch Cython
  • Ich setze moderne Entwicklungstools und Bibliotheken wie uv, ruff, pytest und Pydantic aktiv in meinem Workflow ein und steigere so die Produktivität
  • Ich nutze außerdem Projektstrukturen und Automatisierung, um die Unterschiede zwischen Produktionsumgebungen und einer auf Jupyter-Notebooks/Skripten basierenden Entwicklung zu verringern
  • Mit GitHub Actions, Docker usw. habe ich CI/CD, Tests und Infrastrukturverwaltung effizient aufgebaut

Zusammenfassung von I’m Switching to Python and Actually Liking It

Warum der Wechsel zu Python

  • In AI-zentrierten Entwicklungsumgebungen ist Python zur de-facto-Standardsprache geworden
  • Früher wurde es nur für einfache Skripte verwendet, inzwischen nutze ich es ernsthaft, um „praxisnahe Anwendungen“ wie RAG, Agenten und generative AI zu bauen
  • Dabei wurde mir klar, dass sich das Python-Ökosystem im Vergleich zu früher enorm weiterentwickelt hat

Drei Stärken von Python

  1. Reiches Ökosystem aus Bibliotheken und Tools: spezialisiert auf Datenverarbeitung, Analyse, Web und AI
  2. Performance-Verbesserungen durch Cython usw.: compilerbasierte Optimierung ist möglich
  3. Verbesserte Lesbarkeit der Syntax: Legacy-Syntax wie __init__, __new__ wird verborgen, stattdessen gibt es intuitivere Syntax

Projektstruktur (auf Monorepo-Basis)

  • Bevorzugt wird eine Monorepo-Struktur, die Backend und Frontend zusammenführt
    • Die Wahl eines einzelnen Repositories wird mit effizienterer Codeverwaltung, leichterer Suche sowie vereinfachten Deploy- und Test-Pipelines begründet
    • Eine übermäßige Aufteilung eines Projekts auf mehrere Repositories wird als Zeichen von Over-Engineering betrachtet
  • Ein typisches Beispiel für die Projektstruktur sieht so aus
    project/  
    ├── .github/ : CI/CD-Workflows wie GitHub Actions  
    ├── .vscode/ : VSCode-Umgebungseinstellungen  
    ├── docs/ : statische Dokumentation und Website auf Basis von MkDocs  
    ├── project-api/ : FastAPI-Backend inkl. Daten/Notebooks/Tools/Quellcode/Tests  
    ├── project-ui/  : React/Next.js-Frontend  
    ├── docker-compose.yml (integrierte Ausführung)  
    └── Makefile usw. für Automatisierungsskripte  
    
  • Das Frontend ruft die vom API-Server verarbeiteten Daten per HTTP ab, was für eine klare Trennung der Verantwortlichkeiten sorgt
    • Dem Frontend wird schwere Datenverarbeitung untersagt; stattdessen delegiert es per HTTP-Anfragen an das Python-basierte Backend-API
  • Jedes Python-Modulverzeichnis wird mit __init__.py klar gekennzeichnet
  • Innerhalb von project-api sind die Ordner src/app, notebooks, tools und tests getrennt organisiert

Wichtige Tools und Einstellungen

  • uv

    • Ein moderner Python-Paketmanager und Build-Tool von Astral
    • Erledigt die meisten Aufgaben wie Abhängigkeitsverwaltung, Erzeugen virtueller Umgebungen und Projektinitialisierung sehr schnell
    • pyproject.toml ist die zentrale Konfigurationsdatei, in der alle Metadaten und Abhängigkeitsinformationen zusammengeführt werden
    • Mit den Befehlen uv init, uv add, uv sync lässt sich die Projektumgebung schnell aufsetzen
  • ruff

    • Ein ultraschneller Python-Linter und Code-Formatter
    • Ein Tool, das isort, flake8, autoflake usw. integriert
    • Mit ruff check, ruff format lassen sich Linting und automatische Korrekturen ausführen
    • Grundlegende Unterstützung für den PEP-8-Coding-Style-Guide
  • ty

    • Ein von Astral entwickelter statischer Typprüfer für Python
    • In Kombination mit typing wirksam für statische Analyse und das frühe Verhindern von Bugs
    • Trotz früher Entwicklungsphase bereits stabil genug für den praktischen Einsatz
  • pytest

    • Das bekannteste Python-Test-Framework für Unit-Tests und erweiterbare Testumgebungen
    • Mit einfachen Dateinamensregeln und einem einzigen Befehl sind sofort Integrationstests möglich
      • Tests werden als test_*.py angelegt und mit uv run pytest ausgeführt
    • Knappe Syntax, reiches Plugin-Ökosystem
  • Pydantic

    • Eine Bibliothek für Datenvalidierung und Verwaltung von Umgebungseinstellungen
    • Lädt Konfigurationen auf Basis von .env-Umgebungsvariablen und validiert Typen
    • Mit der Klasse BaseSettings lassen sich API-Keys oder DB-URLs sicher verwalten
  • MkDocs

    • Unterstützt die einfache Erstellung statischer Websites und Dokumentation für Python-Projekte
    • Ansprechendes Design im Stil von Open-Source-Projekten lässt sich schnell anwenden
    • Auch die Integration mit GitHub Pages ist unkompliziert
  • FastAPI

    • Framework zum schnellen Aufbau von RESTful APIs
    • Vorteile sind automatische Validierung und Dokumentation, hohe Performance und einfache Integration mit Pydantic
    • Auf Basis von Starlette und Pydantic mit hoher Typsicherheit und Performance
  • Dataclasses

    • Eine Python-Standardfunktion, mit der sich datenzentrierte Klassen einfach definieren lassen
    • Durch automatische Erzeugung spezieller Methoden wird Boilerplate-Code stark reduziert

Versionsverwaltung und Automatisierung

  • GitHub Actions

    • Für project-api und project-ui werden jeweils separate CI-Pipelines eingerichtet
    • Bietet Workflows, die sich ideal für den Aufbau von CI-Pipelines auf verschiedenen Betriebssystemen eignen
    • Mit Docker-basierten Testumgebungen sind Tests in derselben Umgebung wie in der Produktion möglich
  • Dependabot

    • Automatisiert Updates von Abhängigkeiten und die Verwaltung von Sicherheits-Patches
  • Gitleaks

    • Ein Tool zur Verhinderung des Lecks sensibler Informationen wie Passwörter oder API-Keys und führt Sicherheitsprüfungen vor Git-Commits aus
  • Pre-commit Hooks

    • Ein Tool für automatisches Linting, Formatting und Sicherheitsprüfungen vor Commits
    • Zusammen mit ruff, gitleaks usw. sorgt es für konsistenten und hochwertigen Code

Infrastruktur-Automatisierung

  • Make

    • Unterstützt einen konsistenten Entwicklungs-Workflow mit Befehlen wie make test, make infrastructure-up
    • Sowohl im Projekt-Root als auch in project-api gibt es jeweils ein Makefile
  • Docker & Docker Compose

    • project-api und project-ui werden jeweils in getrennten Containern ausgeführt
    • Mit docker compose up --build -d lässt sich die gesamte App in einer Zeile starten
    • Das Dockerfile enthält die Installation von uv sowie den Befehl zum Starten der FastAPI-App

Fazit

  • Mit einer modernen Python-Entwicklungsumgebung wie dieser lässt sich ein effizienter und robuster Produktions-Workflow aufbauen
  • In Bereichen wie AI, Daten und Webentwicklung lassen sich viele Vorteile aus dem Wachstum des Python-Ökosystems und der Weiterentwicklung seiner Tools ziehen
  • Von Monorepo-Strukturen über Automatisierungstools, Linter und Typprüfer bis hin zu sofort nutzbaren Testumgebungen, Dokumentation und Infrastruktur-Orchestrierung lässt sich eine integrierte Entwicklungskultur umsetzen

6 Kommentare

 
howudoin 2025-07-18

Python bietet zwar eine Fülle an Bibliotheken und Frameworks, aber ein Nachteil ist, dass die Verwaltung von Paketversionen nicht besonders gut funktioniert und es häufig zu Konflikten kommt.
In der Tendenz sind die Vor- und Nachteile ähnlich wie beim Java von früher.

 
kylian 2025-07-18

uv, das auch im Artikel erwähnt wird, ist wirklich ein starkes Tool. Es ist nicht nur schnell, sondern kümmert sich auch um Versions- und Abhängigkeitsverwaltung ähnlich wie npm, deshalb stelle ich gerade komplett auf uv um.

 
wyatt216 2025-07-18

Aber heutzutage scheinen sich Versionsverwaltung und Konflikte mit uv und poetry größtenteils gelöst zu haben.

 
shakespeare 2025-07-18

Ist es auch dafür geeignet, das Ökosystem bis hin zu React und solchen Bereichen abzudecken?

 
wyatt216 2025-07-18

Die direkte Anbindung an React ist wegen der unterschiedlichen Sprachen in manchen Bereichen schwierig, aber je nachdem, was Sie möchten, dürfte manches möglich sein.
Persönlich denke ich, dass Frontend-Entwicklung mit Python kein Bereich ist, der besonders stark verbreitet ist.

 
GN⁺ 2025-07-17
Hacker-News-Kommentar
  • Wenn im Code bei fehlenden Umgebungsvariablen die Meldung „YOUTUBE_API_KEY oder YOUTUBE_CHANNEL_ID fehlt“ mit einem OR angezeigt wird, nervt das die Nutzer in einer Situation, in der ein OR gar nicht nötig ist. Es ist eine deutlich bessere User Experience, jeden Wert einzeln zu prüfen und klar anzugeben, welcher fehlt. Da der zusätzliche Entwicklungsaufwand praktisch null ist, würde ich das so empfehlen.

    • Das ist zwar eine Diskussion, die sich an kleinen Details festbeißt, aber ich finde, für solche Fälle eignet sich der :=-Operator (Walrus-Operator) perfekt. Zum Beispiel kann man direkt if not (API_KEY := os.getenv("API_KEY")): schreiben. Persönlich lasse ich bei internen Tools aus os.environ["API_KEY"] einfach den KeyError hochkommen. Auch das ist meiner Meinung nach hinreichend klar.

    • Noch besser ist es meiner Meinung nach, die Bedingungen einzeln zu prüfen und, wenn etwas fehlt, alles auf einmal anzuzeigen. So vermeidet man den Ärger, das Programm wegen einer fehlenden Variablen zu starten und danach gleich den nächsten Fehler für eine andere Variable zu sehen. Je nach Situation lässt sich der Aufwand nicht ganz vermeiden, aber wenn möglich, ist es besser, alles in einem Durchgang zu zeigen.

    • Am besten ist es, alle Umgebungsvariablen einzulesen und dann alle fehlenden gesammelt zu melden.

    • Man kann auch mit einem Boolean-Flag arbeiten und erst am Ende einmal exit(1) aufrufen. So lassen sich alle fehlenden Umgebungsvariablen auf einmal anzeigen.

    • Man kann auch mit exit("Missing ...") direkt eine Meldung ausgeben und mit Code 1 beenden.

  • Wenn du nach einem Tool suchst, das die Projektstruktur automatisch erzeugt, empfehle ich cookiecutter. Ich habe ein paar Templates, die ich oft benutze, darunter python-lib, click-app, datasette-plugin und llm-plugin. Verwendet wird es so: uvx cookiecutter gh:simonw/python-lib

    • Ich habe in Ruby etwas namens baker gebaut. baker kopiert kein Template-Repo, sondern erstellt eine Aufgabenliste mit den nötigen Schritten und kann manuelle Aufgaben (einen API key besorgen und konfigurieren) mit automatischen Aufgaben (uv init usw.) mischen. Man kann Markdown-Syntax, Ruby-String-Interpolation und Bash verwenden. Ich habe es gebaut, weil ich yml-basierte Configs gründlich leid war.

    • Das Tool, das gerade im Trend ist, heißt Copier. Details stehen in der Copier-Dokumentation.

    • Ich richte neue Projekte eher gern ein. Ich habe nicht unbedingt das Bedürfnis, so etwas zu automatisieren.

    • Ich finde, solche Strukturierungs- und Automatisierungstools passen heute eigentlich perfekt zu agentenbasierten LLM-Entwicklungs-Workflows.

  • Die Einschätzung, „Python sei menschenfreundlicher, weil es auf den meisten Unix-Systemen vorinstalliert ist“, ist etwas optimistisch. Sobald man über import json hinausgeht, landet man schnell in der virtualenv-Hölle. Wenn etwas unter Python 3.13.x auf Ubuntu 22.04 oder 24.04, Rocky 9 usw. laufen soll, sind venv, Container oder Versionsmanager am Ende Pflicht.

    • Selbst eine Standardbibliothek wie import json müsste man bei Sprachen ohne so etwas extra installieren, aber Python hat dank seiner Standardbibliothek anfangs eine hohe Produktivität. Für große Projekte reicht die Standardbibliothek natürlich nicht aus, aber ich habe tatsächlich mehrere produktive Programme nur mit der Standardbibliothek ausgeliefert und dabei keine Probleme mit Deployment oder Sicherheitsmanagement gehabt. Auch das Verwalten von venv ist nicht mehr so schwer wie früher, und die Paketmanager haben sich verbessert.

    • Eine meiner halb scherzhaften Theorien ist, dass Docker/Container sich zur Hälfte deshalb so schnell verbreitet haben, weil sie geholfen haben, die Python-Abhängigkeits-Hölle zu überwinden. Meine erste Python-Erfahrung war 2012 die Installation eines Python-Dienstes auf einem Server, und das war schrecklich: Abhängigkeits-Hölle, venv-Kommandos, schwer handhabbare Umgebungs-Setups. Auch mit pip, brew und in der macOS-Umgebung bin ich nur gegen Wände gelaufen, und sobald ich Python sah, habe ich einen Bogen darum gemacht. Aber in letzter Zeit fühlt sich Python dank uv selbst aus Sicht von Einsteigern deutlich besser an. uv init, uv add und uv run reichen schon.

    • Ich finde, man sollte immer virtualenv verwenden. Am Ende ist es nur ein einzelnes Verzeichnis, und inzwischen erscheinen sogar Warnungen, wenn man versucht, mit pip systemweit zu installieren. So schwierig wie früher ist das nicht mehr.

    • Es ist besser, unbedingt virtualenv oder Container zu verwenden. Selbst wenn sie sich unbequem anfühlen, verhindert man damit, dass Updates oder neue Bibliotheksversionen systemweit Auswirkungen haben.

    • Früher war oft nur Python 2 im System enthalten, und manchmal hing sogar das System selbst von diesem Python 2 ab, was es eher riskant machte.

  • Python wirkt auf mich zugleich geschwätzig und unzureichend. Wenn man etwas Einfaches machen will, zieht man entweder 500 Abhängigkeiten hinein, oder der Code wächst selbst für Kleinigkeiten schnell auf Dutzende bis Hunderte Zeilen an. Deshalb vermeide ich Python, weil es zu viel unnötigen Aufwand erzeugt. Mit Perl kann ich Dinge viel schneller und knapper erledigen, daher bevorzuge ich Perl. Python fühlt sich eher wie Programmieren um des Programmierens willen an als wie tatsächliche Arbeitserledigung.

    • Ich baue auch viele Projekte ohne Abhängigkeiten. Mit der Standardbibliothek und einer einzigen Datei kann man wirklich viel machen. Wenn Python installiert ist, kann man es direkt per curl herunterladen und ausführen. Ich habe zum Beispiel ein 2000-Zeilen-CLI-Tool für Geldverwaltung, plutus, das etwa 12 Standardmodule verwendet. Rund 25 % des Codes sind argparse für das Command-Parsing. Ich schreibe so etwas gern explizit, mit einer Zeile pro Parameter.

    • Du hast gesagt, Perl sei schneller und mächtiger als Python. Mich würde interessieren, welche konkreten Beispiele du dafür hast.

    • An Python ist bequem, dass man bei verschachtelten Datenstrukturen nicht groß nachdenken muss. Listen, Tupel und Dictionaries lassen sich frei mischen, und man greift mit einer einheitlichen Syntax darauf zu. Perl ist zweifellos cleverer und unterhaltsamer, aber gerade deshalb verknotet es mir leichter den Kopf, also ist es nicht meins. Python ist vielleicht etwas langweilig, dafür aber so klar, dass ich den Code auch in fünf Jahren noch verstehen kann.

    • Ich finde, Python ist schon allein mit der Standardbibliothek gut brauchbar.

  • Ich bin eigentlich ein Fan von Monorepos, aber in einem früheren Unternehmen ist daraus durch diese Arbeitsweise eine riesige, überladene Struktur geworden, an die niemand mehr heran wollte, weil man Angst hatte, versehentlich Code anderer Teams zu berühren. Das Kernproblem war weniger das Repo selbst als vielmehr, dass eine einzige requirements.txt für das ganze Repo verwendet wurde oder die Build-Skripte verheddert waren. Theoretisch sollte ein einmaliges Dependency-Update allen Code auf den neuesten, sicheren Patch-Stand bringen, aber in der Praxis konnte das niemand anfassen. Monorepos funktionieren nur dann gut, wenn eine Organisation stark von NIH geprägt ist, also wie Google alles selbst baut. Wegen solcher Erfahrungen schätze ich inzwischen eher Microservices, bei denen jeder Service zur Teamstruktur der Organisation passt. Siehe auch Conway's law.

  • Python ist die Sprache, die meinem geschriebenen Pseudocode am nächsten kommt. An jeder Stelle, an der ich im Kopf denke „das ist klar“, liefert Python tatsächlich eine intuitive Abstraktion. Da ich aus einem mathematisch geprägten Hintergrund komme, hat mich das sehr angesprochen. Natürlich mag ich inzwischen auch andere Sprachen, aber Python hat immer noch seinen Reiz.

    • Für Menschen mit mathematischer Denkweise macht eher OOP das Denken schwerer. Äquivalentes Schließen, bessere Lambdas, das Vermeiden von Nebenwirkungen und die Unveränderlichkeit von Daten sind wichtig. OOP ist unter den Paradigmen, die ich kennengelernt habe, dasjenige, das sich für mich am weitesten von der Mathematik entfernt anfühlt.
  • Ich baue meine Projekte fast immer nach demselben Muster auf. Es ist so ähnlich, dass es schon unheimlich ist. Ich frage mich, ob das Python-Ökosystem für Entwickler allmählich auf einen sehr ähnlichen Stil konvergiert. Früher dachte ich, meine Entscheidungen seien einzigartig, aber wenn am Ende alle dasselbe tun, frage ich mich, wo mein freier Wille geblieben ist. Es fühlt sich an wie bei beliebten Babynamen: Man hält seine Wahl für originell und stellt dann fest, dass sie in Wahrheit auf Platz 2 der Hitliste steht.

    • Solche Strukturen sind in Python schon seit zehn Jahren beliebt. Wahrscheinlich landen viele vernünftige Ingenieure nach reiflicher Überlegung ganz natürlich bei diesem Muster.

    • Es fühlt sich an, als läge das menschliche Ego wie eine Pilotwellen-Quantenwelle über das ganze Spektrum und würde sich daraus in Sein verwandeln. becoming-being, da muss ich lachen.

  • Es freut mich, wenn auch andere Python mögen. Ich habe ursprünglich Ruby bevorzugt, musste aber wegen Kundenanforderungen zwangsläufig Python verwenden. Früher war Ruby ziemlich langsam, doch während ich mich widerwillig in Python eingearbeitet habe, habe ich mich allmählich daran gewöhnt und inzwischen macht es mir durchaus Spaß. Bei der Verwendung von Make sehe ich einiges etwas anders: Wenn man überhaupt keine Abhängigkeiten nutzt, unterscheidet es sich kaum von einem Skript mit case-Anweisungen ... halb im Scherz gesagt, aber wenn ich sehe, wie wenig die heutige Generation mit Make vertraut ist, macht mich das etwas wehmütig. Ganz nach dem Motto: zu meiner Zeit.

    • Ruby hat eine viel schönere Syntax. Dass Python Scopes nur über Einrückung unterscheidet, ist nicht mein Stil.

    • Ich habe zuerst mit einem Skript auf Basis von case-Anweisungen angefangen und bin dann bei einem flachen Makefile gelandet. Ein Makefile ist standardisierter und leichter zu überblicken als irgendein zufälliges Skript.

  • Ich frage mich, wann man Dataclass und wann Pydantic Basemodel verwenden sollte. Wenn man ohnehin schon Pydantic benutzt, könnte man dann nicht einfach alles mit Pydantic vereinheitlichen? Ich überlege, ob es überhaupt noch Gründe gibt, bewusst Dataclass zu verwenden.

    • Das attrs-Projekt hat dazu einen sehr guten Vergleich. Es gibt den offiziellen Vergleich von attrs; natürlich ist er ein wenig voreingenommen, aber ich finde die Argumentation trotzdem schlüssig. Auch dieser Blog ist hilfreich.

    • Dataclass unterstützt keine Validierung verschachtelter Objekte. Deshalb ist dataclass besser für einfache flache Strukturen geeignet, die man etwa zur Übergabe von Funktionsargumenten verwendet. Das ist klarer, als zu viele Argumente einfach in einer Liste zu übergeben.

    • Wegen der Datenvalidierung bei der Erzeugung gibt es Performance-Einbußen. Es gibt deutlich leichtere und schnellere Alternativen wie msgspec.

    • Wenn man weder Validierung noch Serialisierung braucht, ist Pydantic eher unnötiger Overhead. Meine Faustregel ist: Wenn Serialisierung nötig ist, Pydantic, sonst dataclass.

    • Man kann mit TypeAdapter(MyDataclass) eine vorhandene Dataclass direkt verwenden; da frage ich mich, warum man überhaupt noch ein separates Pydantic-Modell anlegen sollte.

  • In letzter Zeit bin ich eher von Python auf andere Sprachen umgestiegen und damit zufriedener. Meine Gedanken zu Python habe ich in diesem Text zusammengefasst. Wenn ich künftig wieder Python verwenden sollte, würde ich auf jeden Fall uv, ruff und ty ausprobieren.

    • Ich selbst bin im Backend ebenfalls von Python zu JS gewechselt und habe seitdem viel Spaß damit. Ich stimme zu, dass Installation und Paketverwaltung bei Python kaputt sind. Für mich war async aber der größte Produktivitätsschub. Python hat zwar asyncio, aber es gibt mehrere konkurrierende Ansätze, ohne dass sich ein Standard wirklich durchgesetzt hat. Bei JS gefällt mir, dass alles auf einen Ansatz hinausläuft, das war für mich deutlich angenehmer. Auch viele kleine Punkte summieren sich am Ende zu einem großen Unterschied: Scoping über Einrückung in Python, Probleme mit relativen Pfaden bei Imports, die Objekt-Syntax in JS und andere Dinge fühlen sich angenehmer an. Siehe auch diese Erläuterung.