Artikel

discord.js v14: was der Umstieg wirklich gekostet hat

Umbenannte Aufzählungen, veränderte Rechteprüfungen, ein neues Modul für Einbettungen. Ein Wochenende Umstellung, das drei Wochen gedauert hat — und warum ich trotzdem früh umgestiegen bin.

BlackZackBlackzack

923 Wörter · 5 Min. Lesezeit

  • yurna
  • discord
  • migration

Im Juli erschien discord.js v14. Ich bin innerhalb der ersten Wochen umgestiegen, obwohl mein Bot lief und niemand danach gefragt hatte.

Der Grund war einfach: Bibliotheks-Updates werden mit jedem Monat teurer, den man wartet. Wer eine Hauptversion überspringt, muss beim nächsten Mal zwei auf einmal machen, und dann greifen die Änderungen ineinander.

Was sich geändert hat

Aufzählungen statt Zeichenketten. Kanaltypen, Rechte, Stile — überall, wo vorher ein Text stand, steht jetzt ein Wert aus einer Aufzählung.

// vorher
channel.type === "GUILD_TEXT"
member.permissions.has("MANAGE_GUILD")
 
// nachher
channel.type === ChannelType.GuildText
member.permissions.has(PermissionsBitField.Flags.ManageGuild)

Das ist die häufigste Einzeländerung und betrifft praktisch jede Datei. Der Gewinn: Ein Tippfehler ist jetzt ein Fehler beim Schreiben, vorher war er ein stiller Vergleich, der nie zutrifft. Genau dieser Fehler hatte mich Monate vorher eine Stunde gekostet — eine Rechteprüfung, die wegen eines falsch geschriebenen Namens immer negativ war und deshalb nie jemanden durchgelassen hat.

Einbettungen und Komponenten als Builder. Statt Objektliterale gibt es Klassen mit Methoden. Umständlicher zu tippen, dafür mit Prüfung beim Bauen.

Intents sind Pflicht und ausdrücklich. Man muss genau angeben, welche Ereignisgruppen man empfangen will. Nachrichteninhalte sind eine privilegierte Berechtigung, die man begründen muss, sobald der Bot auf vielen Servern läuft.

Das ist die Änderung, die am meisten über die Richtung sagt, in die sich Discord bewegt: weg von „der Bot bekommt alles" hin zu „der Bot bekommt, was er braucht". Für meine Umstellung auf Slash-Commands war das rückblickend ein Glücksfall — sie brauchen keinen Zugriff auf Nachrichteninhalte.

Was die Umstellung wirklich gekostet hat

Ich hatte ein Wochenende eingeplant. Es wurden drei Wochen nebenher, und die Zeit ging fast vollständig in zwei Dinge.

Das Finden der Stellen. Die Änderungsliste beschreibt, was sich geändert hat, nicht wo es bei mir vorkommt. Bei über hundert Befehlen und dreißig Ereignisbehandlungen ist das mühsame Kleinarbeit. Was geholfen hat: das Projekt schrittweise übersetzen zu lassen und die Fehlerliste als Aufgabenliste zu benutzen — ein Argument für Typprüfung, das mich später zum Umstieg auf TypeScript gebracht hat.

Die Dinge, die keinen Fehler werfen. Der unangenehme Teil. Ein Vergleich mit einer Zeichenkette, die es nicht mehr gibt, ist syntaktisch einwandfrei — er ist nur immer falsch. Solche Stellen findet kein Übersetzer, sondern nur ein Test oder ein Nutzer.

Warnung

Bei Migrationen zwischen Hauptversionen sind die stillen Änderungen gefährlicher als die lauten. Alles, was einen Fehler wirft, findet man in einer Stunde. Alles, was nur ein anderes Ergebnis liefert, findet man Wochen später — und meistens, weil sich jemand beschwert. Ich gehe deshalb nach so einer Umstellung gezielt alle Vergleiche und Prüfungen durch, nicht nur die Stellen, die der Übersetzer meldet.

Die Sache mit den Rechten

Der Bereich, in dem ich am längsten gesessen habe, waren Berechtigungen. Nicht wegen der Umbenennungen, sondern weil ich bei der Gelegenheit gemerkt habe, dass meine Prüfungen an mehreren Stellen unvollständig waren.

Discord hat zwei Ebenen: die Berechtigungen einer Rolle auf Serverebene und Überschreibungen pro Kanal. Ich hatte an einigen Stellen nur die Serverebene geprüft. Für einen Nutzer, dem in einem bestimmten Kanal etwas ausdrücklich verboten war, ging der Befehl trotzdem durch.

Die richtige Prüfung fragt immer nach der effektiven Berechtigung im konkreten Kanal:

const rechte = kanal.permissionsFor(mitglied);
if (!rechte?.has(PermissionsBitField.Flags.ManageMessages)) return verweigern();

Dass ich das bei einer Bibliotheksumstellung gefunden habe und nicht bei einem Sicherheitsdurchgang, ist Zufall. Es ist auch ein Argument dafür, solche Umstellungen nicht bloß mechanisch abzuarbeiten: Man liest dabei Code, den man seit Jahren nicht angefasst hat.

Die Werkzeuge, die eine Migration erträglich machen

Aus dieser und späteren Umstellungen habe ich mir ein Vorgehen zurechtgelegt, das den Aufwand planbar macht.

Typprüfung als Aufgabenliste. Das Projekt einmal durch den Übersetzer schicken und die Fehlerliste als Arbeitsvorrat nehmen. Das erwischt alles, was strukturell nicht mehr passt.

Eine Suche nach Zeichenketten, die früher Bedeutung hatten. Für jeden umbenannten Wert eine Textsuche im gesamten Projekt. Das erwischt die stillen Fälle, die kein Übersetzer findet.

Eine Liste der berührten Bereiche vor dem Anfangen. Nicht alle Dateien, sondern die Themen: Berechtigungen, Kanaltypen, Einbettungen, Ereignisse. Danach weiß man, was zu testen ist.

Ein zweiter Bot-Zugang zum Testen. Der wichtigste Punkt. Eine Migration im Livebetrieb zu prüfen ist eine schlechte Idee, und ein zweiter Zugang mit eigenem Testserver kostet nichts.

Die Frage, die ich mir bei Bibliotheken heute stelle

Vor jeder größeren Abhängigkeit — und eine Bot-Bibliothek ist eine sehr große — beantworte ich inzwischen drei Punkte:

Wie oft gibt es Hauptversionen? Alle zwei Jahre ist normal. Alle drei Monate bedeutet, dass Migrationen zur Dauerbeschäftigung werden.

Wie werden Änderungen dokumentiert? Eine vollständige Liste mit Vorher-Nachher-Beispielen ist Gold wert. Eine Aufzählung ohne Beispiele bedeutet, dass man die Umstellung selbst herausfinden muss.

Wie lange werden alte Fassungen gepflegt? Das entscheidet darüber, ob man unter Zeitdruck steht.

Diese drei Fragen zu stellen kostet zwanzig Minuten und ändert manchmal die Wahl. Bei Bibliotheken, die tief im Projekt sitzen, ist die Pflegepolitik wichtiger als der Funktionsumfang — den Umfang sieht man beim Vergleich, die Politik erst nach zwei Jahren.

Warum ich früh umsteige

Ich habe mir aus dieser und späteren Migrationen eine einfache Haltung angewöhnt:

  • Nebenversionen zeitnah, weil sie meistens nur Fehlerbehebungen enthalten.
  • Hauptversionen innerhalb weniger Wochen, solange die Änderungsliste noch frisch diskutiert wird und man in Foren Antworten auf dieselben Probleme findet.
  • Niemals eine überspringen, wenn es sich vermeiden lässt.

Der Preis dafür ist, gelegentlich in Anfangsprobleme zu laufen. Der Preis für das Gegenteil ist, irgendwann vor einem Sprung über drei Versionen zu stehen, bei dem sich Änderungen überlagern und man nicht mehr weiß, welche Umstellung welchen Fehler verursacht hat.

Genau in dieser Lage bin ich Jahre später bei einem ganz anderen Projekt gelandet — bei Minecraft-Plugins, die eine Version übersprungen hatten. Danach war ich mir bei dieser Haltung ziemlich sicher.