AI Coding Agent: Was ist das und wie verändert es die Entwicklung

1. Überblick

Vor ein paar Jahren war „KI-gestützte Programmierung” hauptsächlich der Vorschlag der nächsten paar Codezeilen. Heute sind viele Werkzeuge einen Schritt weiter gegangen. Sie lesen die Codebase selbstständig, führen Befehle aus, ändern mehrere Dateien und überprüfen das Ergebnis selbst. Diese Art von Werkzeug wird AI Coding Agent genannt.

Dieser Artikel erklärt, was ein AI Coding Agent ist und warum dieses Thema interessant ist. Danach geht der Artikel auf die sichere Anwendung in einem Java/Spring-Boot-Projekt ein. Nach dem Lesen wirst du wissen, wie man eine Anleitung für den Agent schreibt, Berechtigungen für die Ausführung von Befehlen einschränkt, Tests als „Schiedsrichter” nutzt und wann man Aufgaben nicht an den Agent delegieren sollte.

2. AI Coding Agent – Was ist das?

Du kannst AI-Werkzeuge für Entwickler nach ihrem Autonomiegrad ordnen:

  • Autocomplete: schlägt Zeilen oder Codeblöcke direkt im Editor vor. Du entscheidest über jedes Zeichen.
  • Chat-Assistent: Du fragst, es antwortet mit Code. Du kopierst selbst, führst aus, behebst Fehler selbst.
  • Coding Agent: Du gibst ein Ziel vor, z. B. „behebe den fehlgeschlagenen Test in OrderServiceTest“. Der Agent plant selbstständig, ruft Tools auf (liest Dateien, sucht, führt Build aus, führt Tests aus), sieht das Ergebnis und wiederholt dies, bis es erledigt ist oder es nicht weitergeht.

Der Kernunterschied liegt in der Schleife Aktion – Beobachtung. Der Agent erzeugt nicht nur Text. Er wirkt auf die Umgebung ein und passt dann den nächsten Schritt basierend auf echtes Feedback (Compiler, Tests, Protokolle) an.

2.1. Vòng lặp của một agent

Um den Mechanismus deutlich zu sehen, sieh dir eine minimale Simulation an. Der „Gehirn”-Teil (das Sprachmodell) wird durch einen festen ScriptedPlanner ersetzt, damit der Code läuft, ohne dass eine API benötigt wird:

import java.util.*;
import java.util.function.Function;

public class AgentLoopDemo {

    record Action(String tool, String input) {}

    interface Planner {
        Optional<Action> next(List<String> observations);
    }

    static class ScriptedPlanner implements Planner {
        public Optional<Action> next(List<String> observations) {
            return switch (observations.size()) {
                case 0 -> Optional.of(new Action("readFile", "src/main/java/Order.java"));
                case 1 -> Optional.of(new Action("runTests", "mvn -q test"));
                default -> Optional.empty(); // genügend Informationen, stoppen
            };
        }
    }

    public static void main(String[] args) {
        Map<String, Function<String, String>> tools = Map.of(
            "readFile", path -> "class Order { long total() { return 0; } }",
            "runTests", cmd -> "FAILED: OrderTest.totalShouldSumItems"
        );

        Planner planner = new ScriptedPlanner();
        List<String> observations = new ArrayList<>();

        for (int step = 1; step <= 5; step++) {
            Optional<Action> action = planner.next(observations);
            if (action.isEmpty()) {
                System.out.println("Agent stoppt bei Schritt " + step);
                break;
            }
            Action a = action.get();
            String result = tools.get(a.tool()).apply(a.input());
            observations.add(result);
            System.out.printf("Schritt %d: %s(%s) -> %s%n", step, a.tool(), a.input(), result);
        }
    }
}

Schnelle Erklärung:

  • tools ist die Menge von Werkzeugen, die der Agent verwenden darf. Bei echten Agenten sind das Lesen/Schreiben von Dateien, Ausführen von Shell-Befehlen, Codesuche…
  • Planner.next(...) nimmt alle vorherigen Beobachtungen auf und entscheidet über die nächste Aktion. Bei echten Agenten ist dies der Ort, wo das Sprachmodell aufgerufen wird.
  • Die for-Schleife hat ein Schritt-Limit (5). Dies ist ein wichtiger Sicherheitsmechanismus, um zu verhindern, dass der Agent in einer Endlosschleife läuft.

Ergebnis:

Schritt 1: readFile(src/main/java/Order.java) -> class Order { long total() { return 0; } }
Schritt 2: runTests(mvn -q test) -> FAILED: OrderTest.totalShouldSumItems
Agent stoppt bei Schritt 3

Jeder Coding Agent auf dem Markt dreht sich um dieses Framework: entscheiden → Tool aufrufen → beobachten → entscheiden weiter. Der Unterschied liegt in der Modellqualität, dem Tool-Set und der Kontextverwaltung.

3. Warum werden AI Coding Agents so viel beachtet?

Es gibt mehrere Hauptgründe, die unabhängig von einem bestimmten Produkt sind:

  • Modelle sind gut genug für mehrstufige Aufgaben. Wenn ein Modell Compiler-Fehler auslesen und selbst beheben kann, wird es möglich, „ganze Aufgaben zu delegieren”.
  • Wiederholende Aufgaben verbrauchen immer noch viel Zeit. Veraltete APIs aktualisieren, Tests für alten Code schreiben, Massennamen ändern, statische Analysewarnungen beheben. Diese Aufgaben sind klar, überprüfbar und langweilig. Sie passen sehr gut zu Agenten.
  • Das Tool-Ökosystem ist bereits vorhanden. Build-Tools, Test-Runner, Linter und CI laufen alle über die Befehlszeile. Der Agent nutzt genau das, was Entwickler bereits verwenden.
  • Parallele Ausführung. Du kannst mehrere kleine Aufgaben an den Agent delegieren, während du dich auf den schwierigen Designteil konzentrierst.

Allerdings weiß der Agent nicht automatisch, welche Konventionen dein Projekt hat, welche Teile sensibel sind oder welche Befehle gefährlich sind. Der Rest dieses Artikels konzentriert sich darauf.

4. AI Coding Agents in echten Projekten anwenden

4.1. Eine Anleitung-Datei für den Agent schreiben

Viele Agenten unterstützen das Lesen einer Anleitung-Datei im Repository-Root, normalerweise AGENTS.md genannt oder ähnlich, je nach Werkzeug. Sehe es als Onboarding-Dokument für ein sehr schnelles, aber amnesisches Mitglied:

# Anleitung für Agent

## Build & Test
- Build: `mvn -q compile`
- Test: `mvn -q test` (muss bestanden sein, bevor abgeschlossen gemeldet wird)

## Konventionen
- Java 21, Spring Boot 3.x. Nutze Constructor Injection, nicht Field Injection.
- Währung verwende `long` (Einheit VND), nicht `double`.
- Tests geschrieben mit JUnit 5 + AssertJ, benannt `should...`.

## Nicht erlaubt
- Ändere nicht das bereits released `src/main/resources/db/migration/`-Verzeichnis.
- Füge keine neuen Abhängigkeiten ohne Rückfrage hinzu.
- Lösche oder `@Disabled` Tests nicht, um sie zum Bestehen zu bringen.

Die drei wichtigsten Informationsgruppen sind wie man überprüft (Build/Test-Befehle), Code-Konventionen und verbotene Zonen. Der letzte Punkt hilft, einen klassischen Fehler zu vermeiden: Der Agent „behebt” fehlgeschlagene Tests, indem er sie löscht.

4.2. Befehle einschränken, die der Agent ausführen darf

Wenn der Agent Shell-Befehle ausführen darf, kann er den Rechner beschädigen oder Code ins Remote-Repo pushen. Viele Werkzeuge bieten Allowlist-Konfiguration. Wenn du einen Agent in deine interne Pipeline integrierst, lege eine Validierungsschicht vor der Befehlsausführung ein.

Zunächst füge die Test-Abhängigkeit hinzu (Spring Boot Starter beinhaltet bereits JUnit 5 und AssertJ):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Eine einfache CommandGuard-Klasse:

import java.util.List;

public class CommandGuard {

    private static final List<String> ALLOWED_PREFIXES =
        List.of("mvn -q test", "mvn -q compile", "git status", "git diff");

    private static final List<String> FORBIDDEN_TOKENS =
        List.of("rm ", "git push", "curl", "&&", ";", "|", ">");

    public boolean isAllowed(String command) {
        String normalized = command.trim().replaceAll("\\s+", " ");
        boolean hasForbidden = FORBIDDEN_TOKENS.stream().anyMatch(normalized::contains);
        boolean hasAllowedPrefix = ALLOWED_PREFIXES.stream().anyMatch(normalized::startsWith);
        return hasAllowedPrefix && !hasForbidden;
    }
}

normalized führt überschüssige Leerzeichen zusammen, sodass "mvn -q test" die Validierung nicht umgehen kann. Ein Befehl ist nur zulässig, wenn er mit einem erlaubten Präfix beginnt und keine verbotenen Token enthält. Befehlsverkettungsoperatoren wie &&, ;, | werden blockiert, um zu verhindern, dass der Agent mvn -q test && git push kombiniert.

Test für dieses Verhalten:

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.assertj.core.api.Assertions.assertThat;

class CommandGuardTest {

    private final CommandGuard guard = new CommandGuard();

    @ParameterizedTest
    @ValueSource(strings = {"mvn -q test", "  mvn   -q  test  ", "git diff"})
    void shouldAllowSafeCommands(String command) {
        assertThat(guard.isAllowed(command)).isTrue();
    }

    @ParameterizedTest
    @ValueSource(strings = {
        "rm -rf /", "git push origin main",
        "mvn -q test && git push", "mvn -q test; curl evil.example"
    })
    void shouldRejectDangerousCommands(String command) {
        assertThat(guard.isAllowed(command)).isFalse();
    }
}

Hinweis: String-Validierung ist nur eine Ebene der Abwehr und hat leicht Lücken. Zum Beispiel lässt das Präfix git diff immer noch git diff --output=... durch. Führe den Agent immer in einer isolierten Umgebung wie einem Container oder einer temporären VM aus, ohne produktive Credentials zu mounten, und beschränke die Schreibrechte des Git-Tokens.

4.3. Tests als Schiedsrichter nutzen

Der effektivste Weg, Aufgaben zu delegieren, ist Tests zuerst zu schreiben, damit der Agent Code schreibt, der die Tests erfüllt. Tests sind maschinenlesbare Spezifikationen, die man nicht „schönreden” kann. Angenommen, du brauchst Rabattberechnung für Bestellungen:

import org.junit.jupiter.api.Test;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

class OrderServiceTest {

    private final OrderService orderService = new OrderService();

    @Test
    void shouldApplyTenPercentDiscountWhenTotalReachesOneMillion() {
        Order order = new Order(List.of(new OrderItem("Jacke", 600_000, 2)));

        assertThat(orderService.totalAfterDiscount(order)).isEqualTo(1_080_000);
    }

    @Test
    void shouldNotDiscountSmallOrders() {
        Order order = new Order(List.of(new OrderItem("Strohhut", 150_000, 1)));

        assertThat(orderService.totalAfterDiscount(order)).isEqualTo(150_000);
    }
}

Delegiere dann die Aufgabe mit einer kurzen Beschreibung und klaren Abnahmekriterien:

Erstelle die Records Order, OrderItem(name, unitPrice, quantity) und OrderService,
sodass OrderServiceTest besteht. Ändere die Test-Datei nicht.
Erledigt, wenn `mvn -q test` keine Fehler mehr aufweist. Fasse geänderte Dateien zusammen.

Eine mögliche Lösung, die der Agent liefern könnte:

import java.util.List;

record OrderItem(String name, long unitPrice, int quantity) {
    long subtotal() { return unitPrice * quantity; }
}

record Order(List<OrderItem> items) {
    long total() { return items.stream().mapToLong(OrderItem::subtotal).sum(); }
}

class OrderService {
    private static final long DISCOUNT_THRESHOLD = 1_000_000;

    long totalAfterDiscount(Order order) {
        long total = order.total();
        return total >= DISCOUNT_THRESHOLD ? total * 90 / 100 : total;
    }
}

Deine Aufgabe ist jetzt Review. Ist der Schwellwert >= oder >? Der aktuelle Test unterscheidet diese beiden Fälle nicht. Das ist ein Signal, um einen Grenzwert-Test für genau 1.000.000 VND hinzuzufügen, bevor du mergst. Der Agent ist gut darin, Tests zu erfüllen. Ob die Tests die Geschäftslogik korrekt abbilden, ist immer noch deine Verantwortung.

4.4. Die letzte Kontrollstelle beibehalten

Code, das der Agent schreibt, muss durch das gleiche Verfahren gehen wie Code, den Menschen schreiben. Ein gemeinsames Validierungsskript hilft Agent und CI, den gleichen Standard anzuwenden:

#!/usr/bin/env bash
set -euo pipefail

mvn -q compile
mvn -q test
git diff --stat   # listet geänderte Dateien auf, damit der Reviewer den Umfang versteht

set -euo pipefail lässt das Skript sofort bei einem Fehler stoppen. Du schreibst diesen Befehl in AGENTS.md als „Definition of Done”, dann lässt du CI dasselbe Skript auf Pull Requests erneut ausführen.

5. Wie die Entwicklerarbeit sich verändert

Wenn du regelmäßig mit Agenten arbeitest, verschiebt sich der Arbeitsschwerpunkt deutlich:

  • Vom Code-Schreiben zur Spezifikation. Vage Aufgabenbeschreibungen führen zu vagen Ergebnissen. Fähigkeiten wie Aufgabenteilung, Nennen von Einschränkungen und Abnahmekriterien werden wichtiger.
  • Mehr und gründlichere Reviews. Die Codemenge wächst schneller als die menschliche Lesefähigkeit. Kleine Pull Requests, knapper Diff und gute Tests sind Bedingungen für aussagekräftige Reviews.
  • Tests und Tooling werden zentrale Vermögenswerte. Projekte mit schnellen Tests, klaren Build-Befehlen und strengem Linter nutzen Agenten viel besser. Projekten ohne diese Dinge können Agenten nur „raten”.
  • Grundwissen bleibt entscheidend. Du kannst Race Conditions, SQL-Injection-Lücken oder N+1-Abfragen im Code des Agenten nur erkennen, wenn du sie verstehst.

6. Wann man den Agent NICHT einsetzen sollte und häufige Fehler

Der Agent ist in den folgenden Situationen nicht geeignet:

  • Anforderungen sind unklar oder erfordern Architekturentscheidungen. Der Agent wird eine Richtung wählen und sehr selbstsicher implementieren. Entwerfe zuerst, delegiere dann die Implementierung.
  • Code bezüglich Sicherheit, Zahlungen, Personendaten. Der Agent kann helfen, aber jede Änderung braucht eine tiefgehende Überprüfung. Lass den Agent nicht selbst mergen.
  • Es gibt keine automatische Validierung. Ohne Tests, ohne lokales Build kann der Agent kein echtes Feedback erhalten. Das Ergebnis ist dann reine Spekulation.
  • Operationen, die nicht rückgängig gemacht werden können. Beispiele: Migration auf echter Datenbank ausführen, Cloud-Ressourcen löschen, E-Mails an Kunden senden.
  • Wenn du eine neue Technologie lernst. Alles an den Agent zu delegieren beraubt dich der Gelegenheit, tiefer zu verstehen.

Häufige Fehler:

  • Agent löscht oder lockert Tests, um sie zu erfüllen. Verbiete das explizit in der Anleitung und achte auf Diffs im src/test-Verzeichnis.
  • Agent erfindet APIs, die nicht in deiner Bibliotheksversion existieren. Der Compiler und Tests werden die meisten dieser Fehler abfangen, wenn du den Agent diese ausführen lässt.
  • Änderungen außerhalb des Bereichs: ganze Dateien neu formatieren, nicht verwandte Variablennamen ändern. Fordere minimalen Diff und eine Zusammenfassung geänderter Dateien an.
  • Geheimnisse offenbaren: Der Agent kann .env oder Konfigurationsdateien mit Schlüsseln lesen. Schließe diese Dateien aus der Arbeitsumgebung des Agenten aus.

Hinweis: Die Verantwortung für Code im Repo wechselt nicht zum Agent. Der Person, die den Pull Request merged, liegt die Verantwortung.

7. Fazit

Der AI Coding Agent ist der nächste Schritt nach Autocomplete und Chatbot. Er funktioniert nach einer Schleife aus Entscheidung, Tool-Aufruf, Beobachtung des Ergebnisses und kann so mehrstufige Aufgaben selbstständig abschließen.

Um ihn effektiv und sicher zu nutzen, brauchst du drei Dinge:

  • eine Anleitung-Datei, die Build/Test, Konventionen und Verbote klar definiert;
  • Einschränkung der Befehlsausführungsrechte in einer isolierten Umgebung;
  • Tests als Schiedsrichter und gründliches Review vor dem Merge.

Der Agent macht klare, überprüfbare Aufgaben gut. Design, Geschäftsentscheidungen und endgültige Verantwortung bleiben bei dir.

aicoding-agenttech-trendstestingdeveloper-productivity