Salzburger Software Modell

Das Salzburger Software Modell ist ein Fundament auf dem nachhaltige Software aufbaut. Es besteht aus Prozessen, Hilfsmitteln und Techniken die zu wiederholbarem Erfolg führen.

Nachhaltige Software erfüllt einen bestimmten Zweck und überlebt in chaotischen Umfeldern.

Es erfordert diszipliniertes Vorgehen um dieses erstrebenswerte Ziel zu erreichen. Glück, Zufall und Hoffnung dürfen nichts mit einem erfolgreichen Software Projekte zu tun haben.

Viel Spaß beim stöbern!

Merkmale von Software

Software gibt es in allen Farben und Formen. Diese Vielfältigkeit ermöglicht es auf den unterschiedlichsten Ebenen darüber zu sprechen. Das ist eine Herausforderung in der Kommunikation. Die Vielfältigkeit verleitet dazu in die Tiefe zu gehen. In vielen Situationen ist ein hoher Detailgrad jedoch hinderlich.

Für solche Situationen ist die Liste von Merkmalen gedacht. Sie ermöglicht es Software zu charakterisieren ohne dabei ins Detail zu gehen. Zum Beispiel wird die Planung beschleunigt durch das frühzeitige Sichtbarmachen von Komplexität.

Die Merkmale sind auch in der Implementierung nützlich. Sie sind ein guter Einstiegspunkt für Fragen zur Implementierung. Ebenso sind sie als Gedankenstütze und Referenzdokument geeignet.

Abfrage

Macht Informationen für Benutzeroberflächen und Schnittstellen zugänglich.

Beispiel: Kunden und Kundinnen die offene Rechnungen haben.

Checkliste

Befehl

Stoßt einen Prozess innerhalb der Software an.

Beispiel: Stellenangebot sichtbar schalten.

Checkliste

Benutzereingaben

Daten die von BenutzerInnen stammen. Werden meist über ein Formular erfasst.

Beispiel: Leistungsbeschreibung bei der Angebotsstellung.

Checkliste

  • Wie werden die Daten validiert?
  • Wird gegen klassische Angriffsvektoren geschützt?
  • Sind Videos inkludiert?
  • Sind Bilder inkludiert?
  • Sind sonstige Dateien inkludiert?

Benutzeroberfläche

Ermöglicht Menschen mit der Software zu interagieren.

Beispiel: Übersicht aller erhaltenen Bestellungen.

Checkliste

  • Funktioniert es auf Desktop-Geräten?
  • Funktioniert es auf Tablet-Geräten?
  • Funktioniert es auf Mobile-Geräten?
  • Ist es barrierefrei?
  • Funktioniert es ohne JavaScript?

Langlebiger Client

Je nach Implementierung kann die Benutzeroberfläche lange in Betrieb sein. Typische Beispiele sind Single-Page-Webanwendung.

Beispiel: Mobile Anwendung die Offline fähig ist.

Checkliste

  • Wie werden Versionskonflikte in der Schnittstelle zwischen Client und Server gehandhabt?
  • Ist ein Schutz gegen Gleichzeitigkeit gewährleistet?
  • Ist ein Schutz gegen Fehlerszenarien eines externen Dienstes gewährleistet?
  • Wie kann der Client per Fernwartung aktualisiert werden?

Schnittstelle

Ermöglicht externen Systemen mit der Software zu interagieren.

Beispiel: Einspielen von Stellenangeboten aus SAP.

Checkliste

  • Ist eine Authentifizierung nötig?
  • Ist eine Zugriffskontrolle nötig?
  • Welches Format wird verwendet?
  • Wie wird das Schema dokumentiert?
  • Wie wird die Verwendung dokumentiert?
  • Wie wird Versionierung gehandhabt?

Authentifizierung

Teile der Software sind nur verwendbar nachdem sich Mensch oder Maschine erfolgreich authentifiziert haben.

Beispiel: Jobbörsen Verwaltungsbereich ist nur mit erfolgreicher Authentifizierung zugänglich.

Checkliste

  • Wird die Authentifizierung innerhalb der Software vorgenommen?
  • Ist eine Zwei-Faktor-Authentifizierung angebracht?
  • Wie kann eine Authentifizierung entzogen werden?
  • Wer kann sich authentifizieren?

Zugriffskontrolle

Der Zugriff auf Befehle und Abfragen wird individuell kontrolliert.

Beispiel: Die Buchhaltung kann Rechnungen sehen jedoch nicht stellen.

Checkliste

  • Setzt eine Authentifizierung voraus.
  • Wie werden die Berechtigungen kontrolliert?
  • Wie sieht das Berechtigungsmodell aus?

Mandantenfähigkeit

Die selbe Instanz der Software bedient multiple, voneinander unabhängige, Mandanten. Mandanten haben nur Einsicht in ihre eigenen Daten. Nicht zu verwechseln mit Mehrfachsitzungen.

Beispiel: Shopify wird für unterschiedliche Online-Shops genutzt.

Checkliste

  • Wie werden die Daten zwischen Mandanten getrennt?
  • Gibt es Basisdaten auf die alle Mandanten zurückgreifen?
  • Sind getrennte Backups nötig?
  • Wie werden Mandanten hinzugefügt?
  • Wie werden Mandanten entfernt?

Mehrfachsitzungen

Multiple Menschen oder Maschinen interagieren mit der Software gleichzeitig.

Beispiel: Zwei BuchhalterInnen sind für die Rechnungsstellung verantwortlich.

Checkliste

Hintergrundprozess

Wird genutzt um rechenintensive Aufgaben im Hintergrund abzuarbeiten ohne die Rückmeldung an BenutzerInnen zu verzögern. Wird oft von einem Befehl angestoßen.

Beispiel: Versand von Emails.

Checkliste

Terminierter Befehl

Ein Befehl wird zu einem Zeitpunkt in der Zukunft ohne Benutzerinteraktion gegeben.

Beispiel: Stellenangebot 30 Tage nach dem sichtbar schalten automatisch verbergen.

Checkliste

Zeit

Zeitangaben werden gespeichert und verarbeitet.

Beispiel: Stellenangebote sind nur in einem bestimmten Zeitraum sichtbar.

Checkliste

  • Ist es nötig Zeitzonen zu berücksichtigen?
  • Werden Zeitangaben einheitlich persistiert?

PDF Erstellung

Zur Laufzeit erstellte PDF Dateien.

Beispiel: Angebot kann als PDF heruntergeladen werden.

Checkliste

Import von Daten

Große Mengen an Daten werden in einem einzigen Arbeitsschritt importiert.

Beispiel: Verkaufszahlen werden aus einem Excelsheet importiert.

Checkliste

  • Um welche Datenmengen handelt es sich?
  • Wie oft werden Daten importiert?
  • Wie werden die Daten validiert?
  • Welche Formate werden unterstützt?
  • Findet der Import in einem Hintergrundprozess statt?

Export von Daten

Daten werden für eine externe Weiterverwendung exportiet.

Beispiel: Prozess Statistiken werden als Excelsheet exportiert.

Checkliste

  • Um welche Datenmengen handelt es sich?
  • Wie oft werden Daten exportiert?
  • Welche Formate werden unterstützt?
  • Findet der Export in einem Hintergrundprozess statt?

Externer Dienst

Gewisse Aufgaben werden an Dritte ausgelagert. Strenggenommen zählt dazu auch der Datenbankserver.

Beispiel: Email Versand wird von einem Dienstleister übernommen um bessere Zustellraten zu erreichen.

Checkliste

  • Wird dieser externe Dienst wirklich benötigt?
  • Wie wirkt sich eine Verschlechterung des externen Dienstes aus?
  • Wie wirkt sich ein Ausfall des externen Dienstes aus?
  • Wie wirkt sich eine Stilllegung des externen Dienstes aus?
  • Werden personenbezogene Daten übermittelt?
  • Unterstützt der externe Dienst Idempotenz?
  • Sind die Kompetenzen für Betrieb & Wartung vorhanden?

Personenbezogene Daten

Es werden personenbezogene Daten gespeichert und verarbeitet.

Beispiel: Kundenportal mit Email und Passwort Zugang.

Checkliste

  • Welche personenbezogene Daten werden erfasst?
  • Wie werden die Betroffenen Rechte gehandhabt?
  • Wie werden die Einwilligungen eingeholt?
  • Wie werden die Einwilligungen persistiert?
  • Welche externen Dienste verarbeiten personenbezogene Daten?

Online Bezahlung

BenutzerInnen können selbstständig Online bezahlen und Zahlungsstatus wird automatisch erfasst.

Beispiel: Unternehmen können Stellenangebote direkt auf der Jobbörse bezahlen.

Checkliste

Email Versand

Transaktionale Emails die über individuelle Vorkommnisse informieren.

Beispiel: Bestellbestätigung über den gerade getätigten Einkauf.

Checkliste

  • Wie wird eine fehlgeschlagene Zustellung gehandhabt?
  • Wird eine HTML-Email benötigt?
  • Gibt es eine Klartext Version von HTML-Emails?
  • Ist die Idempotenz gewährleistet?
  • Kann eine Vorschau angezeigt werden?
  • Findet der Versand in einem Hintergrundprozess statt?
  • Welche personenbezogene Daten werden erfasst?

Newsletter Versand

Transaktionale Emails die über allgemeine Vorkommnisse informieren.

Beispiel: Ankündigung eines neuen Produktes.

Checkliste

  • Wie wird eine fehlgeschlagene Zustellung gehandhabt?
  • Wird eine HTML-Email benötigt?
  • Gibt es eine Klartext Version von HTML-Emails?
  • Ist die Idempotenz gewährleistet?
  • Kann eine Vorschau angezeigt werden?
  • Findet der Versand in einem Hintergrundprozess statt?
  • Ist ein Probeversand möglich?
  • Welche personenbezogene Daten werden erfasst?

Implementierung

Idempotenz

Eine idempotente Abfrage verändert keine Daten. Jede Abfrage sollte idempotent sein. Wenn das nicht möglich ist handelt es sich wahrscheinlich um keine Abfrage sondern einen Befehl.

Ein Befehl führt in der Regel zu veränderten Daten. Die Veränderungen finden unter Umständen nicht nur innerhalb der Software sondern auch bei externen Diensten statt. Im Gegensatz zu Abfragen bedeutet es bei Befehlen etwas anderes wenn von Idempotenz gesprochen wird.

Ein Befehl führt natürlich zu Veränderung, aber nur beim ersten Mal. Wenn der selbe Befehl mehrfach entgegen genommen wird hat nur der erste Auswirkungen.

Diese Eigenschaft ist in der Praxis für robuste Systeme sehr nützlich. Bei einem Implementierungsfehler wodurch ein Befehl mehrfach ausgelöst wird entsteht kein Schaden. In einer Situation mit schlechter Netzwerkverbindung können Befehle erneut gesendet werden. Das passiert zum Beispiel auch automatisch durch einen Webbrowser. Diese Wiederholung von HTTP-Requests führt ohne idempotente Befehle zu Problemen.

Implementierung

Folgender Ansatz funktioniert nur wenn eine einzige, ACID kompatible, Datenspeicher verwendet wird und es zu keinen sonstigen Datenveränderungen in externen Diensten kommt. Wenn diese Voraussetzungen nicht gegeben sind muss mit einem individuellen Wiederherstellungs-Prozess gearbeitet werden.

In einer Situation mit den beschriebenen Voraussetzungen ist der erste Schritt das garantieren einer sequenziellen Abarbeitung von identischen Befehlen. In PostgreSQL kann das mit dem Isolations Level Serializable von Transaktionen erreicht werden. (Relevante Dokumentation von PostgreSQL.)

Der zweite Schritt ist das erkennen dass ein Befehl bereits ausgeführt wurde. In diesem Fall wird das ausführen des Befehls übersprüngen und stattdessen sofort die Erfolgsantwort zurückgegeben.

Im folgenden Beispiel ist Pseudoquellcode für einen fiktiven HTTP-Endpunkt zur Registrierung eines Accounts zu sehen.

post '/accounts' do |request|
  email = request.parameters.fetch(:email)

  DB.transaction(isolation: :serializable) do
    account = Account.find_by(email: email)

    if account.exists?
      return successful_account_registration(account)
    else
      account = Account.create(email: email)

      return successful_account_registration(account)
    end
  end
end

Literatur

Angriffsvektoren

Eine Software die im Internet erreichbar ist muss gegen zufällige und gezielte Angriffe geschützt werden. Es ist zu empfehlen davon auszugehen das auch interne Netzwerke kompromitiert werden können. In diesem Fall muss jede Art von Software entsprechend geschützt werden.

Als Startpunkt ist die OWASP Top 10 Liste an Angriffsvektoren geeignet.

Literaturverzeichnis

Vorschau von Dokumenten

Bei Dokumenten wie Emails und PDFs ist es empfehlenswert eine Implementierung zu wählen mit der unkomplizierte Vorschau Dokumente generiert werden können.

Um das zu erreichen muss eine Entkopplung von Frameworks und Bibliotheken erreicht werden die nicht zwingend nötig sind. Im Idealfall werden nur primitive Daten übergeben um das entsprechende Dokumente zu generieren.

Dieses Vorgehen verkürzt die Feedbackschleife in der Implementierung erheblich. Unterschiedliche Szenarien und Versionen des Dokuments können mit niedrigen Kosten erstellt werden.

Außerdem erleichtert dieser Ansatz die Entwicklung von Vorschau Funktionalität gegenüber BenutzerInnen.

Volltextsuche

Ist die PostgreSQL Volltextsuche ausreichend oder muss wirklich ein zusätzlicher externer Dienst verwendet werden?

Facetten Navigation

Möglichst schnell einen Prototypen bauen um herausfinden was benötigt wird und wie es am besten funktioniert.

  • Welche Facetten gibt es?
  • Wie sehen die Facetten Optionen aus?
  • Können mehrere Facetten aktiv sein?
  • Können mehrere Optionen einer Facette aktiv sein?

Transaktionale Hintergrundprozesse

Hintergrundprozesse die auf einem externen, zweiten Datenspeicher, aufbauen müssen nach einem bestimmten Schema implementiert sein um Robustheit zu erlangen.

Wenn die Zeile queue_job(stuff) in folgendem Beispiel direkt in den zweiten Datenspeicher schreibt gibt es ein Problem. Wenn Sidekiq wie empfohlen verwendet wird ist die Problematik immer da.

DB.transaction do
  stuff = do_and_save_stuff_to_database
  queue_job(stuff)
  do_other_stuff
end

Was ist das Problem?

Es könnte in do_other_stuff zu einem Fehler kommen der die Transaktion abbricht. In diesem Fall gibt es einen Hintergrundprozess der sich auf nicht vorhandene Daten bezieht.

Alternativ könnte der Hintergrundprozess sofort abgearbeitet werden bevor die Daten in den primären Datenspeicher geschrieben wurden.

Wenn der Hintergrundprozess nach der Transaktion angestoßen wird gibt es ein weiteres Fehlerszenario. Die Transaktion schreibt Daten in den primären Datenspeicher um dann später zu merken dass der Hintergrundprozess nicht angestoßen werden konnte.

Alle diese Situation sind nicht wünschenswert. Eine mögliche Lösung wird in Transactionally Staged Job Drains in Postgres aufgezeigt.

Literaturverzeichnis

Gleichzeitigkeit

Mit Mehrfachsitzungen kann es zu Situationen kommen wo Befehle gleichzeitig abgearbeitet werden. Wenn sich diese Befehle widersprechen kommt es im schlimmsten Fall zu einem Datenverlust der nicht bemerkt wird.

Im Idealfall sendet deshalb jeder Befehl eine Version auf der er basiert. Das ermöglicht es sich überschneidende Befehle zu entdecken und darauf zu reagieren.

Fehler Szenarien

HTTP Retry

Wenn ein Webbrowser keine Antwort auf einen HTTP-Request bekommt weil zum Beispiel die Verbindung unterbrochen wurde wiederholt er automatisch den HTTP-Request. Dieses Verhalten ist für die BenutzerInnen nicht bemerkbar. Aus diversen Gründen werden auch POST und PUT HTTP-Requests wiederholt. Was wiederum zu Problemen und inkonsistenten Daten führen kann.

The status quo, therefore, is that no Web application can read HTTP’s retry requirements as a guarantee that any given request won’t be retried, even for methods that are not idempotent. As a result, applications that care about avoiding duplicate requests need to build a way to detect not only user retries but also automatic retries into the application “above” HTTP itself. https://mnot.github.io/I-D/Abandoned/httpbis-retry/#auto_retry

In der Praxis müssen demnach auch POST und PUT HTTP-Endpunkte idempotent sein. Details dazu sind unter Implementierung von Idempotentz zu finden.

Die Implementierung für HTTP-Endpunkte ist identisch zu sonstigen idempotenten Befehlen. Wie in der folgenden Visualisierung zu sehen ist gibt es drei verschiedene Szenarien.

  1. Der Original HTTP-Request (Request) wird von der Software vollständig abgearbeitet bevor der Retry HTTP-Request (Request') ankommt.

  2. Der Retry HTTP-Request (Request'') kommt an bevor die Verarbeitung des Originals abgeschlossen ist. Die Verarbeitung des Originals ist jedoch schneller.

  3. Der Retry HTTP-Request (Request''') kommt an bevor die Verarbeitung des Originals abgeschlossen ist. Die Verarbeitung des Originals ist jedoch langsamer.

Visualisierung von HTTP-Request Retry Szenarien