This post is part of the series about my private BeagleBone Black project. The project uses StrictDoc for requirements management — no Word document, no Jira ticket, just text files directly in the repository. What sounds odd turns out to be surprisingly sensible in practice.
What Is StrictDoc?
StrictDoc is an open-source tool for document-based requirements management.
Requirements are written in .sdoc text files
that live in the same repository as the code — and therefore in the same Git history.
Why not a classic ALM tool?
Jira, Azure DevOps, Polarion — all these tools share the same drawback: the requirements live separately from the code. A requirement changes, the code is adjusted, but nobody links the two explicitly. A year later, nobody knows anymore whether the implementation still matches the requirement.
StrictDoc forces the link to happen. A code comment references a requirement ID, and StrictDoc can check whether every requirement has at least one implementation reference.
StrictDoc is not a replacement for DOORS in safety-critical systems with regulatory requirements. For a private project, it’s exactly right. |
Document Structure
StrictDoc documents are .sdoc text files with a simple syntax.
Basic Structure
[DOCUMENT]
TITLE: BeagleBone Black — GPIO Requirements
[SECTION]
TITLE: Functional Requirements
[REQUIREMENT]
UID: BBB-GPIO-001
STATUS: Active
TITLE: Read GPIO pin
STATEMENT: The system MUST be able to read the digital state of a configured GPIO pin.
[REQUIREMENT]
UID: BBB-GPIO-002
STATUS: Active
TITLE: Set GPIO pin
STATEMENT: The system MUST be able to set a configured GPIO pin to HIGH or LOW.
RELATIONS:
- TYPE: Refines
VALUE: BBB-GPIO-001Important fields:
UIDUnique ID — referenced in the code. Never change after the first commit.
STATUSActive,Draft,ObsoleteSTATEMENTThe actual requirement — using SHALL/MUST/SHOULD if you use EARS notation
RELATIONSLink to other requirements (Refines, Implements, Verifies)
Hierarchy: System to Software
I separate requirements into three levels:
docs/requirements/
system/
gpio.sdoc ← system requirements (what the system must be able to do)
software/
hal-gpio.sdoc ← software requirements (how the HAL implements it)
api-gpio.sdoc ← API requirements (how the REST API exposes it)
tests/
gpio-tests.sdoc ← test cases (how it's verified)Writing Requirements — GPIO Example
System Level (system/gpio.sdoc)
[DOCUMENT]
TITLE: System — GPIO
[REQUIREMENT]
UID: SYS-GPIO-001
TITLE: Digital output
STATEMENT: The system MUST provide at least 4 independent digital outputs.
[REQUIREMENT]
UID: SYS-GPIO-002
TITLE: Digital input
STATEMENT: The system MUST provide at least 4 independent digital inputs.Software Level (software/hal-gpio.sdoc)
[DOCUMENT]
TITLE: HAL — GPIO
[REQUIREMENT]
UID: SW-GPIO-001
TITLE: Read GPIO via sysfs
STATEMENT: The HAL MUST read GPIO states via the Linux sysfs interface.
RELATIONS:
- TYPE: Implements
VALUE: SYS-GPIO-002
[REQUIREMENT]
UID: SW-GPIO-002
TITLE: Write GPIO via sysfs
STATEMENT: The HAL MUST write GPIO states via the Linux sysfs interface.
RELATIONS:
- TYPE: Implements
VALUE: SYS-GPIO-001Traceability in the Code
The implementation reference sits directly in the source code as a comment.
C Driver
/**
* gpio_read - Reads the state of a GPIO pin 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 the C driver.
/// @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 verifies SW-GPIO-001 and 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)
}The |
StrictDoc HTML Export
# Installation
pip install strictdoc
# HTML export of all requirement documents
strictdoc export docs/requirements/ --output-dir out/requirementsThe HTML export shows:
All requirements with status and text
Traceability matrix: which requirement has which implementation
Gaps: requirements without a code reference (marked in red)
Coverage level per document
Gap Analysis
StrictDoc highlights requirements without an implementation reference. That’s the real value: not just documenting what’s implemented, but seeing what’s still missing.
strictdoc check docs/requirements/
# [ERROR] SW-GPIO-003: no implementation reference foundCI Integration
In the 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/A failed StrictDoc check blocks the merge — just like a failed unit test.
Conclusion
Is StrictDoc worth it for a private project?
Yes, if:
you want to learn what requirements management looks like in practice
you want to see how code and requirements drift apart (they always do)
you want to show a portfolio project that goes beyond "I wrote some code"
No, if:
you want fast results and see documentation as overhead
the project only runs for a few weeks
For me, it’s the right choice. Professionally, I work in an environment where traceability is a regulatory requirement — this is my practice ground for understanding what that actually means, beyond the process documents.
Next post in the series: Drone CI with Podman