Dieser Post ist Teil der Serie über mein privates BeagleBone-Black-Projekt. Das Projekt nutzt StrictDoc für Anforderungsmanagement — kein Word-Dokument, kein Jira-Ticket, sondern Textdateien direkt im Repository. Was merkwürdig klingt, ist in der Praxis überraschend sinnvoll.

Was ist StrictDoc?

StrictDoc ist ein Open-Source-Tool für dokumentenbasiertes Anforderungsmanagement. Anforderungen werden in .sdoc-Textdateien geschrieben, die im selben Repository wie der Code leben — und damit auch im selben Git-Verlauf.

Warum kein klassisches ALM-Tool?

Jira, Azure DevOps, Polarion — all diese Tools haben denselben Nachteil: die Anforderungen leben getrennt vom Code. Eine Anforderung wird geändert, der Code angepasst, aber niemand verknüpft beides explizit. Nach einem Jahr weiß niemand mehr ob die Implementierung noch zur Anforderung passt.

StrictDoc zwingt zur Verknüpfung. Ein Code-Kommentar referenziert eine Anforderungs-ID, und StrictDoc kann prüfen ob jede Anforderung mindestens eine Implementierungs-Referenz hat.

StrictDoc ist kein DOORS-Ersatz für sicherheitskritische Systeme mit regulativen Anforderungen. Für ein privates Projekt ist es genau richtig.

Dokumentenstruktur

StrictDoc-Dokumente sind .sdoc-Textdateien mit einer einfachen Syntax.

Basis-Struktur

[DOCUMENT]
TITLE: BeagleBone Black — GPIO Anforderungen

[SECTION]
TITLE: Funktionale Anforderungen

[REQUIREMENT]
UID: BBB-GPIO-001
STATUS: Active
TITLE: GPIO Pin lesen
STATEMENT: Das System MUSS den digitalen Zustand eines konfigurierten GPIO-Pins lesen können.

[REQUIREMENT]
UID: BBB-GPIO-002
STATUS: Active
TITLE: GPIO Pin setzen
STATEMENT: Das System MUSS einen konfigurierten GPIO-Pin auf HIGH oder LOW setzen können.
RELATIONS:
- TYPE: Refines
  VALUE: BBB-GPIO-001

Wichtige Felder:

UID

Eindeutige ID — wird im Code referenziert. Nie ändern nach dem ersten Commit.

STATUS

Active, Draft, Obsolete

STATEMENT

Die eigentliche Anforderung — mit SHALL/MUST/SHOULD wenn man EARS-Notation nutzt

RELATIONS

Verknüpfung zu anderen Anforderungen (Refines, Implements, Verifies)

Hierarchie: System bis Software

Ich trenne Anforderungen in drei Ebenen:

docs/requirements/
  system/
    gpio.sdoc       ← Systemanforderungen (was das System können muss)
  software/
    hal-gpio.sdoc   ← Software-Anforderungen (wie der HAL es umsetzt)
    api-gpio.sdoc   ← API-Anforderungen (wie die REST API es exponiert)
  tests/
    gpio-tests.sdoc ← Testfälle (wie verifiziert wird)

Anforderungen schreiben — GPIO-Beispiel

Systemebene (system/gpio.sdoc)

[DOCUMENT]
TITLE: System — GPIO

[REQUIREMENT]
UID: SYS-GPIO-001
TITLE: Digitaler Ausgang
STATEMENT: Das System MUSS mindestens 4 unabhängige digitale Ausgänge bereitstellen.

[REQUIREMENT]
UID: SYS-GPIO-002
TITLE: Digitaler Eingang
STATEMENT: Das System MUSS mindestens 4 unabhängige digitale Eingänge bereitstellen.

Softwareebene (software/hal-gpio.sdoc)

[DOCUMENT]
TITLE: HAL — GPIO

[REQUIREMENT]
UID: SW-GPIO-001
TITLE: GPIO lesen über sysfs
STATEMENT: Der HAL MUSS GPIO-Zustände über das Linux sysfs-Interface lesen.
RELATIONS:
- TYPE: Implements
  VALUE: SYS-GPIO-002

[REQUIREMENT]
UID: SW-GPIO-002
TITLE: GPIO schreiben über sysfs
STATEMENT: Der HAL MUSS GPIO-Zustände über das Linux sysfs-Interface schreiben.
RELATIONS:
- TYPE: Implements
  VALUE: SYS-GPIO-001

Traceability im Code

Die Implementierungs-Referenz sitzt direkt im Quellcode als Kommentar.

C-Treiber

/**
 * gpio_read - Liest den Zustand eines GPIO-Pins via sysfs.
 *
 * @req SW-GPIO-001
 */
int gpio_read(int pin, int *value) {
    char path[64];
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);

    FILE *f = fopen(path, "r");
    if (!f) return -ENODEV;

    fscanf(f, "%d", value);
    fclose(f);
    return 0;
}

Rust HAL

/// GPIO-Read via C-Treiber.
/// @req SW-GPIO-001
pub fn gpio_read(pin: u32) -> Result<u8, HalError> {
    let mut value: i32 = 0;
    let ret = unsafe { c_gpio_read(pin as i32, &mut value) };
    if ret < 0 {
        return Err(HalError::GPIOReadFailed(ret));
    }
    Ok(value as u8)
}

Test

// TestGPIORead verifiziert SW-GPIO-001 und SYS-GPIO-002.
// @req SW-GPIO-001
// @req SYS-GPIO-002
func TestGPIORead(t *testing.T) {
    h := hal.NewMockHAL()
    h.SetGPIOValue(48, 1)

    val, err := h.GPIORead(48)
    assert.NoError(t, err)
    assert.Equal(t, 1, val)
}

Das @req-Tag ist eine Konvention, kein StrictDoc-Feature. StrictDoc selbst bietet Source-File-Tracing mit konfigurierbarem Pattern.

StrictDoc HTML-Export

# Installation
pip install strictdoc

# HTML-Export aller Anforderungsdokumente
strictdoc export docs/requirements/ --output-dir out/requirements

Der HTML-Export zeigt:

  • Alle Anforderungen mit Status und Text

  • Traceability-Matrix: welche Anforderung hat welche Implementierung

  • Lücken: Anforderungen ohne Code-Referenz (rot markiert)

  • Abdeckungsgrad pro Dokument

Lückenanalyse

StrictDoc hebt Anforderungen ohne Implementierungs-Referenz hervor. Das ist der eigentliche Mehrwert: nicht nur dokumentieren was implementiert ist, sondern sehen was noch fehlt.

strictdoc check docs/requirements/
# [ERROR] SW-GPIO-003: no implementation reference found

CI-Integration

In der Drone-Pipeline:

steps:
  - name: requirements-check
    image: python:3.11-slim
    commands:
      - pip install strictdoc --quiet
      - strictdoc export docs/requirements/
          --output-dir out/requirements
          --formats=html
      - strictdoc check docs/requirements/

Ein fehlgeschlagener StrictDoc-Check blockiert den Merge — genau wie ein fehlgeschlagener Unit-Test.

Fazit

Lohnt sich StrictDoc für ein privates Projekt?

Ja, wenn:

  • man lernen will wie Anforderungsmanagement in der Praxis aussieht

  • man sehen will wie Code und Anforderungen auseinanderlaufen (sie tun es immer)

  • man ein Portfolio-Projekt zeigen will das über "ich habe Code geschrieben" hinausgeht

Nein, wenn:

  • man schnell Ergebnisse will und Dokumentation als Overhead sieht

  • das Projekt nur wenige Wochen läuft

Für mich ist es die richtige Wahl. Ich arbeite beruflich in einem Umfeld wo Traceability eine regulatorische Anforderung ist — das hier ist mein Übungsfeld um zu verstehen was das wirklich bedeutet, jenseits der Prozessdokumente.


Nächster Post in der Serie: Drone CI mit Podman