Ich kann
@Marco01_809 nur zu 100% zustimmen.
Ausserdem ist der Startbeitrag ohne konkretes Beispiel auch nicht besonders nützlich.
Micke schrieb:
Sondern der Umstand, dass die Sprache offensichtlich in einer problematischen Sackgasse gelandet ist, aus der sie sich nicht anderes befreien kann.
Hast Du ein Beispiel wo eine Sprache in einer "problematischen Sackgasse" ist, die nur durch ein Redesign zu lösen ist?
Was ist in Deiner Definition überhaupt ein Breaking Change?
Streng genommen kann schon die Einführung eines neuen Keywords (z.B. "async" und "await") ein breaking change sein, weil es in existierendem Code Bezeichner mit genau diesem Namen geben kann. Andererseits werden diese Konflikte selten sein und lassen sich relativ leicht beseitigen. Ändert man aber z.B. die Semantik von Datentypen (so geschehen z.B. in Lua wo der Typ "Number" seit Lua 5.3 zwischen Integer und Double "Subtypen" unterscheidet) kann das erhebliche Seiteneffekte haben, die auch nicht immer offensichtlich in existierendem Code zu finden sind.
Für mich ist das abschreckende Beispiel von Breaking Changes immer noch der Wechsel von Python 2.x auf Python 3.x. Python ist eine Sprache deren Wert vor allem im großen Ecosystem an Libraries liegt, und der Übergang von Python 2.x nach 3.x hat in fast jedem Python Projekt über viele Jahre für erheblichen Mehraufwand geführt, und der Übergang ist noch immer nicht abgeschlossen. Mittlerweile ist zwar jedes relevante Python Paket kompatibel mit Python 3, aber viele Projekte fangen nun an, alte Python 2 Abhängigkeiten/Workarounds auszubauen.
Bei Python hat es sich vermutlich am Ende trotzdem gelohnt, Python 3 ist in vieler Hinsicht besser und zukunftstauglicher als Python 2.
Natürlich entwickeln sich Sprachen weiter (die meisten heutigen C-Programmierer werden ein "K&R Style" C Programm kaum noch lesen können), es gibt aber viele Wege das "verträglich" zu lösen.
Eine Möglichkeit ist z.B. neue Versionen der Sprache auf Modulebene zu aktivieren.
Z.B:
HTML:
<script type="module">
import {mq_start} from './mqclient.js';
await mq_start();
</script>
Lade ich hiermit ein ES6 Modul. Da sich ES6 Module und "klassiches" Javascript einen globalen Namensraum teilen kann ich trotzdem in Kontrollfluss zwischen den Welten umherspringen.
In C/C++ ist das ABI (Application Binary Interface) zwischen verschiedenen Versionen der Sprache kompatibel, d.h. Calling Conventions, Registerbelegung, etc. bleiben gleich. Daher kann man über Compilerdirektiven (z.B. -std=c99 beim GCC) den verwendeten Sprachstandard bestimmen und trotzdem die Module später noch miteinander linken.
Über solche Mechanismen können sie Sprachen weiterentwickeln ohne dass man existierende Codebasen zwingend überarbeiten muss.
Nur erfordert dass viel Planung und z.B. bei Sprachen wie C/C++ auch Abstimmung in Standard Gremien. Man kann also nicht einfach mal so "Ad-hoc" ein neues Feature einführen.