Der Spickzettel für alles nach der ersten laufenden Oberfläche: Wo die Daten liegen, wer was darf, wie geprüft und ausgerollt wird. Das erste Blatt, App bauen, deckt Module, Manifest, Komponenten und Resolver ab.
Für Manifest-Schlüssel, CLI-Flags und die jeweils geltenden Plattformgrenzen bleibt die Atlassian-Doku maßgeblich — die Zahlen hier sind der Stand des Seminars.
Speicher nach Bedarf
| Speicher | Passt bei | Grenze |
|---|---|---|
| Key-Value Store | Einstellungen, kleine Listen | Abfrage nur über den Schlüssel |
| Custom Entity Store | Suche über mehrere Merkmale | 20 Entitäten je App |
| Forge SQL | relationale Modelle, Auswertungen | eigener Betrieb im Entwurf |
Eine Entität führt bis zu 50 Attribute und bis zu sieben eigene Indizes, ein Eintrag darf höchstens 240 KiB groß und 31 Ebenen tief sein.
import { kvs } from '@forge/kvs';
const schluessel = `klarpfad:${vorgangId}`;
await kvs.set(schluessel, { eintraege });
const daten = await kvs.get(schluessel);
await kvs.delete(schluessel);
Die Ablage ist je Installation getrennt — dieselbe App auf zwei Sites teilt keine Daten. Die Wahl richtet sich nach dem Zugriffsmuster, nicht nach der Vertrautheit: Wer nur über einen Schlüssel abfragt, braucht keinen Entity Store.
Modul: Daten passend zum Anwendungsfall speichern
Datenmodell und Schlüssel
app:
storage:
entities:
- name: entscheidung
attributes:
vorgangId: { type: string }
erfasstAm: { type: string }
indexes:
- name: by-vorgang
partition: [vorgangId]
Der Index richtet sich nach den Abfragen, nicht nach der Anzeige. Ein Schlüssel, der den Anzeigetitel enthält, bricht beim ersten Umbenennen; eine Kennung, die im Frontend entsteht, ist beeinflussbar. Vorgangsfelder gehören nicht in den App-Speicher kopiert — dort veralten sie.
Modul: Daten passend zum Anwendungsfall speichern
Scopes sparsam schneiden
# zu breit: erlaubt Änderungen an allen Vorgängen
permissions:
scopes:
- manage:jira-configuration
# passend: lesen, und schreiben nur wo nötig
permissions:
scopes:
- read:jira-work
- write:jira-work
Faustregel: Ein Scope, der sich einem Administrator nicht in einem Satz erklären lässt, ist zu breit.
Was eine Scope-Erweiterung auslöst: neue Zustimmung bei der Installation, kein automatisch durchlaufendes Upgrade, im Unternehmen beginnen Sicherheitsprüfungen von vorn. Das Entfernen eines Scopes ist unkritisch, das Hinzufügen nicht.
Modul: Sicherheitsmodell und Verantwortung · Entwicklungsumgebung und App-Lebenszyklus
Die Vertrauensgrenze
Browser → Bridge → Resolver → Plattform. Vor der Grenze ist jede Angabe beeinflussbar, dahinter ist sie plattformseitig belegt.
resolver.define('setStatus', async (req) => {
const { accountId } = req.context; // nicht aus der Nutzlast
const eintrag = await lade(req.payload.id);
if (eintrag.erfasstVon !== accountId)
return { ok: false, grund: 'fremd' };
return { ok: true, eintrag: await aendere(eintrag) };
});
const STATUS = ['offen', 'entschieden', 'verworfen'];
resolver.define('addEntscheidung', async (req) => {
const { titel, status } = req.payload ?? {};
if (!titel || titel.length > 120) return { ok: false, grund: 'titel' };
if (!STATUS.includes(status)) return { ok: false, grund: 'status' };
return { ok: true, eintrag: await speichern(req) };
});
Ein Feld ok trennt den Fachfehler vom technischen Fehler — beide brauchen
verschiedene Meldungen. Fachfehler sind Ergebnisse, keine Ausnahmen; wer jeden
Fehler wirft, zeigt Nutzenden Systemtexte.
Eine ausgeblendete Schaltfläche ist keine Autorisierung. Und was fürs Lesen gilt, gilt fürs Schreiben doppelt.
Modul: Frontend und Backend verbinden · Sicherheitsmodell und Verantwortung
Externe Zugriffe und Protokolle
permissions:
external:
fetch:
backend:
- api.falkenmoos-labor.example
Fehlt die Domain, scheitert der Aufruf ohne sprechende Meldung. Platzhalter erlauben mehr als eine Domain und erweitern damit die Angriffsfläche. Zugangsdaten gehören in Umgebungsvariablen, nie ins Frontend — dort sind sie öffentlich.
// zu viel: Inhalt und Person im Klartext
console.log('speichere', req.payload, req.context);
// genug: Vorgang, Aktion, Ausgang
console.log('klarpfad.speichern', { vorgangId, aktion: 'anlegen', ok: true });
Eine Korrelationskennung je Aufruf hilft mehr als der vollständige Inhalt. Und: auch den Verlauf protokollieren, nicht nur den Fehlerfall.
Modul: Sicherheitsmodell und Verantwortung
Tests: Fachregel und Grenze
// src/logik/status.js — ohne Plattform prüfbar
export const darfWechseln = (von, nach) =>
von === 'offen' && nach !== 'offen';
test('wechsel aus offen ist erlaubt', () => {
expect(darfWechseln('offen', 'entschieden')).toBe(true);
expect(darfWechseln('verworfen', 'offen')).toBe(false);
});
// Grenze: Plattformpaket durch eine Attrappe ersetzen
jest.mock('@forge/kvs', () => ({
kvs: { get: jest.fn(), set: jest.fn() }
}));
test('Status wird geprüft', async () => {
const antwort = await addEntscheidung({
payload: { titel: 'Messreihe', status: 'x' }
});
expect(antwort.grund).toBe('status');
});
Die Regel bekommt einen Namen und eine eigene Datei — in der Komponente wäre sie ohne DOM nicht prüfbar, im Resolver bräuchte sie Kontext und Speicher. Geprüft werden Erfolgsfall, Fachfehler und der Ausfall der Gegenstelle; festgehalten wird die Antwortform, nicht nur der Wert.
Modul: Testen und Debuggen
Prüfliste vor einer Freigabe
| Fall | Erwartung |
|---|---|
| Frische Installation | Panel erscheint, Leerzustand sichtbar |
| Upgrade mit neuem Scope | Zustimmung wird verlangt |
| Nutzerin ohne Schreibrecht | Aktion wird abgewiesen, Meldung erscheint |
| Vorgang ohne Einträge | Leerzustand statt leerer Liste |
| Pflichtfeld leer | feldbezogene Fehlermeldung |
| Zweimal absenden | genau ein Eintrag entsteht |
| Deinstallation | keine Reste im Produkt sichtbar |
Geprüft wird auf einer sauberen Instanz und nicht nur mit dem eigenen Konto, das alle Rechte hat. Das Upgrade gehört dazu, nicht nur die Erstinstallation.
Modul: Testen und Debuggen
Fehlersuche
forge lint # Manifest und Projekt
forge tunnel # lokale Ausgaben mitlesen
forge logs -e production # Protokolle der Umgebung
forge logs --since 30m
Metriken und Fehlerraten stehen in der Entwicklerkonsole, nicht im Terminal. Protokolle ohne Umgebungsangabe sind leer, und ein Fehler im Frontend hinterlässt keine Spur im Backend-Protokoll.
Modul: Testen und Debuggen
Umgebungen und Rollout
forge deploy # development
forge deploy -e staging
forge deploy -e production
forge install -e production
forge install --upgrade -e production
Ohne -e arbeiten alle Befehle auf development — die häufigste
Verwechslung. Die Trennung ist nicht optional: Der Speicher hängt an der
Installation, Variablen unterscheiden sich je Umgebung, und ein fehlerhafter
Stand trifft nur die Umgebung, in der er steht.
Nach dem Deploy fehlt oft das Upgrade — dann läuft die alte Fassung weiter, und steht eine Zustimmung aus, hängt es unbemerkt.
Modul: Bereitstellung und Betrieb
Betrieb beobachten
| Größe | Quelle | Anzeichen für ein Problem |
|---|---|---|
| Fehlerrate | Metriken | Anstieg nach einem Release |
| Laufzeit | Metriken | schleichend längere Aufrufe |
| Aufrufzahl | Metriken | Vervielfachung ohne mehr Nutzung |
| Meldungen | Protokolle | wiederkehrender Fachfehler |
| Verbrauch | Entwicklerkonsole | Kosten ohne neue Funktion |
Mehr Aufrufe bei gleicher Nutzung deuten fast immer auf einen Effekt ohne saubere Abhängigkeit.
Modul: Bereitstellung und Betrieb
Generierten Code prüfen
| Prüfpunkt | Typischer Befund |
|---|---|
| Paket und Import | @forge/ui statt @forge/react |
| Einstiegspunkt | ForgeUI.render statt ForgeReconciler |
| Komponentenname | TextField statt Textfield, Table statt DynamicTable |
| Manifest | render: native fehlt, function statt resource |
| Scopes | breiterer Scope als der Aufruf braucht |
| Kontext | Autorisierung auf Frontend-Werten |
Diese sechs Punkte decken die Mehrzahl der Fehler ab, die aus älteren Beispielen stammen. Ein Auftrag an einen Agenten trägt eine Ausschlussliste — was unverändert bleiben muss, etwa Manifest, Scopes und Speicherzugriff. Kein Protokollauszug mit Vorgangsinhalten in den Auftrag, keine Zugangsdaten in Dateien, die der Agent mitliest.
Modul: KI und Coding Agents im Forge-Workflow
Abnahme und Review
| Schritt | Woran er gemessen wird |
|---|---|
| Panel öffnen | Ladezustand sichtbar, dann Inhalt oder Leerzustand |
| Eintrag erfassen | Pflichtfelder geprüft, Rückmeldung nach dem Sichern |
| Doppelt absenden | genau ein Eintrag entsteht |
| Fremden Eintrag ändern | Abweisung mit verständlicher Meldung |
| Fehler auslösen | eigene Meldung, kein technischer Text |
| Entscheidungen erklären | Modul, UI-Modell, Speicher, Scopes begründet |
Architektur Modul und UI-Modell begründet?
Oberfläche Laden, Leerstand, Fehler je eigen?
Sicherheit Autorisierung im Resolver, Scopes sparsam?
Daten Speicher zum Zugriffsmuster, Doppelschutz?
Tests Fachregel und Grenze geprüft?
Betrieb Checkliste, Beobachtung, Rückzugsweg?
Eine App ohne offene Punkte wurde meist nicht ehrlich geprüft. Benannte Risiken lassen sich einplanen, verschwiegene nicht.
Modul: Abschlussprojekt und Review
Typische Fallen
- Ein Befehl läuft ohne
-eund trifft die falsche Umgebung. - Eine Liste wächst im Key-Value Store, bis jede Abfrage alles lädt.
- Ein breiter Scope aus dem Prototyp bleibt stehen, und später weiß niemand, welcher Aufruf ihn braucht.
- Die
accountIdkommt aus der Nutzlast statt ausreq.context. - Geprüft wird nur im Formular, der Resolver vertraut der Nutzlast.
- Die ganze Nutzlast wird beim Debuggen protokolliert und bleibt stehen.
- Getestet wird die Attrappe, nicht die eigene Entscheidung.
- Gesucht wird im Tunnel, der Fehler tritt nur in Produktion auf.
- Historie sammelt sich unbegrenzt, ohne Plan fürs Aufräumen.
- Der Rückzugsweg ist beschrieben, aber nie ausprobiert.
Dieses Thema als Schulung für Ihr Team
Dieser Beitrag erklärt das Thema. Damit Ihr Team es danach auch anwendet, gibt es Atlassian Forge mit UI Kit als Schulung — an Ihrem eigenen Code, mit den Fragen, die ein Text nicht beantwortet. Sie wählen die Module, wir bauen daraus ein Programm.
5 Tage·ab 900 EUR netto pro Tag (bis 3 Teilnehmende) ·Termin nach Vereinbarung
Als Team-Schulung anfragenZum Seminar Atlassian Forge mit UI Kit →