Python Development Shifts: Type Checking, Rust ORMs, and Data Access

Original Title: #476 Common themes

The Python Bytes podcast, in its 476th episode, dives into the evolving landscape of Python development, revealing subtle yet significant shifts in tooling and architectural patterns. Beyond the immediate news, the conversation highlights a recurring theme: the tension between immediate convenience and long-term maintainability, particularly in the realms of type checking and data persistence. This discussion is crucial for developers seeking to build robust, scalable applications and avoid the pitfalls of technical debt. Anyone invested in the future of Python tooling, from individual contributors to team leads, will find strategic advantages in understanding these underlying currents.

The Shifting Sands of Type Checking: From Mypy's Grip to Ty's Embrace

The conversation opens with a deep dive into the world of Python type checking, specifically the migration from mypy to ty. While ty promises significant speed improvements due to its Rust core, the real insight lies in the why behind this shift and the downstream implications for library maintainers and end-users. As Sebastian Ramirez's projects, including FastAPI and SQLModel, adopt ty exclusively, it signals a growing maturity and readiness for this alternative. Tim Hopper's blog post, "Migrating from mypy to ty: Lessons from FastAPI," offers a practical roadmap, but the underlying consequence is the increasing burden on library authors to support multiple type-checking ecosystems.

The friction arises from differing conventions and warning thresholds between ty and mypy. Brian Okken shares a personal anecdote about pytest-check, where ty flagged a potential issue that mypy overlooked, and conversely, ty sometimes flags mypy's ignore comments as unnecessary. This creates a complex balancing act for library developers. As Okken puts it, they "work for everyone's type, all the type checkers because whatever anyone may choose will become applied to your usage right in their projects." This means that a seemingly small tooling change can cascade into significant maintenance overhead, forcing developers to choose between supporting a wider audience and managing increasing complexity. The delayed payoff for adopting ty is clear: faster CI runs and potentially fewer subtle bugs, but the immediate cost is the effort of migration and the ongoing need to navigate the nuances of different type checkers.

"It's just, it's a struggle, and if you create a library to your point, then you've got to put yourself in the situation where like you don't have to work for a type checker that you choose, you work for everyone's type, all the type checkers because whatever anyone may choose will become applied to your usage right in their projects."

-- Brian Okken

This dynamic highlights how conventional wisdom--sticking with the established tool (mypy)--can fail when extended forward. The pursuit of performance and developer experience, embodied by ty, forces a re-evaluation of this status quo, revealing that the "obvious" solution of using the most popular tool might not be the most sustainable in the long run.

Oxyde ORM: The Promise of Rust-Powered, Django-Inspired Data Access

The discussion then pivots to Oxyde ORM, a type-safe, Pydantic-centric, asynchronous ORM with a Rust core, explicitly drawing inspiration from Django's API. While Oxyde is a young project, its performance metrics are striking, significantly outperforming established ORMs like Django, SQLAlchemy, and even SQLModel in raw request per second. This isn't just a marginal improvement; it's a near doubling of Django's performance. The implication is that by leveraging Rust for its core SQL generation and execution, Oxyde sidesteps Python's Global Interpreter Lock (GIL) for computationally intensive tasks, offering a tangible performance boost for data-intensive applications.

Michael Kennedy emphasizes that Oxyde is "async from the ground up," a critical distinction for modern Python development. This "async-first" approach, combined with Pydantic v2 models for validation and serialization, presents a compelling package. The "Django-style API" with familiar Model.objects.filter() syntax offers a low barrier to entry for those accustomed to Django, while the underlying Rust implementation provides the performance benefits. The oxyde-admin component further mirrors Django's administrative interface, promising zero-boilerplate setup for managing data.

However, the "hidden consequence" emerges when considering the project's youth. While the performance gains are enticing, the API is subject to evolution. This means that adopting Oxyde now, while offering immediate performance advantages, carries the risk of future breaking changes. The long-term payoff lies in building applications that are not only faster but also more resilient to performance bottlenecks as they scale. The conventional wisdom of using mature, albeit slower, ORMs might falter here, as Oxyde suggests that embracing newer, more performant technologies can yield significant competitive advantages, provided one is willing to manage the inherent risks of early adoption.

"Oxyde ORM is a type-safe, Pydantic-centric asynchronous ORM with a high-performance Rust core."

-- Michael Kennedy

The Raw+DC Pattern: Embracing Stability and Performance in Data Access

Michael Kennedy then introduces his "Raw+DC" (Raw Data Classes) database pattern, a retrospective analysis that challenges the reliance on ORMs as the primary data access layer. His core argument is that ORMs, while convenient, introduce dependencies that can become liabilities. Using Talk Python training as a case study, he illustrates how migrating from MongoEngine (an ORM he previously used) to a pattern that directly interfaces with the database driver (PyMongo in this case) and returns rich data classes, resulted in a significant performance increase--nearly doubling request rates--and a reduction in memory usage.

The "hidden consequence" of traditional ORMs, as Kennedy explains, is their potential for obsolescence and the difficulty in adapting them to modern paradigms like asynchronous programming. He recounts how MongoEngine was not amenable to an async rewrite, forcing a re-evaluation. The "Raw+DC" pattern, by contrast, relies on the stable, foundational database driver, minimizing external dependencies and offering greater control. Furthermore, he posits that AI models are better trained on foundational knowledge like raw SQL or MongoDB queries than on specific ORM syntaxes.

"The web app is much faster using Raw+DC. Plus, this was part of the journey to move from 1.3 GB memory usage to 0.45 GB."

-- Michael Kennedy

This approach offers a delayed payoff: increased application stability, better performance, and improved compatibility with AI tools. The immediate discomfort comes from abandoning the familiar ORM abstraction. However, the long-term advantage is a more robust and performant system, less susceptible to the whims of third-party library maintenance. This directly challenges the conventional wisdom that ORMs are always the best choice for data access, suggesting that a more direct, grounded approach can yield superior results over time.

Key Action Items

  • Immediate Action (Next 1-2 Weeks):

    • Evaluate ty alongside your current type checker (mypy) on a small, non-critical project.
    • Experiment with Oxyde ORM on a new, small service to gauge its performance and API suitability.
    • Review your application's data access layer for potential dependencies on older or less actively maintained ORMs.
  • Short-Term Investment (Next 1-3 Months):

    • If performance is a bottleneck, consider a Proof of Concept migration of a critical data access path to Oxyde ORM or a "Raw+DC" pattern.
    • For library maintainers, begin the process of adding ty support alongside mypy, accepting the dual ignore comments for now.
    • Explore the "Typeshedded CPython docs" for a deeper understanding of built-in Python functions with type annotations.
  • Medium-Term Investment (3-12 Months):

    • For projects with significant data access needs, plan a phased migration away from traditional ORMs towards more stable, performant drivers and data class patterns. This requires careful planning to manage the immediate discomfort of refactoring.
    • As ty matures, consider dropping mypy support for new projects or libraries if the signal-to-noise ratio with ty proves superior.
  • Long-Term Strategic Advantage (12-18+ Months):

    • Develop internal expertise in both asynchronous Rust-based libraries (like Oxyde) and foundational data access patterns (like "Raw+DC") to build applications that are both fast and resilient.
    • Advocate for and implement type-checking strategies that prioritize long-term maintainability and performance, even if it requires initial effort.
    • Stay informed about the health and direction of critical open-source projects (like those in the Encode ecosystem) and have contingency plans for potential maintenance gaps.

---
Handpicked links, AI-assisted summaries. Human judgment, machine efficiency.
This content is a personally curated review and synopsis derived from the original podcast episode.