Zum Inhalt springen
CASOON

Wie LLMs Sprachdesign beeinflussen: Code für Mensch und Maschine

Warum TypeScript besser funktioniert als JavaScript und Rust besser als C++

11 Minuten
Wie LLMs Sprachdesign beeinflussen: Code für Mensch und Maschine
#LLM #Sprachdesign #TypeScript #Rust
SerieSprachtheorie für Praktiker
Teil 4 von 6

Claude Code, Mistral Vibe, GitHub Copilot, Cursor – inzwischen arbeiten die meisten Entwickler mit KI-gestützten Coding-Agents. Was dabei auffällt: Manche Sprachen funktionieren deutlich besser als andere. TypeScript produziert zuverlässigere Vorschläge als JavaScript. Rust schlägt C++ bei komplexen Aufgaben. Python mit Type Hints übertrifft dynamisches Python.

Das ist kein Zufall – es liegt am Sprachdesign. Und je leistungsfähiger die Agents werden, desto stärker fällt der Unterschied ins Gewicht.

Was LLMs „sehen”

Ein LLM verarbeitet Code als Token-Sequenz. Es hat kein Verständnis von Speicherlayout, Laufzeitverhalten oder Compilierung. Was es kann: Muster erkennen, Kontext nutzen, wahrscheinliche Fortsetzungen vorhersagen.

Dabei helfen bestimmte Eigenschaften:

Explizite Typen als Kontext

// Wenig Kontext
function process(data) {
    return data.map(x => x.value);
}

// Viel Kontext
function process(data: Order[]): number[] {
    return data.map(order => order.total);
}

Im zweiten Fall weiß das LLM:

  • data ist ein Array von Order
  • Die Rückgabe ist ein Array von number
  • order.total existiert und ist numerisch

Die Typen liefern Kontext, den das LLM für bessere Vorhersagen nutzt.

Konsistente Syntax

Sprachen mit konsistenter Syntax sind vorhersagbarer:

// Rust: Einheitliches Pattern
let user = get_user(id)?;
let orders = get_orders(user.id)?;
let total = calculate_total(orders)?;
// C++: Viele Wege zum gleichen Ziel
auto user = get_user(id);
if (!user) return std::nullopt;
auto orders = get_orders(user->id);
if (orders.empty()) throw std::runtime_error("No orders");
int total = 0;
for (const auto& order : orders) total += order.value;

Je mehr Varianten möglich sind, desto unsicherer wird das LLM über die „richtige” Fortsetzung.

Lokale Verständlichkeit

// Alles Wichtige ist lokal sichtbar
func ProcessOrder(order Order, db *Database) (Receipt, error) {
    user, err := db.GetUser(order.UserID)
    if err != nil {
        return Receipt{}, err
    }
    // ...
}

Versus implizite Kontexte:

# Was ist self.db? Welche Methoden hat es?
class OrderProcessor:
    def process(self, order):
        user = self.db.get_user(order.user_id)

Go’s explizite Parameter machen den Kontext für LLMs sichtbar.

Sprachen im Vergleich

TypeScript vs. JavaScript

TypeScript gewinnt deutlich:

AspektJavaScriptTypeScript
TypenImplizit, zur LaufzeitExplizit, zur Kompilierzeit
AutovervollständigungGeratenPräzise
FehlerLaufzeitSchon beim Schreiben
LLM-KontextWenigViel

Beispiel:

// LLM weiß genau, was erwartet wird
interface CreateUserInput {
    email: string;
    name: string;
    role: 'admin' | 'user';
}

function createUser(input: CreateUserInput): Promise<User> {
    // LLM kann korrekte Implementierung generieren
}

Rust vs. C++

Rust’s Stärken für LLMs:

  1. Ownership ist explizit: &, &mut, ownership im Typ sichtbar
  2. Keine Header-Files: Alles in einer Datei sichtbar
  3. Pattern Matching: Exhaustiveness-Prüfung macht Fälle explizit
  4. Einheitliche Fehlerbehandlung: Result<T, E> überall
// Alles relevant ist im Scope sichtbar
fn transfer(from: &mut Account, to: &mut Account, amount: u64) -> Result<(), TransferError> {
    if from.balance < amount {
        return Err(TransferError::InsufficientFunds);
    }
    from.balance -= amount;
    to.balance += amount;
    Ok(())
}

C++ dagegen:

  • Header und Implementation getrennt
  • Implizite Kopien vs. Moves
  • Exceptions vs. Error Codes vs. std::expected
  • Makros, Templates, Concepts – viele Abstraktionsebenen

Python mit Type Hints

Der Unterschied ist messbar:

# Ohne Hints - LLM rät
def calculate_discount(order, customer):
    pass

# Mit Hints - LLM weiß Bescheid
def calculate_discount(
    order: Order,
    customer: Customer
) -> Decimal:
    if customer.tier == CustomerTier.GOLD:
        return order.total * Decimal("0.15")
    return order.total * Decimal("0.05")

Design-Patterns für LLM-freundlichen Code

1. Descriptive Naming

// Schlecht
const d = getData();
const p = process(d);

// Besser
const userOrders = fetchUserOrders(userId);
const orderSummary = calculateOrderSummary(userOrders);

2. Kleine, fokussierte Funktionen

// LLM kann jede Funktion einzeln verstehen
const validated = validateInput(raw);
const normalized = normalizeData(validated);
const enriched = enrichWithMetadata(normalized);
const result = persistToDatabase(enriched);

3. Explizite Konfiguration

// Implizit - LLM muss raten
const client = new ApiClient();

// Explizit - LLM kennt die Optionen
const client = new ApiClient({
    baseUrl: 'https://api.example.com',
    timeout: 5000,
    retries: 3,
    headers: { 'Authorization': `Bearer ${token}` }
});

4. Discriminated Unions für Zustände

type RequestState<T> =
    | { status: 'idle' }
    | { status: 'loading' }
    | { status: 'success'; data: T }
    | { status: 'error'; error: Error };

// LLM kann alle Fälle korrekt behandeln
function render(state: RequestState<User>) {
    switch (state.status) {
        case 'idle': return <Button>Load</Button>;
        case 'loading': return <Spinner />;
        case 'success': return <UserCard user={state.data} />;
        case 'error': return <Error message={state.error.message} />;
    }
}

Die Rückkopplung

LLMs beeinflussen nicht nur, wie wir Code schreiben, sondern auch Sprachdesign selbst:

Rust: Fehlermeldungen als Kontext

Rust investiert seit der 2024 Edition gezielt in selbsterklärende Fehlermeldungen und bessere Diagnostics. Das hilft Menschen beim Lernen – und LLMs beim Verständnis von Fehlerkontexten.

TypeScript 5.x: Inference ohne Informationsverlust

Mehr automatische Typinferenz bedeutet weniger Boilerplate, aber der Typ bleibt im System verfügbar. Der Trend geht zu „weniger schreiben, gleich viel Kontext” – ideal für Coding-Agents.

Python: Type Hints sind Pflicht geworden

PEP 484 war optional. Mit FastAPI, Pydantic und dem gesamten LLM-Tooling-Ökosystem sind Hints inzwischen de facto Standard. Projekte ohne Type Hints werden von Agents messbar schlechter unterstützt.

Der Trend: Expliziter Code

Die LLM-Ära bevorzugt:

  • Explizite Typen über implizite Konventionen
  • Konsistente Patterns über flexible Syntax
  • Lokale Lesbarkeit über globalen Zustand
  • Deklarative Beschreibungen über imperatives Detail

Das konvergiert interessanterweise mit guten Software-Engineering-Praktiken. Code, der für LLMs gut funktioniert, ist auch für Menschen lesbar.

Praktische Empfehlungen

  1. Typen aktivieren: TypeScript strict mode, Python type hints, Rust (sowieso)
  2. Konsistent bleiben: Ein Pattern pro Projekt, nicht fünf
  3. Kontext lokal halten: Parameter explizit, nicht aus dem Kontext gezogen
  4. Kleine Einheiten: Funktionen, die auf einen Bildschirm passen

Die beste Sprache für LLM-Unterstützung ist die, die auch ohne LLM gut lesbar wäre. Das ist vielleicht die wichtigste Erkenntnis.

Was sich seit 2025 verändert hat

Als ich diesen Artikel ursprünglich skizziert habe, war GitHub Copilot das Maß der Dinge. Inzwischen gibt es vollständige Coding-Agents wie Claude Code oder Mistral Vibe CLI, die nicht einzelne Zeilen vervollständigen, sondern ganze Dateien verstehen, refactoren und testen.

Das verstärkt die Effekte, die oben beschrieben sind, erheblich. Ein Agent, der über 20 Dateien hinweg arbeitet, profitiert noch stärker von expliziten Typen und konsistenten Patterns als ein Autocomplete, das eine einzelne Zeile vorschlägt. Die Sprachdesign-Prinzipien bleiben dieselben – aber der Hebel wird größer.

Weiterführende Ressourcen