Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The static vs dynamic language debate is decisively over and static has won. I called this out back in 2023, and I've only become more convinced since then.

Statically typed languages are easier for the reader because you can see the types and quickly jump to their definitions (or even just hover over them in some IDEs).

They're easier for the AI because they provide natural guardrails and feedback to guide it, as well as much more confidence to the programmer that the code does what it is supposed to. Rust even provides strong guarantees about correctness across threads, which is so helpful to multi-threaded code.

The fact that they run faster and use less memory is just icing on the cake.

Even just last year the AI could not handle the borrow checker well. Today I think it is better than me at handling tricky lifetime issues that ocassionally happen in multi-threaded Tokio code. I've been doing almost 100% Rust development over the last 3 years, and the experience is now very good. I don't write code by hand any more, nor do any of the 50 engineers where I work.

I imagine it does quite well with Go, since it's such a simple language. And Go is very readable, and compiles very fast. If you can afford the GC in your problem domain, it might be a good fit. You would have to be so careful with introducing concurrency, because it would be so easy to introduce race conditions that both the AI and human reviewer might miss. I haven't tried to use Go in anger yet with LLMs, so this is all just speculation.



Strong typing has clearly won.

However, verbose typing is likely a negative for LLMs.

Algorithms written in "pseudo-code", aka a higher level language without type information, are far more readable to a human, and thus likely an LLM too.

In regards to control flow and general concept of what code is doing, types provide very little info over well named variables. In fact they often impair understanding by breaking up logic with implementation details.

I'd be curious to see some experiments around this, but I'd guess strongly typed languages where the type information is mostly hidden/inferred would have better generation accuracy from a semantics perspective (and likely worse from a type safety perspective, but can be corrected on compile/retry)


> Algorithms written in "pseudo-code", aka a higher level language without type information, are far more readable to a human, and thus likely an LLM too.

What’s the basis of this claim? There are many many more lines of code LLM’s are trained versus pseudo-code.

Also I agree, anecdotally the self-correction is key benefit from static types. If there is a mistake, it is caught at compile time and not at runtime.


It seems clear to me from first principles.

Humans are trained on human language. LLMs are trained on human language.

Thus something that is easier for a human to understand is likely easier for an LLM to understand.

That higher level language with well named variables reads more comprehensibly than code:VERB with:PREPOSITION types:NOUN, intermixed:ADJECTIVE, stems:VERB from:PREPOSITION first:ADJECTIVE principles:NOUN too:ADVERB


For models as complex as these I'm not confident we can apply arguments from first principles; we could just as easily argue that type information is helpful, from first principles. What is much more useful is empirical evidence, and AutoCodeBench [1] found that LLMs are most proficient in Elixir (dynamic) followed by Kotlin (static), with Rust and PHP at the bottom. So it would seem like, as of publication, typing style doesn't really matter!

[1] https://autocodebench.github.io/


As far as the AI is concerned, it's more like

Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo

versus

Buffalo:PN buffalo:N Buffalo:PN buffalo:N buffalo:V buffalo:V Buffalo:PN buffalo:N

I think the second one makes much more sense.


In the rare case that all your concepts use the exact same descriptive word, you are probably right!

The majority of the time you can infer the type from reading well written code (to the extent that the shape of the type matters in the context of that piece of code)


If the type can be inferred by the reader it should be inferred by the type system and at least be available to the LLM as a query. But we're also talking about dynamic languages in which type cannot be inferred until runtime. What's the type of x?

x = y + z

Well that depends on the types of y and z, which themselves may depend on the types of other operands, which themselves may not be known until the program actually runs. All that inference takes a lot of thinking, which takes tokens, which cost money. Why not just write the types down? Although we call these things "inference engines" they're really pattern matching explicit tokens, so it's better to actually write down the types so they can be pattern matched than to figure them out at inference time.


You are basically rehashing the false beliefs of the codeless programming camp. Human language that is 99% correct is a standing ovation for a speech writer while it is paying a cyber ransom as the software maker.


Strong typing is not a synonym for static typing, it refers to a different aspect of type safety.

Static typing is, roughly, where variables and expressions have fixed types that can be determined ahead of execution. Strong typing means the language doesn't offer implicit type conversions. Python is dynamically typed, i.e. not statically typed, and strongly typed. (Ignoring its type annotations feature, of course.)


I think Java shows that too much naming is a horrible idea even if there is a good type layer. It's exceptionally easy to have homonyms and other problems that feed errors in LLMs and if every mix of nonsense runs that's all the worse. Add to that attempts to put many English words together and there are no way points left. Everything is an exhausting essay by Dickens where two very long sentences are subtly different (to look at but not in results.)


This is a lot of speculation with absolutely nothing backing it.


> types provide very little info over well named variables

Types guarantee invariants at compile time, adding type info to a variable name is just a prayer that the next human or robot will enforce the invariants with respect to that type when it matters. This is like saying you don't need a saw stop because you should just avoid sticking your hand in the saw blade.


If static has won, why are dynamic languages more popular now (even since 2023).

Comically, I’ve witnessed people say this since the 90s.

For me, I don’t care about static because dynamic is easier. For the very few conditions where it matters, I’ll use static. Otherwise I like the simplicity of dynamic languages, especially python. IDEs provide support and jump to definitions in dynamic languages, too.


I mostly use C++ at work, but I love ruby for its expressiveness and use it all the time for small to medium sized scripts where performance doesn't really matter.

However I've definitely noticed that the larger a ruby program gets, the more likely I am to manually add type checks. Beyond a certain size I simply can't fit everything in my head at once. Even though these checks are still done at run time, debugging is much easier when I can find out ASAP when something is not what I expected it to be.

People often say "that's what tests are for!". But if I'm spending time writing tests that verify the types are correct, I see that as a waste of my time because that's exactly the kind of thing that a compiler could do for me in a statically typed language.


I was on team dynamic for a long time and have moved to team static.

For any long-lived code base, dynamic piles up invisible problems over time.

It's great for short-lived throwaway stuff, but as soon as you know you'll be maintaining a large code base for a long time, the "easier" part of dynamic actually becomes harder than just spelling stuff out.

It's obviously a trade-off and not everyone agrees, but that's my personal experience having run large eng teams for both types.


This is exactly my experience as well.

If it were up to me, I'd start out by using the AI to port a Python web app to a compiled language like Go. Then we could maintain that going forward instead.

In reality, a Python code base is maintained by Python programmers, most of whom would vote against such a change.


I think most people agree with you -- that's why. Also because I'd say most programmers don't care much about maintainability or quality.

I personally find that AI writes better Scala than Python.


Before AI, I used to say types reduce quality by wasting dev time that could've been spent on testing. They may also encourage overly complex code.

With AI, I don't know. If we're forced to use types, the AI does that work for me, but that added verbosity can't be good for it.


> could’ve been spent on testing

To me, this argument sounds similar to “making salads to eat reduces health because they waste time that could’ve been spent on working out” - it assumes the time savings will be spent on working out and not on sitting on the couch.

In the case of SWE, any time saved will always be spent on “2 more features we think we can ship this sprint if we deprioritize these pesky ‘additional tests’ tickets - don’t worry, we’ll circle back to those next sprint of course”


When I was working on large python code bases, a full 50% of my time was spent dealing with tests failing because:

* someone assumed duck typing where it wasn't or the inverse. Or changed the assumed interface of a duck.

* somewhere doesn't handle None properly even though it's a valid agrument.

* making sure every function properly checked that the input parameters were valid and generated a meaningful error message

* making sure side effects of the ducks and the meta-bs didn't break other things

AKA all type related nonsense.

With go, and even more-so rust, the time the compiler saves me by obviating all that type related testing is far larger than the time spend dealing with the type related testing. Even when you factor in the extra time twiddling with types adds to the coding. And don't get me started with the whole "deal with type bullshit in dynamic languages" mess that occurs when a bug slips through into prod....


It's sort of an open question how LLM authored code will stand up over the years/decades. It's a lot of code, but maybe the capabilities for reading lots of code will also improve, or maybe the way human shops can snarl up in giant balls of mud that are terrifying to work on, the rate of change for a given system authored by LLMs will show an S-curve shape rather than staying responsive to changing conditions or whatever.


Programmers, at least the mass market programmers, have optimized for ease of writing code for many decades. It's a natural urge, but in the long run for successful applications, it's more important that code is readable and maintainable. As the years go by the initial cost of tapping out the code becomes more and more negligible.


They are? Nobody writes JavaScript anymore. Everything is Typescript. Even PHP and Python have sort of back ported type annotations. What major language is still purely dynamic these days?

I know the whole dynamic vs static typing is an age old flame war, but it sure seems that static is winning to me.


> The static vs dynamic language debate is decisively over and static has won

I wouldn't be so fast. It wasn't that long ago that the dynamic zealots were declaring victory. And before that the static zealots. And before that the dynamic zealots. Going back decades.


I was there, I was one of the dynamic zealots.

But at least I can't imagine how this trend reverses course now.


> I can't imagine how this trend reverses course now.

That's exactly what those/you dynamic zealots were telling people like me 15 years ago :-)

But yes, I largely agree with you. My $0.02 is that the larger pendulum over the decades is a trend towards static but being less and less visible to the end user, increasing the static typing while reducing the boilerplate and overhead on the part of the developer. Think things like type inference and the sort. Even as a static typing fan I don't miss the days when literally every variable needed an explicit type annotation.


Could you have imagined the trend reversing when you were a dynamic zealot? Maybe the lesson is that it really isn't that important. As the pendulum swings back and forth between trends, software doesn't change that much. It is just a bunch of small trade-offs.


That kind of back-and-forth dynamic ideally ends up with something combining the best parts of both approaches.

Is anything like that happening?


I agree and I believe that's what has been happening. In another post I mentioned an increase of type inference & related technologies. For instance, one annotates the type signatures for inputs & outputs of a function as well as any tricky constructs for readability, but doesn't bother with "int x = 5".

Reduces manual boilerplate and visual noise while retaining static typing semantics.


I find it does not do very well with go at all. I speculate that it's partly because go is going to be a language you find a lot of concurrent programming happening in. And sure enough, I find even the best claude is nearly useless at anything beyond copy-pasting examples out of the docs for goroutines.

My own experience with agents, I'd summarize as "the more the world model (which the LLM does not have) is not concretely represented by the text, the worse LLMs are at it."

So it's _great_ at HTML, CSS, markdown, and most cursory-inspected English. Good at javascript. OK at most languages. Then very bad at concurrent programming and closely-inspected English.

I also don't think your top-line conclusion is right at all. I'm quite the opposite opinion. The types "working out" does not actually give me hardly any conviction that the code actually works. And notably, LLMs seem good at making types work out (they're in the text!) but then still have code that's not actually at all right (for the world model).

I also find that types are not worth the often COPIOUS amounts of boilerplate that comes with them. Some of the worst code I've seen is using reflection to make something happen that would otherwise barely be metaprogramming in Python or Ruby.

But that's not to say types are useless. I just think rigorous static typing is not worth it. My current favorite way to program is Python, with an enthusiastic use of type hints, enforced by a good type checker (pyright). It gets you 99% of the benefits of traditional static typing, but you can also just tell the type checker to just look the other way for a moment if you're going to commit a dynamic typing.


my experience with elixir is that if you tell the LLM "let it crash" it is productive. If you don't tell it this it hides errors and writes crappy tests. So, maybe it is go and not concurrency programming.


Hard disagree, LLM's benefit from jacking in to the powerful nREPL dynamic languages in the Lisp family like Clojure, letting the agent manipulate the code in unprecedented ways.


"manipulate the code in unprecedented ways" is pretty vague and of uncertain value, can you elaborate?


You need to understand the basic principles of how Lisp REPL operates. Simple example - if you're building a web scraper in Clojure, you can connect to the browser and "poke" through elements interactively, without reloading, without compiling, without losing the state.

Now imagine the same principle works with backend services, e.g. we've enabled nrepl endpoint in our staging k8s service, we can modify the behavior dynamically, like adding a new route, for that we'd just need to connect to the REPL, write something like `(POST "/v1/new-effing-route" request ...`, eval it and voila. We don't have to re-deploy, recompile, even save that code - it would just work, like magic.

Now imagine giving this ability to an LLM. It won't have to guess, it won't have to go into write/compile/run/restore-the-state/try loop - it knows what's available, what can affect the behavior of the system, etc. It works surprisingly well and saves tons of time and tokens. Kids who have not tried that, have zero idea how great that is.


You explained the magic behind this better than I ever could. But hey, I'm one of the nuschool kids on this, and we want this great language to breach escape velocity too!


Ah, well, "magic" probably not the best word here, to be honest. I find Clojure to be the absolute opposite of witchcraft - everything is pretty transparent, there's no magic encapsulated behind layers and layers of abstraction. Data is just data. Functions do what they say. The macro system is explicit. The REPL makes every intermediate step inspectable.

The irony is that Lisp looks cryptic to newcomers precisely because there's almost zero syntactic sugar hiding the structure. Once you adjust, you realize the "weird syntax" is actually the absence of magic - it's the parse tree, exposed directly. Alas, people prefer sugar flavored lies instead of "inconvenient" truth. I was pretty much the same - wasted years of my life, circling around shit that was all about "magic". At some point, your mind just can't take it anymore - it wants "plain & stupid". Because when shit just works - it doesn't feel that stupid anymore.


The LLM can evaluate any chunk of code at any time and see the result/errors. It lets it iteratively build solutions up, like how a human does. It also lets it verify that everything works before saying “done!”

It can even use REPL access to investigate bugs, cause it can run whatever inputs it wants on whatever functions it wants. It can tweak functions and retry stuff. It’s really ridiculously cool. AI programming with clojure is nuts. It can solve WAY harder problems, including with libraries and domains it’s never seen before, cause it can struggle through em just like a person would!


Agree 100%.

In the early days (before Claude Code mastered Rust,) I would get into this annoying pattern where Claude used different names for variables between tests and implementation, get confused, and then more times than not, would change the implementation to match the test (which was not written first--was not doing TDD and thus not the behavior I wanted.)

Static languages prevent that. I've had great success with Claude writing Rust, and I think it's an excellent language for LLMs not just for low level work, but for production-grade code of all types (I see rust as better aligned to compete with C++, Java, and C#.)

I've also had great success with Claude writing C#. Using Claude, I've built C#/.Net in Linux, deployed in Windows (via Visual Studio) with Claude Code running in WSL, and it's been a great experience all around.


LLMs are amazing at golang. They seem to have great training in the k8s world, so writing custom controllers and operators takes minutes instead of days now.


> I called this out back in 2023

People have been "calling this out" for decades. Yet the most productive languages are still dynamic/strongly typed.


oh yeah, static has won so much that people are now programming with a language where every possible string forms valid programs.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: