But under this frame, it appears that the developer's task involves prompt engineering. This is not the case.
Even if an agent generates 90% of the code, each and every diff is going to be in my review queue. Code readability of Python isn't an advantage during write; it's an advantage while reviewing. As an agent generates a piece of code, I will have to read the code, comprehend the code, and determine whether it does what I want. This is the other 10% of the task, and it's the crucial one.
Python is, thus, clearly superior to other languages in terms of ease of review.
> Code readability of Python isn't an advantage during write; it's an advantage while reviewing.
This is completely subjective though. I personally find that Python's lack of static types makes code very difficult to reason about. Yes, some devs will write decent comments and name things in a way that's easier to read, but most devs are lazy (myself included) and things get out of hand quickly.
But this is also a subjective opinion, and you could argue that I feel this way because I spend most of my time in TypeScript, Go, and Rust.
I would go even farther and say that static types are a tool designed specifically for a code reader.
When you're writing the code, you know what the types are, as you literally just created/wired/whatever them. Static types become a benefit only when you visit code without that fresh context. For instance, third party libraries are far easier to use when the interfaces are typed.
Yes, but:
a) they're a second class citizen, not guaranteed to be used in whatever niche of the python ecosystem you find yourself in and there's already an n+1 problem with multiple type checker written by third parties, rather than having 1st class language support tool that's consistent. You're not going to get it by default, you're usually going to have to do some configuration (and maybe bike shedding) to get it working;
b) they completely negate the idea of python being "easy to read", your code is now littered with `if TYPE_CHECKING:`, `Literal`, `TypeAliasType` and any number of workarounds needed to make your hints work out. Unfortunately the syntax was just not designed with typing in mind, and I think it shows;
c) the idea of "hinting" rather than enforced type checking means you have no guarantees that a type is what you need it to be, you have to do a lot of boundary work to make sure the edges of your code are coercing things to the right type. While I love pydantic and find it to be an excellent library, to me it's the kind of code smell you get in languages without strong typing. Also you're going to get a lot of spurious type errors along this path as well;
I will gladly use python's type hints, it's a whole lot better than nothing (IMHO better than typescript), but in it's current form it will always fall short of a language that was designed with strong typing in mind.
The idea of the Pydantic-as-code-smell hinges on the objective being type-safety throughout the codebase. It isn't the aim when an agent creates the majority of the internal logic.
The winning architectural approach: enforcement at the borders, but flexibility within. The agent uses Pydantic for validating FastAPI schemas and models for the database—those are the contracts that need validation. The internal logic the agent produces is subject to line-by-line analysis, rather than being inferred from type propagation.
That's the right way to do things. It isn't some sort of a compromise. There is a clear boundary between validated "external input" and internal logic. And you aren't counting on type inference to propagate across the codebase. You catch errors at the border, where they come into or out of your codebase.
Your criticism of the type system in Python is spot on. The problem is that it is an add-on. It isn't consistent. And a language developed from the ground up for type annotations will do a far better job. However, this isn't the general case for agent-generated codebases.
Good point about subjectivity; it depends entirely on what you are reviewing for.
Static typing holds more ground while making assumptions about contracts between components in huge codebases written by many developers. But in the realm of agents, it all boils down to a simpler question, will this particular function generated by an agent do the job that was requested? Line-by-line readability of Python suffices in this case, regardless of whether type annotations are used throughout.
The pragmatic approach would be to enforce type rules where needed (i.e., when working with Pydantic schemas or in FastAPI routes), while not applying any constraints within the code itself.
2. Explicit types give context, and if a project guidelines do not enforce type hints, as many don't, then it's hard to see what happens there.
3. Monkey patching and operator override -- I mostly stumbled upon that with "smart" types like ORM objects. Combined with 2. makes it very hard to review.
So I almost always had to download the change and review with IDE help. So it's not just code review anymore, it's manual testing.
> Python is, thus, clearly superior to other languages in terms of ease of review.
My experience has not been this. Dynamic languages make it harder to figure out things locally, unless someone has done the hard work of adding type hints.
Python has had type hints for like... Oh 11 years now. Just like C# has introduced var and quicker ways to write less, to the point it almost looks like JavaScript sometimes, but its because we can infer types pretty easily now. Rust has a nice system as well, forcing method signatures to declare types, everything is easier to infer from this.
Sometimes they are wrong (as they are more like a comment than a compiler directive).
My first task in any project was to figure out why devs don't have error highlighting on for bad types (often it's "it was red so we turned it off"), but good luck forcing others who don't do type hunting to start doing it when "it slows us down".
AI-unrelated tangent, but I think it's pertinent to your comment.
I come from a heavily Python background, professionally. I spent the entire first decade-and-change of my career using almost exclusively Python; I know it about as well as a person reasonably can (outside of scientific and ML Python, which I just never got interested in, but that's beside the point).
A year and a half ago I got a job doing Rust. At a surface level, it's about as far as you can get from Python in terms of ease of readability, but after 18 months I'm really reconsidering some of my points of view on the matter.
"Explicit is better than implicit," for example, is something I still strongly agree with, but my definition of "explicit" has shifted a lot in the past year. Seeing which guarantees are provided through mandatory, explicit, strong typing saves a lot of time over tracking down guarantees in MRs while reviewing Python code. If I see a signature as an `Arc`, for example, I immediately know that:
- It's thread-safe and memory-managed using reference counting (because `Arc` provides those guarantees);
- It's a type-erased object but is guaranteed to provide all the functionality from the `AudioInterface` trait (which, let's say, could be a supertrait of `AudioInput` and `AudioOutput` -- so it provides both of those);
- It uses runtime dispatching (since it's a `dyn` rather than a generic/`impl T` where `T: AudioInterface`)
I can choose to operate on it by reference with all the caveats that entails, or decide to either `Copy` or `Clone` it, depending on whether that's available for that type and if I can stomach the runtime cost.
All that to say -- Rust doesn't suck to review, relative to Python, in the long run. At first, yes, holy crap, it's such a huge cliff, and I can appreciate your point of view... but there's something to be said about having all this information surfaced as part of the language's syntax and semantics.
Python still has a special place in my heart, and I'd still use it over anything else if Rust isn't an option, but to echo a popular sentiment from other people who've made this migration, I don't know if I can go back to handwaving away whether or not something'll cause an allocation :)
Is it though? You assume the abstractions in Python are battle tested and you understand them. Usually people are relying on arbitrary libraries so unless you’re constraining libraries and those libraries have good review processes, it won’t be long until high level functions you’re reading are generated by LLMs to, so to review your LLMs use of other LLM generated functions you have to drop down a few levels and review at that level.
At some point that becomes less sustainable and looking at something with less abstraction assures you’re at least looking at a baseline source of truth, even if the volume is massive.
There’s going to be a whole world in the knowledge economy, not just software but everywhere, around validation and sign off of information that we’ve taken for granted as a cost prohibitive process where only the best options make it to high levels of function and maturity.
the trend is AI also does the code review. Too many anecdotes and studies showing AI is a better code review that a human and the models are just going to get better.
Whether we get better results if AI reviews Python or Rust I'm not sure. But I suspect Rust will win out as the training data likely has more content around Rust correctness and language usage than Python does.
You must have a low bar for human code review. I've seen that in practice too. But I've also been on teams that took code review very seriously, and frontier AI really doesn't come close to a good human code reviewer imo
They're complementary. AI reviewers are bad at spotting inappropriate architecture patterns and unnecessary verbosity, but they're very good at identifying various types of complex logic bugs and detecting mismatches between code docs and implementation. They add substantial value.
except this will go away. we will likely reach a point sooner rather than later (I think 2027) where it will be infeasible for humans to review the code. This will happen at startups first rather than big corps obviously and the engineers who design systems (dark factories) fully leaning into this will have a huge advantage. And yes there are exceptions to this and a play on the other side but the this is where the buck is going. at that point even Assembly becomes interesting.
Even if an agent generates 90% of the code, each and every diff is going to be in my review queue. Code readability of Python isn't an advantage during write; it's an advantage while reviewing. As an agent generates a piece of code, I will have to read the code, comprehend the code, and determine whether it does what I want. This is the other 10% of the task, and it's the crucial one.
Python is, thus, clearly superior to other languages in terms of ease of review.