Ticket-System v2: Transkripte, Kategorien, Automatik
Aus einem Knopf, der einen Kanal erzeugt, wurde ein System mit Abschriften, Bewertungen, Notizen und Regeln. Der größte Einzelbrocken des Jahres — und die Fragen, die dabei nicht technisch waren.
1051 Wörter · 5 Min. Lesezeit
- yurna
- discord
- architektur
Das Ticket-System war 2021 in zwei Fassungen entstanden. 2023 kam die dritte, und diesmal nicht, weil die vorherige kaputt war, sondern weil sie an ihre Grenzen kam: Server mit mehreren hundert Tickets pro Monat brauchen andere Werkzeuge als Server mit fünf.
Was dazukam
Abschriften als eigenständige Dokumente. Vorher wurde beim Schließen der Verlauf als Textdatei gespeichert. Jetzt entsteht eine HTML-Datei mit Zeitstempeln, Rollenfarben, Anhangsverweisen und einer Kopfzeile mit den Eckdaten. Sie lässt sich über einen Link öffnen, der nur für Berechtigte funktioniert.
Notizen, die der Ersteller nicht sieht. Ein Feld, in dem das Team Dinge festhält, die nicht ins Gespräch gehören: „schon dreimal wegen desselben Themas hier", „Fall an Person X übergeben". Das war der meistgewünschte Punkt überhaupt.
Bewertungen. Nach dem Schließen bekommt der Ersteller eine kurze Rückfrage. Nicht, um Teammitglieder zu bewerten, sondern um Muster zu erkennen: Welche Kategorien laufen gut, wo bleiben Leute unzufrieden zurück.
Automatikregeln. Wenn ein Ticket in Kategorie X geöffnet wird, informiere Rolle Y. Wenn drei Tage keine Antwort kam, erinnere. Wenn der Ersteller sieben Tage nicht reagiert, schließe automatisch mit Hinweis.
Formularfelder pro Kategorie. Statt eines leeren Kanals mit „Beschreib dein Problem" gibt es vorab gestellte Fragen, deren Antworten oben im Ticket stehen. Das halbiert die Rückfragen.
Die Datenstruktur
CREATE TABLE ticket_kategorien (
id INTEGER PRIMARY KEY,
gilde VARCHAR(20) NOT NULL,
name VARCHAR(60) NOT NULL,
discord_kat VARCHAR(20) NOT NULL,
zustaendig VARCHAR(20) NOT NULL,
max_offen INT NOT NULL DEFAULT 1,
aktiv BOOLEAN NOT NULL DEFAULT 1
);
CREATE TABLE ticket_felder (
id INTEGER PRIMARY KEY,
kategorie_id INTEGER NOT NULL,
bezeichnung VARCHAR(100) NOT NULL,
typ VARCHAR(20) NOT NULL, -- text, lang, auswahl
pflicht BOOLEAN NOT NULL DEFAULT 0,
reihenfolge INT NOT NULL DEFAULT 0
);Die Trennung von Kategorie und Feldern ist dieselbe Idee wie beim Bewerbungssystem im Jahr davor: Was sich ändern kann, gehört in Daten, nicht in Code. Ich habe das damals gelernt und hier zum ersten Mal von vornherein so gebaut.
Die Fragen, die nicht technisch waren
Wie lange bewahrt man Abschriften auf? Ein Ticket kann persönliche Dinge enthalten — Beschwerden über andere, gesundheitliche Gründe für Abwesenheit, Streitigkeiten. Diese Texte dauerhaft aufzubewahren ist bequem und nicht selbstverständlich richtig. Ich habe eine einstellbare Frist eingebaut, standardmäßig neunzig Tage, und die Möglichkeit, einzelne Abschriften als dauerhaft zu markieren.
Wer darf sie lesen? Nicht „das Team", sondern die Rolle, die für die Kategorie zuständig ist. Ein Bewerbungsticket geht andere Leute an als eine technische Frage.
Was passiert, wenn der Ersteller den Server verlässt? Sein Ticket bleibt bestehen, seine Daten auch. Ich habe eine Löschmöglichkeit auf Anfrage eingebaut — und dabei gemerkt, dass eine Abschrift, die andere Personen zitiert, sich nicht einfach vollständig löschen lässt, ohne den Zusammenhang zu zerstören. Die Lösung war, den Namen zu ersetzen statt die Zeilen zu entfernen.
Warnung
Wer Chatverläufe archiviert, sollte sich vorher überlegen, was er da eigentlich anlegt. Eine Ticket-Abschrift ist eine Sammlung personenbezogener Daten mit Zeitstempel, oft mit sensiblem Inhalt. Frist, Zugriff und Löschweg gehören zum Feature dazu, nicht in einen späteren Nachtrag. Diese drei Punkte waren bei mir die Hälfte des Aufwands.
Der Teil, der am meisten gebracht hat
Nicht die Abschriften, nicht die Automatik. Es war eine simple Übersicht: eine Liste aller offenen Tickets, sortiert nach Alter, mit Zuständigem und letzter Aktivität.
Vorher war der Zustand eines Support-Bereichs eine Sammlung von Kanälen in einer Seitenleiste. Man sah nicht, welches Ticket seit vier Tagen unbeantwortet ist, weil alle gleich aussehen. Mit der Liste sieht man es in zwei Sekunden.
Das ist dieselbe Erkenntnis wie bei den Statistiken im Jahr davor: Sichtbarkeit schlägt Funktionalität. Die Automatikregeln waren die aufwendigste Arbeit und haben weniger verändert als eine sortierte Tabelle.
Die Zahlen, die das System sichtbar gemacht hat
Sobald Tickets Datensätze waren statt Kanäle, ließen sich Fragen beantworten, die vorher niemand stellen konnte.
Wie lange dauert die erste Antwort? Der wichtigste Wert für alle, die warten. Bei uns lag er anfangs bei mehreren Stunden, im schlechtesten Fall bei Tagen — und niemand im Team hatte das so eingeschätzt.
Welche Kategorien erzeugen die meiste Arbeit? Nicht die mit den meisten Tickets, sondern die mit den längsten Verläufen. Zwei Kategorien haben die Hälfte der Bearbeitungszeit verursacht.
Welche Fragen kommen immer wieder? Die Grundlage für eine Sammlung häufiger Fragen — und die hat das Ticketaufkommen danach spürbar gesenkt.
Alle drei Auswertungen waren nur möglich, weil ein Ticket einen Zustand mit Zeitstempeln hatte. Vorher wäre die Antwort auf jede dieser Fragen ein Bauchgefühl gewesen.
Die Automatik, die ich zurückgebaut habe
Ich hatte eine Regelsprache gebaut, mit der sich beliebige Abläufe zusammenklicken lassen. Benutzt wurden drei Muster.
Was ich daraus gelernt habe, ist keine Absage an Konfigurierbarkeit, sondern eine Reihenfolge: Erst die drei konkreten Fälle fest einbauen, dann beobachten, ob ein vierter kommt. Wenn er kommt und sich vom Muster unterscheidet, ist das der Zeitpunkt für eine Verallgemeinerung — dann kennt man drei echte Anwendungsfälle statt eines echten und zweier erfundener.
Der Umbau in die andere Richtung war übrigens teurer als der Aufbau. Eine bestehende Regelsprache abzuschaffen bedeutet, jede konfigurierte Regel in festen Code zu überführen und dabei niemandem etwas wegzunehmen. Verallgemeinerung ist leicht einzuführen und schwer zurückzunehmen — das ist der eigentliche Grund, vorsichtig damit zu sein.
Der Zustand, den ich zuletzt eingebaut habe
Ein Zustand hat lange gefehlt, und sein Fehlen hat Arbeit verursacht, die niemandem aufgefallen ist: wartet auf Antwort des Erstellers.
Vorher gab es „offen" und „geschlossen". Ein Ticket, in dem das Team geantwortet hatte und auf eine Rückmeldung wartete, sah aus wie eines, das unbearbeitet ist.
Die Folge: In der Liste offener Tickets standen dutzende, bei denen der Ball nicht beim Team lag. Das erzeugt ein schlechtes Gewissen ohne Handlungsmöglichkeit — und es verdeckt die, bei denen tatsächlich jemand wartet.
Mit dem zusätzlichen Zustand wurde aus einer langen Liste eine kurze. Und daran hängt eine Automatik, die tatsächlich sinnvoll ist: Nach einigen Tagen ohne Reaktion des Erstellers wird das Ticket mit Hinweis geschlossen — es kann jederzeit wieder geöffnet werden.
Das ist die Sorte Änderung, die technisch trivial ist und den Alltag verändert: Eine Liste, die nur enthält, was tatsächlich anliegt, wird auch angesehen.
Was ich rückblickend anders machen würde
Ich habe die Automatikregeln zu allgemein gebaut — mit Bedingungen, Aktionen und Verknüpfungen, praktisch eine kleine Regelsprache. Benutzt wurden am Ende drei Muster: benachrichtigen bei Öffnung, erinnern nach X Tagen, schließen nach Y Tagen Inaktivität.
Drei feste Regeln mit ein paar Einstellungen hätten dasselbe geleistet und wären in einem Bruchteil der Zeit fertig gewesen. Stattdessen habe ich ein System gebaut, das theoretisch alles kann, praktisch drei Dinge tut und dabei eine Oberfläche braucht, die niemand auf Anhieb versteht.
Das ist ein Fehler, den ich mit Ansage gemacht habe. Er hat einen Namen, den ich mir seitdem regelmäßig vorsage: die Verallgemeinerung, für die es nur einen Anwendungsfall gibt.