2025 in Review
I used Claude Code to analyze my git repo contributions from 2025. The numbers are what they are: 9,435 commits across 75+ repositories. That averages to about 26 commits per day, every day, for a year. Whether that's a lot or a little depends on how you count—plenty of those are dependency updates from Renovate, and plenty more are small fixes. But it's a reasonable proxy for the kind of year it was: busy.


The Year of Types
If there's a theme to 2025, it's types. I've been pushing TypeScript for years, but only really started taking Python type hints seriously in 2024, prompted largely by Reactivated. We've done a lot of work at thelab to standardize our tooling around Reactivated and the Wagtail CMS. This combination lets us unify our templating—server-side and client-side—to just be React, eschewing weakly-typed Django templates entirely. Even better, it lets us type check our code, even data structures from Wagtail StreamFields and blocks, all the way from the database to the front-end templates.
That, combined with the improved generics in Python 3.12, triggered us to finally take Python type hints seriously. As a result, all but one of our Python projects now have full strict-mode type coverage via mypy.
The tooling caught up too. We migrated from black and flake8 to ruff across all Python projects. It's faster, it's simpler, and it does both formatting and linting in one tool. We also moved from Poetry to uv for Python package management. Poetry was a great tool and I still like it—uv is just a little better. The biggest subtle-benefit we found was the ability to use uv in your shebang line and define inline script dependencies. No more creating a virtualenv just to run a one-off script.
Another tool I came to love in 2025 is mise. It's already become our standard for defining tooling versions, replacing pyenv and nvm. And it's started replacing Make as a task runner on a few projects too.
On the TypeScript side, we continued using io-ts and fp-ts for runtime type validation. Zod gets more attention these days, but we've been so happy with io-ts that I've seen no reason to consider switching. We also forked and started maintaining ts-to-io, which converts TypeScript types into io-ts codecs. When your API responses are validated at the boundary, you can trust the types throughout the rest of your code.
Open Source Highlights
One new open-source library that I'm excited about right now: thelab-typing. thelab-typing includes some utilities for building strongly typed APIs in Django, inspired much by FastAPI. We've started replacing Django REST Framework with it and have been very happy with the results so far. DRF served us well for years, but the lack of proper typing made it increasingly painful as we increased type safety throughout the rest of our codebases. The new approach gives us a single source of truth for API contracts, for both the request and response data shapes. We can then generate TypeScript types, io-ts-codecs, etc from the same Pydantic models we use to validate/serialize the API data—true cross-network type-safe bliss.
Looking Forward to 2026
Some things I'd like to improve or see more of:
More newtypes, fewer primitives. We still pass around a lot of model IDs and other not-really-a-primitive types as plain integers and strings. We've started introducing newtypes and found it extremely useful for catching bugs related to mismatching these types. For example, accidentally pass a ProductID where an OrderID was expected. Even better would be making those newtypes traverse from Python to TypeScript automatically (which we've started to do by adding support for in ts-to-io).
More Result types. We have a Result type implementation in thelab-typing, but it's fairly new and not widely or consistently used across our projects. Encoding success and failure in the type system instead of relying on exceptions makes code easier to reason about.
More adoption of gradual typing in Python. The tooling is there—mypy, pyright, basedpyright—and the benefits compound over time. Every # type: ignore is tech debt. Every properly typed function is documentation that can't get out of date. Library authors especially—looking at you Django & Wagtail—should absolutely be adopting type hints at full-steam.
Less churn in JavaScript bundlers and meta-frameworks. I don't need a new build tool every six months. I need the current one to keep working.
Continued progress on AI tooling. Programming with LLMs gets most of the attention right now—they absolutely are amazingly powerful tools to pair program with. But they also have a ton real value in the realms of code review, research, and debugging. I'm hoping the tooling in these areas continues to improve.