Go ist die beste Sprache für KI-geschriebenen Code, weil ein Modell ihn zuverlässig schreiben und du leicht prüfen kannst, was es geschrieben hat. Die Sprache ist klein, also hat ein Modell nur wenige Möglichkeiten, dasselbe zu schreiben. Jede Go-Datei ist gleich formatiert, also sieht der Output aus wie der Code, aus dem das Modell gelernt hat. Der Compiler lehnt ungenutzte Imports und Variablen ab. Das Kompatibilitätsversprechen von Go 1 sorgt dafür, dass die APIs, die ein Modell vor Jahren gelernt hat, heute noch funktionieren. Und die Standardbibliothek deckt den Großteil eines Backends ab, also braucht das Modell selten ein Paket, bei dem es danebenliegen könnte. Sobald ein Agent den Großteil des Diffs schreibt, entscheiden genau diese Eigenschaften, wie viel deiner Zeit ins Review fließt.
Kurzfassung
- KI-Code scheitert meistens daran, dass er fast richtig ist. Für 66 % der Entwickler ist Code, der „fast, aber eben nicht ganz“ stimmt, das größte Ärgernis mit KI-Tools (Stack Overflow Developer Survey, 2025). Die beste Sprache für KI-geschriebenen Code ist die, in der sich diese Beinahe-Treffer am leichtesten erkennen lassen.
- Go hält den Output von Modellen kurz und vorhersehbar. Die Spezifikation hat 25 Keywords (Go spec), und die Autoren von Multi-SWE-bench stellten fest, dass „Go sowohl bei Input als auch Output einen relativ geringen Token-Verbrauch zeigt, vermutlich wegen seiner minimalistischen Syntax und klaren Konventionen“ (Multi-SWE-bench, 2025).
- Der Compiler kennt keine Warnungen. Go „weigert sich, Programme mit ungenutzten Variablen oder Imports zu kompilieren“ und meldet nur Fehler, die den Build stoppen (Go FAQ). Laut dem Octoverse-Report von GitHub „helfen typisierte Systeme, von LLMs erzeugte Compile-Fehler früher in der Pipeline zu erkennen“ (GitHub Octoverse, 2025).
- Go-Code von 2012 kompiliert immer noch. Das Go-1-Versprechen sorgt dafür, dass alte Programme „unverändert“ weiter bauen (Go 1 compatibility), also bleiben die Muster in den Trainingsdaten eines Modells gültig.
- Agents arbeiten am besten mit einem Check, den sie selbst ausführen können. Aus der Anleitung zu Claude Code von Anthropic: „Gib Claude etwas, das ein Bestanden oder Nicht bestanden liefert, und die Schleife schließt sich von selbst“ (Claude Code docs, 2026). Go liefert diese Checks mit der Sprache aus:
go build,go vetundgo test -race.
Was KI-geschriebener Code von einer Sprache braucht
Eine Sprache passt zu KI-geschriebenem Code, wenn sie bei vier Punkten gut abschneidet:
- Das Modell kann sie zuverlässig schreiben. Eine kleine Sprache mit einem verbreiteten Stil lässt weniger Raum für Fehler.
- Tools können sie ohne einen Menschen prüfen. Statische Typen, strenge Compile-Fehler und ein eingebauter Test-Runner lehnen schlechten Output ab, bevor ihn jemand liest.
- Ein Mensch kann sie schnell reviewen. Code ohne versteckten Kontrollfluss und ohne Magie zeigt einem Reviewer schon im Diff selbst, was eine Änderung tut.
- Was das Modell gelernt hat, bleibt richtig. Trainingsdaten sind Monate oder Jahre alt, also müssen die Sprache und ihre Bibliotheken weiter so funktionieren wie damals.
Go wurde bei Google für große Codebasen und große Teams entworfen, lange bevor es Coding-Assistenten gab. Dieselben Entscheidungen, durch die neue Teammitglieder schnell in Go einsteigen können, machen es einem Modell leicht, Go zu schreiben, und dir leicht, es zu reviewen.
Eine kleine Sprache, die Modelle richtig schreiben
Go hat weniger Features als die meisten Sprachen, die in Produktion laufen, und damit hat ein Modell weniger Möglichkeiten, etwas Cleveres und Falsches zu schreiben.
Die Spezifikation reserviert 25 Keywords (Go spec). Es gibt keine Klassen und keine Vererbung, keine Exceptions, kein Operator Overloading, keine Makros und keine impliziten Zahlenkonvertierungen. Schleifen schreibst du mit for und sonst nichts. Verhalten entsteht aus einfachen Funktionen, Structs und kleinen Interfaces. Wenn ein Modell einen HTTP-Handler in Go schreibt, gibt es nur eine Handvoll sinnvoller Wege, und die sehen alle ähnlich aus. Go Keywords: Alle 25 reservierten Wörter erklärt geht die komplette Liste durch.
Eine kleine Sprache bedeutet außerdem weniger Text pro Aufgabe. Die Autoren von Multi-SWE-bench haben den Token-Verbrauch über mehrere Sprachen gemessen, und Go lag sowohl bei Input als auch bei Output unter den niedrigsten (Multi-SWE-bench, 2025). Weniger Tokens pro Änderung machen jede Aufgabe günstiger und lassen im Kontextfenster mehr Platz für deinen eigenen Code.
Jede Go-Datei sieht gleich aus
gofmt ist Teil der Go-Toolchain und hat keine Einstellungen, über die man streiten könnte. Laut der Go FAQ ist der allergrößte Teil des Open-Source-Go-Codes durch dieses Tool gelaufen (Go FAQ). Das Go, aus dem ein Modell gelernt hat, ist also ungewöhnlich einheitlich, und deshalb sieht der Output eines Modells meist schon beim ersten Versuch nach idiomatischem Go aus.
Die Formatierung hilft auch im Review. Wenn das Modell ein Detail im Layout falsch macht, korrigiert gofmt es mit einem Befehl, also zeigen deine Diffs Änderungen an der Logik und nie Tabs gegen Leerzeichen oder die Position von Klammern. Bei der Benennung ist es genauso. Exportierte Namen beginnen mit einem Großbuchstaben, Fehler sind der letzte Rückgabewert, und context.Context ist der erste Parameter von allem, was I/O macht. Wer reviewt, weiß in jeder Go-Datei, wo er hinschauen muss, egal ob ein Mensch oder ein Modell sie geschrieben hat.
Der Compiler prüft jede Zeile zuerst
Der Go-Compiler lehnt eine ganze Klasse von KI-Fehlern ab, bevor überhaupt ein Test läuft. Nimm diesen Helper, den ein Assistent nach einem Refactoring zurücklassen könnte:
example.gogoimport ( "net/http" "strings" ) func countItems(r *http.Request) int { n := 0 items := r.URL.Query()["item"] return len(items) }
In Go lässt sich das nicht bauen:
example.texttext./handler.go:5:2: "strings" imported and not used ./handler.go:9:2: declared and not used: n
Beide Fehler sind typische Überbleibsel, wenn ein Modell eine Funktion halb umschreibt: ein Import aus einem früheren Versuch, eine Variable aus Logik, die es entfernt hat. Go macht sie mit Absicht zu Fehlern. Die Sprache tauscht „kurzfristige Bequemlichkeit gegen langfristige Build-Geschwindigkeit und Klarheit des Programms“, und „der Go-Compiler meldet keine Warnungen, nur Fehler, die das Kompilieren verhindern“ (Go FAQ). Weil es keine Warnung zum Ignorieren gibt, muss der Agent das Problem beheben, bevor der Build durchläuft.
Statische Typen fangen die übrigen typischen Ausrutscher ab. Einen string übergeben, wo ein int64 erwartet wird, einen Wert aus einer Funktion zurückgeben, die zwei liefert, oder eine Methode aufrufen, die es nicht gibt: All das scheitert beim Kompilieren. Damit ist der Großteil dessen abgedeckt, was Modelle falsch machen. Forscher der ETH Zürich und der UC Berkeley stellten fest, dass etwa 94 % der Compile-Fehler in LLM-generiertem TypeScript fehlgeschlagene Typprüfungen waren und keine Syntaxfehler (Mündler et al., PLDI, 2025). Die Studie drehte sich um TypeScript, und die Erkenntnis lässt sich direkt auf Go übertragen: Im Type Checker werden generierte Fehler abgefangen.
Weil Go so schnell baut, kostet dieser Check wenig. Die Go FAQ setzt als Ziel, dass „es höchstens ein paar Sekunden dauern sollte, ein großes Executable auf einem einzelnen Computer zu bauen“ (Go FAQ). Ein Agent, der nach jeder Änderung neu baut, führt den Compiler pro Aufgabe dutzendfach aus, und schnelle Builds halten diese Schleife kurz. Die Schleife lohnt sich: Als Compiler-Ergebnisse an ein Modell zurückgegeben wurden, stieg die Compile-Erfolgsquote bei einer Code-Completion-Aufgabe von 44,18 % auf 89,18 % (Wang et al., ACL, 2022).
Go-Code, den ein Modell vor Jahren gelernt hat, funktioniert immer noch
Ein Modell kennt nur die APIs, die in seinen Trainingsdaten vorkamen. Bei Go veraltet dieses Wissen nicht.
Das Go-1-Versprechen besagt, dass „Programme, die nach der Go-1-Spezifikation geschrieben wurden, über die gesamte Lebensdauer dieser Spezifikation unverändert weiter kompilieren und korrekt laufen“ (Go 1 compatibility). net/http-Code von 2014 kompiliert immer noch. Wenn ein Modell Muster mit http.HandleFunc, database/sql oder encoding/json vorschlägt, die es in älteren Repositories gesehen hat, funktionieren sie. Jedes öffentliche Go-Repository seit 2012 ist weiterhin gültiges Trainingsmaterial.
Wenn Go doch einmal einen besseren Weg einführt, etwas zu schreiben, bringt die Toolchain alten Code für dich auf den neuen Stand. Das Go-Team hat die go fix-Modernizer in Go 1.26 zum Teil mit Blick auf KI gebaut. Alan Donovan schrieb, dass Coding-Assistenten „(wenig überraschend) dazu neigten, Go-Code in einem ähnlichen Stil zu erzeugen wie die Masse des Go-Codes, der im Training verwendet wurde, selbst wenn es neuere, bessere Wege gab, dieselbe Idee auszudrücken“ (Go blog, 2026). go fix ./... schreibt diese Muster um: Aus interface{} wird any, aus for i := 0; i < n; i++ wird for i := range n, und handgeschriebene Begrenzungen werden zu min und max. Go-fix-Modernizer in Go 1.26 listet jeden Analyzer mit Vorher-nachher-Beispielen auf.
Eine Standardbibliothek, die das Backend abdeckt
Das meiste, was ein Backend-Service braucht, wird mit Go ausgeliefert. HTTP-Server und -Clients, Routing mit Methoden und Pfadparametern (seit Go 1.22), JSON, SQL-Interfaces, TLS, Kryptografie, Tests und strukturiertes Logging mit log/slog stecken alle in der Standardbibliothek. Ein Modell kann einen kompletten API-Service bauen, ohne etwas außerhalb der Standardbibliothek zu importieren, und Modelle haben diese Pakete in sehr vielen Trainingsdaten gesehen.
Das ist wichtig, weil Modelle tatsächlich Pakete erfinden. Eine Studie auf der USENIX Security 2025 mit 16 Modellen ergab, dass kommerzielle Modelle in mindestens 5,2 % der Fälle Pakete vorschlugen, die es nicht gibt, Open-Source-Modelle in 21,7 % der Fälle, mit insgesamt 205.474 eindeutigen erfundenen Namen (Spracklen et al., USENIX Security, 2025). Angreifer registrieren diese Namen inzwischen, ein Trick, den Seth Larson, Developer-in-Residence der PSF, „Slopsquatting“ getauft hat (Socket, 2025). Jeder Import, den ein Modell nicht braucht, ist einer, bei dem es sich nicht irren kann.
Wenn ein Go-Projekt doch eine Abhängigkeit hinzufügt, lässt sie sich dank des Modulsystems leicht prüfen. Imports sind vollständige Repository-Pfade wie github.com/jackc/pgx/v5 statt kurzer Namen, und go.sum zusammen mit der öffentlichen Checksum-Datenbank legt jede Abhängigkeit auf die exakten Bytes fest. Eine unerwartete Abhängigkeit fällt im Diff auf. Ist Go sicherer als Node.js? geht das Modulsystem im Detail durch.
Expliziter Code ist leicht zu reviewen
Wenn ein Modell den Code schreibt, besteht deine Aufgabe darin, ihn zu lesen. Der explizite Stil von Go, auch bei der Fehlerbehandlung, ist aufs Lesen ausgelegt.
Ein Agent mit gutem Prompt schreibt eine Repository-Methode und einen Handler so:
example.gogovar ErrNotFound = errors.New("user not found") func (s *Store) FindUser(ctx context.Context, id string) (User, error) { var u User err := s.db.QueryRowContext(ctx, `SELECT id, email FROM users WHERE id = $1`, id, ).Scan(&u.ID, &u.Email) if errors.Is(err, sql.ErrNoRows) { return User{}, ErrNotFound } if err != nil { return User{}, fmt.Errorf("find user %s: %w", id, err) } return u, nil } func getUser(users finder) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { u, err := users.FindUser(r.Context(), r.PathValue("id")) switch { case errors.Is(err, ErrNotFound): http.Error(w, "not found", http.StatusNotFound) return case err != nil: slog.Error("get user", "err", err) http.Error(w, "internal error", http.StatusInternalServerError) return } fmt.Fprintf(w, "%s\n", u.Email) } }
Jeder Weg aus den beiden Funktionen heraus steht auf der Seite. Du kannst vier Dinge prüfen, ohne eine andere Datei zu öffnen: Die Query nimmt den Request-Kontext, eine fehlende Zeile wird zu einem 404, andere Datenbankfehler werden geloggt und werden zu einem 500, ohne Details an den Client zu verraten, und der gewrappte Fehler behält die User-ID für die Log-Zeile. Es gibt keine Exception, die irgendwo anders geworfen und wer weiß wo gefangen wird.
Dieselbe Explizitheit macht übersprungene Fehler sichtbar. Ein ignorierter Fehler zeigt sich als Aufruf ohne err auf der linken Seite oder als explizites _ =, und errcheck (siehe den Setup-Abschnitt weiter unten) markiert die Fälle, die man leicht übersieht, etwa ein ungeprüftes json.NewEncoder(w).Encode(v). 10 häufige Go-Fehler, die du vermeiden solltest listet die Ausrutscher bei der Fehlerbehandlung auf, auf die du in generiertem Code achten solltest.
Nebenläufigkeit, die leicht zu schreiben und leicht zu prüfen ist
Goroutines, Channels und context.Context geben Go ein kleines, einheitliches Modell für nebenläufige Arbeit. Ein Modell, das einen Worker-Pool oder einen Fan-out von HTTP-Aufrufen schreibt, nutzt dieselben wenigen Bausteine wie jede Go-Codebasis, und die Go-Toolchain kann das Ergebnis prüfen.
Data Races sind der Nebenläufigkeitsbug, über den ein Reviewer am ehesten hinwegliest. Diese Rate-Limiting-Middleware, die Requests pro API-Key zählt, sieht auf den ersten Blick in Ordnung aus:
example.gogotype Limiter struct { hits map[string]int max int } func (l *Limiter) Wrap(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { key := r.Header.Get("X-API-Key") l.hits[key]++ if l.hits[key] > l.max { http.Error(w, "too many requests", http.StatusTooManyRequests) return } next.ServeHTTP(w, r) }) }
HTTP-Handler laufen auf vielen Goroutines gleichzeitig, und diese Map hat keinen Lock. Wir haben einen Test, der 50 gleichzeitige Requests durch die Middleware schickt, fünfmal unter Go 1.27 laufen lassen. go test -race schlug alle fünf Male fehl, mit WARNING: DATA RACE und einem Stack Trace, der auf die Zeile mit l.hits zeigt. Der Race Detector ist in die Toolchain eingebaut und „findet nur Races, die zur Laufzeit auftreten“, er braucht also einen Test, der den Code nebenläufig ausführt (Go race detector). Sobald es diesen Test gibt, findet ein Agent, der go test -race ausführt, den Bug bei jedem Lauf und kann ihn mit einem sync.Mutex beheben.
Tools, die der Agent selbst ausführen kann
Alles, was ein Agent braucht, um seine eigene Arbeit zu prüfen, steckt im go-Befehl. Es gibt vorher nichts zu installieren oder zu konfigurieren.
go vet „untersucht Go-Quellcode und meldet verdächtige Konstrukte“, die kompilieren, aber vermutlich falsch sind (cmd/vet). Ein typischer KI-Ausrutscher ist ein Formatverb, das nicht zu seinem Argument passt:
example.gogofunc showUser(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") fmt.Fprintf(w, "user %d", id) }
example.texttexthandler.go:10:23: fmt.Fprintf format %d has arg id of wrong type string
go test führt einen Teil dieser vet-Checks automatisch aus, ein Agent, der die Tests ausführt, bekommt sie also gratis dazu. go build kompiliert standardmäßig zu einer einzelnen, statisch gelinkten Binary (Go FAQ), also kann der Agent den Service, den er gerade geändert hat, bauen und starten, ohne erst eine Umgebung dafür einzurichten.
Das Go-Team baut auch direkt für Agents. gopls, der Go Language Server, enthält seit v0.20 „einen experimentellen eingebauten Server für das Model Context Protocol (MCP)“ (gopls MCP). Ein Agent, der damit verbunden ist, kann Definitionen, Referenzen und Diagnosen von derselben Engine abfragen, die auch dein Editor nutzt, statt aus dem Text zu raten.
So richtest du ein Go-Repo für KI-Coding-Agents ein
Den Großteil seines KI-Sicherheitsnetzes bekommt ein Go-Repo von der Standard-Toolchain. Beim Setup geht es darum, dass der Agent sie jedes Mal ausführt.
1. Schreib die Checks in eine Anweisungsdatei für den Agent. Die meisten Coding-Agents lesen eine CLAUDE.md- oder AGENTS.md-Datei im Root des Repos. AGENTS.md wird inzwischen von der Agentic AI Foundation unter dem Dach der Linux Foundation betreut und von über 60.000 Open-Source-Projekten genutzt (agents.md, 2026). Halte die Datei kurz und konkret:
example.markdownmarkdown## Checks (run before every commit) - go build ./... - go vet ./... - go test -race ./... - golangci-lint run ## Conventions - Standard library first. Ask before adding a dependency. - Wrap errors with fmt.Errorf("context: %w", err). Never discard an error. - Pass context.Context as the first argument to anything that does I/O.
2. Füg golangci-lint hinzu. Es bündelt über hundert Linter hinter einem Befehl, darunter errcheck für ignorierte Fehler und staticcheck. Ein Agent bekommt so eine einzige Liste von Fehlern zum Beheben statt fünf Tools.
3. Halte tabellengetriebene Tests nah am Code. Diese Tests kann ein Agent am leichtesten korrekt erweitern, weil ein neuer Fall nur eine neue Zeile bedeutet:
example.gogofunc TestGetUser(t *testing.T) { tests := []struct { name string finder fakeFinder wantCode int }{ {"found", fakeFinder{user: User{ID: "42", Email: "[email protected]"}}, http.StatusOK}, {"missing", fakeFinder{err: ErrNotFound}, http.StatusNotFound}, {"db down", fakeFinder{err: errors.New("connection refused")}, http.StatusInternalServerError}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("GET /users/{id}", getUser(tt.finder)) rec := httptest.NewRecorder() mux.ServeHTTP(rec, httptest.NewRequest("GET", "/users/42", nil)) if got := rec.Result().StatusCode; got != tt.wantCode { t.Errorf("status = %d, want %d", got, tt.wantCode) } }) } }
Schreib die ersten beiden Fälle selbst, damit der Agent deine Absicht übernimmt. Dann lass ihn die Randfälle ergänzen und lies, was er hinzufügt.
4. Führ -race in der CI aus. Wenn ein Agent nebenläufigen Code schreibt, ist der Race Detector der Check, den du bei jedem Push am dringendsten haben willst.
5. Führ go fix nach großen generierten Änderungen aus. Es ersetzt alte Idiome durch aktuelle, bevor sie sich in der Codebasis ausbreiten.
Nichts davon ersetzt das Lesen des Diffs. Es bedeutet nur, dass der Compiler, go vet, der Linter und der Race Detector die mechanischen Fehler schon abgelehnt haben, wenn du ihn liest. Was übrig bleibt, ist Urteilsvermögen: Ist das das richtige Design, und deckt es die Fälle ab, auf die es ankommt?
Wo LevelUpGo ins Spiel kommt
Go von einer KI schreiben zu lassen, funktioniert am besten, wenn du Go reviewen kannst. Du musst den ignorierten Kontext, den fehlenden Lock und den verschluckten Fehler erkennen. LevelUpGo bringt dir das bei, indem du den Code selbst schreibst: In jeder Lektion steht das Konzept links und ein echter Editor rechts, und dein Code muss kompilieren und echte Tests bestehen, damit du weiterkommst. Fang mit dem kostenlosen Go-Basics-Kurs an oder schau dir die komplette Go-Roadmap an. Für die Lernperspektive auf das Thema KI zeigt Solltest du Code noch von Hand schreiben?, wann du selbst tippen solltest, und Lohnt es sich, Go 2026 zu lernen? behandelt die Karriereseite.
Häufig gestellte Fragen
Ist Go gut für KI-generierten Code?
Ja, und für Backend-Services, CLIs und Infrastruktur passt es am besten. Go ist klein genug, dass der Output von Modellen vorhersehbar bleibt, gofmt hält jede Datei in einem Stil, der Compiler lehnt ungenutzte Imports und Variablen ab, und go vet und go test -race finden Bugs, die kompilieren. Mehr KI-Fehler schlagen automatisch fehl, und für den menschlichen Reviewer bleibt weniger übrig.
Warum fällt es KI-Modellen leicht, Go zu schreiben?
Go hat 25 Keywords, einen offiziellen Formatter und eine Standardbibliothek, die den Großteil der Backend-Arbeit abdeckt. Für ein gegebenes Problem gibt es nur wenige Lösungswege, also sieht der Output eines Modells aus wie das idiomatische Go, aus dem es gelernt hat. Die Autoren von Multi-SWE-bench fanden außerdem, dass Go beim Token-Verbrauch unter den niedrigsten liegt, und führten das auf die minimalistische Syntax und die klaren Konventionen zurück.
Ist Go für KI-Coding-Agents besser als Python?
Für Services, die in Produktion laufen, ja. Go gibt einem Agent statische Typen, einen Compiler ohne Warnungen, einen eingebauten Race Detector und einen einzigen Formatter, ohne zusätzliches Setup. Diese Checks machen aus vielen der Beinahe-Treffer, die KI-Tools produzieren, Build-Fehler, die der Agent selbst behebt.
Halluzinieren KI-Modelle Go-Pakete?
Die wichtigste Studie zu halluzinierten Paketen, Spracklen et al. (USENIX Security 2025), hat Python und JavaScript getestet, eine veröffentlichte Quote für Go gibt es also nicht. Die Standardbibliothek von Go deckt mehr von einem typischen Backend ab. Dadurch gibt es weniger Imports, bei denen ein Modell danebenliegen kann, und vollständige Importpfade plus go.sum machen eine unerwartete Abhängigkeit im Review leicht sichtbar.
Wie bringe ich einen KI-Agent dazu, besseres Go zu schreiben?
Gib ihm Checks, die er selbst ausführen kann. Schreib go build ./..., go vet ./..., go test -race ./... und golangci-lint run in eine CLAUDE.md- oder AGENTS.md-Datei. Pflege tabellengetriebene Tests, die der Agent erweitern kann, und führ go fix ./... aus, um veraltete Idiome zu aktualisieren. Danach liest du den Diff selbst.
Quellen
- Stack Overflow Developer Survey 2025, KI
- Zan et al., "Multi-SWE-bench," NeurIPS (2025)
- GitHub Octoverse 2025
- Mündler et al., "Type-Constrained Code Generation with Language Models," PLDI (2025)
- Wang et al., "Compilable Neural Code Generation with Compiler Feedback," ACL (2022)
- Spracklen et al., "We Have a Package for You!" USENIX Security (2025)
- Socket, "Slopsquatting" (2025)
- Go FAQ
- Go 1 and the Future of Go Programs
- The Go Programming Language Specification
- cmd/vet
- Data Race Detector
- Alan Donovan, "Using go fix to modernize Go code," Go-Blog (2026)
- gopls MCP server
- Claude Code best practices
- AGENTS.md
