1. Dezember 2012

AIM: mein erstes Projekt — ein Webservice, den Handcode-Entwickler wirklich nutzten

ZeitraumDez 2012 – Jun 2014
Kunde / KontextDaimler AG (Driver Assistance) · via Elektrobit
RolleTechnischer Projektleiter, Scrum Master & Entwickler (AIM-Service)
Problem

Das Enterprise-Architect-Reverse-Modell half der Vorentwicklung, aber Serien-Handcode-Entwickler empfanden es als zusätzlichen Pflegeaufwand. AUTOSAR 3.1.4 bildete Schnittstellen nur flach ab und passte nicht zu handcode-typischen Strukturen (verschachtelte Structs, Pointer).

Soll

Einen einfachen, webbasierten Service schaffen, mit dem Handcode-Entwickler ihre Schnittstellen selbst pflegen, versioniert vergleichen und daraus automatisiert konforme Header-Dateien generieren können — mit sofortigem Mehrwert statt zusätzlichem Prozessaufwand.

Ist / Ergebnis

AIM (Architecture and Interface Manager): ein Django-Webservice (WebForm + REST-API, LDAP-Auth) für ~40–50 SWCs mit Schnittstellensuche, versioniertem Diff, konformer Header-Generierung und Testbett-Anbindung. Der einmalige Header-Import wurde zugleich zum Compliance-Audit für den Kunden; nach gut einem Jahr produktiv, später Pflicht im Handcode-Prozess und an die CI/CD-Pipeline angebunden.

Tech-Stack
PythonDjangodjango-polymorphicDjango REST FrameworkMySQLLDAPAUTOSAR / ARXMLJavaScriptApacheJenkinsJira
Mitgebrachte Fähigkeiten
PythonSchnittstellenspezifikation & AUTOSAR-SWCsNetzwerk zu Funktionsentwicklern aus den RDU3/RDU4-Interviews
Entwickelte Fähigkeiten
Technische Projektleitung & PersonalverantwortungWebentwicklung (Django, REST-API, Datenbanken, LDAP-Auth)Standardnahe Datenmodellierung (AUTOSAR-Typen, django-polymorphic)Datenbank-Migrationen (Schema & Daten)Scrum Master (Zertifizierung), Product Backlog & Requirements EngineeringCI/CD mit Jenkins, TDD & Code-Coverage

Das hier war mein Leuchtturm: die erste echte Projektverantwortung meiner Laufbahn — technisch, fachlich und personell. Und sie begann mit einer unbequemen Wahrheit: Wir bekamen drei Monate, um eine Idee zu beweisen, für die uns die wichtigsten Fähigkeiten noch fehlten.

Ausgangslage: ein Modell, das die Serie nicht erreichte

Das Enterprise-Architect-Modell aus dem RDU3/RDU4-Projekt hatte einen Nerv getroffen — aber nur bei der Vorentwicklung. Bei den Serien-Handcode-Entwicklern kam etwas anderes an: Ein Architekturmodell muss kontinuierlich gepflegt werden, und dieser „jemand” waren am Ende wir zu zweit. Der Bedarf war trotzdem real — gerade in der C-Handcode-Welt, wo Schnittstellen kaum zugänglich und noch schwerer pflegbar waren.

Warum AUTOSAR für den Handcode nicht reichte

AUTOSAR existierte, und Daimler betrieb dafür sogar ein weltweit eingesetztes Werkzeug. Nur: Auch das war den Handcode-Entwicklern zu schwer für den Alltag. Ihr Kopf steckte woanders — in Sensordatenfusion, Fahrdynamik-Regelung, den harten Prozessauflagen der Serie.

Kamera Klassifikation, Farbe, Fahrspur schwach bei Dunkelheit/Nebel Lidar präzise 3D-Distanz teuer, damals noch jung im Serieneinsatz Radar robust bei Wetter, misst Geschwindigkeit direkt grobe Objektklassifikation Ultraschall günstig, Nahbereich nur wenige Meter Reichweite Sensordatenfusion komplementär (mehr Sichtbereich) & konkurrierend (mehr Genauigkeit) Konsistentes Umfeldmodell
Die Welt der Handcode-Entwickler: Kein Sensor sieht alles, also kombiniert man mehrere. Ihre Zeit steckte in dieser Fachdomäne — nicht in der Pflege von Schnittstellenmodellen. Genau das musste AIM ihnen abnehmen.

Dazu kam eine harte technische Grenze der damaligen AUTOSAR-Version 3.1.4: Schnittstellen ließen sich nur flach abbilden. Structs ja — aber nur eine Hierarchieebene; verschachtelte Structs oder Pointer waren nicht darstellbar. Das hatte einen guten Grund: Eine AUTOSAR-SWC ist verschiebbar zwischen Steuergeräten, und ein Pointer ist eine feste Speicheradresse — auf einer verschiebbaren Komponente ergibt das keinen Sinn. Handcode dagegen war eng aufs jeweilige Target optimiert.

AUTOSAR-SWC (verschiebbar) struct { uint16 speed; uint8 status; } nur eine flache Hierarchieebene kein Pointer — Zieladresse variiert Vorteil: Komponente lässt sich auf jedes Steuergerät verschieben Handcode (target-gebunden) struct { struct Sensor s; Data_t *pData; } verschachtelte Structs üblich Pointer auf feste Speicheradresse auf das eine Ziel-Steuergerät optimiert, nicht auf SWC-Ebene abbildbar
Der Standard war nicht „falsch" — er löste ein anderes Problem (Verschiebbarkeit). Für über Jahre gewachsenen Handcode passte er schlicht nicht.

Man konnte die Entwickler zwingen, sofort auf den Standard umzustellen — oder man konnte sie dort abholen, wo sie standen. Wir entschieden uns für Letzteres.

Die Idee: ein Werkzeug in der Sprache der Entwickler

Ein teures, mächtiges Kommerzwerkzeug hat einen unterschätzten Nachteil: Man wird erschlagen von Möglichkeiten, Schaltflächen, Begriffen. Eine Individualentwicklung, die exakt auf die Bedürfnisse eines Kunden zielt, fühlt sich völlig anders an — schlank, verständlich, sofort nützlich. Genau das wollten wir bauen: kein weiteres Architektur-Framework, sondern einen Webservice, fokussiert auf C-Header und Schnittstellen. So entstand AIM — Architecture and Interface Manager.

Der Fokus lag bewusst auf der Provided-Seite des einzelnen SWC-Entwicklers, nicht auf der Required-Seite. Architektonisch nicht perfekt — jeder konnte weiter per #include alles ziehen. Aber genau dort war der Bedarf, und irgendwo fängt man an.

Was AIM konnte

Entwickler pflegten ihre Schnittstellen auf zwei Wegen: über die WebForm des Services oder über die REST-API (Django REST Framework). Die Authentifizierung lief über LDAP, und über das Django-interne User-Management konnten wir API-Tokens ausgeben — so ließ sich AIM auch von anderen Werkzeugen automatisiert ansprechen.

Handcode- Entwickler WebForm · REST-API AIM (Django-Webservice) Schnittstellen-Suche Versionierter Diff Header-Generierung Testbett-Generierung MySQL · LDAP-Login · API-Tokens (DRF) C-Header + CI/CD-Pipeline
Einmal im Browser pflegen — AIM übernimmt Suche, versionierten Vergleich, konforme Header-Generierung und später Testbett-Artefakte, direkt in der Build-Pipeline.

AUTOSAR im Hintergrund: wie AIMs Datenmodell entstand

An der Oberfläche blieb AIM bewusst C-vertraut: eine SWC war ein Ordner, ein Interface eine Header-Datei, ein struct ein Record Type. Eine SWC durfte an ihrer Provided-Seite mehrere Header anbieten, und Header hatten gerichtete Beziehungen zueinander — die nicht rekursiv sein durften (keine Zyklen). Unter der Haube aber orientierten wir uns von Anfang an am AUTOSAR-Standard (Standard- und vor allem Implementation Data Types), um eine spätere Migration in die AUTOSAR-Welt vorzubereiten. Rückblickend war AIM ein Werkzeug zur stufenweisen Transition dorthin.

C-Welt (vertraut)AUTOSAR-Konzept
OrdnerSoftware-Komponente (SWC)
Header-Datei(Provided) Interface
structRecord Type
primitiver Typ (uint16)StdType / Implementation Data Type
Arrayhomogene Liste (Array Type)
PointerData Reference (in ISO 26262 nur restriktiv)
Type Reference · Union Type

Der eigentliche Kern von AIM war die Verwaltung der Datentypen. Und hier lag die Knacknuss: Der AUTOSAR-Standard liest sich wie UML-Klassen mit Vererbung und zielt auf XML-Generierung. Eine relationale Datenbank kennt aber keine Vererbung, und eine Django-models-Klasse erbt anders, als es die klassische OOP lehrt. Die elegante Lösung war django-polymorphic:

from django.db import models
from polymorphic.models import PolymorphicModel

class AutosarDataType(PolymorphicModel):
    """Basistyp — liest sich wie eine UML-Basisklasse."""
    name = models.CharField(max_length=128, unique=True)

class PrimitiveType(AutosarDataType):        # StdType, z. B. uint16
    base_type = models.CharField(max_length=32)        # uint8/uint16/…
    bit_size  = models.PositiveSmallIntegerField()

class RecordType(AutosarDataType):           # C-struct → Record Type
    pass                                     # Felder über RecordMember (geordnet)

class RecordMember(models.Model):
    record      = models.ForeignKey(RecordType, related_name="members", on_delete=models.CASCADE)
    member_type = models.ForeignKey(AutosarDataType, on_delete=models.PROTECT, related_name="+")
    name        = models.CharField(max_length=128)
    position    = models.PositiveSmallIntegerField()

class ArrayType(AutosarDataType):            # homogene Liste
    element_type = models.ForeignKey(AutosarDataType, on_delete=models.PROTECT, related_name="+")
    length       = models.PositiveIntegerField()

class TypeReference(AutosarDataType):        # Referenz auf einen Typ
    target = models.ForeignKey(AutosarDataType, on_delete=models.PROTECT, related_name="+")

class DataReference(AutosarDataType):        # Pointer — ISO 26262: restriktiv
    points_to = models.ForeignKey(AutosarDataType, on_delete=models.PROTECT, related_name="+")

class UnionType(AutosarDataType):            # Union
    variants = models.ManyToManyField(AutosarDataType, related_name="+")

Weil member_type, element_type & Co. immer auf den Basistyp AutosarDataType zeigen, kann ein Record jeden Typ enthalten — auch einen weiteren Record oder ein Array. Verschachtelte und gemischte Konstrukte (etwa ein Array eines Record Types) ergeben sich damit von selbst. Und der Clou von django-polymorphic: AutosarDataType.objects.all() liefert jedes Objekt direkt als seinen konkreten Subtyp zurück — im Code liest sich alles wie normale Vererbung.

AutosarDataType Basistabelle (id, name) PrimitiveTypebase_type, bit_size RecordType→ RecordMember ArrayTypeelement_type, length Union / TypeReferencevariants / target DataReferencepoints_to (Pointer) RecordMemberrecord, member_type, pos Multi-Table Inheritance = „Stern": Basistabelle + je Subtyp eine Tabelle, verbunden per 1:1-Join
Im Code Vererbung, in der Datenbank ein Stern: django-polymorphic legt eine Basistabelle plus je Subtyp eine eigene Tabelle an. Vorteil: standardnah und elegant, polymorphe Queries liefern direkt den konkreten Typ. Nachteil: bei großen Datenmengen viele Joins. Bei ~40–50 SWCs sind wir nie in Laufzeitprobleme gelaufen; optimieren ließe sich das über DB-Views und die richtige Query am richtigen Ort — Listen nur auf die Basistabelle (Name + Typ-Art), erst der Detail-View lädt alles nach.

Dass wir uns so früh am Standard orientierten, zahlte sich aus: Später erzeugte ein Feature aus AIM heraus ARXML — das AUTOSAR-XML — und ebnete damit den Weg in die AUTOSAR-Welt. Und es gab sogar einen in Enterprise Architect gepflegten AUTOSAR-Modellierungsleitfaden, der vorgab, wie ein an AUTOSAR angelehntes Werkzeug zu designen ist — eine wertvolle Orientierung.

Der Import, der heimlich zum Compliance-Audit wurde

Ein Detail, auf das ich bis heute stolz bin: Bevor AIM in Betrieb gehen konnte, mussten die bestehenden Header aller relevanten Schnittstellen hinein. Also schrieben wir ein einmaliges Import-Werkzeug („one shot”). Und dieses Tool tat mehr als importieren — es prüfte jede Header-Datei gegen die Kodierrichtlinien des Kunden.

Was dabei herauskam, war unerwartet wertvoll: Statt still zu scheitern, erzeugte der Import bei jedem Verstoß (nicht eingehaltene Kodierstandards, ungültige Konstrukte, nicht-konforme Elemente) ein Reporting an Daimler — eine ehrliche Momentaufnahme, wie konform die Schnittstellen der einzelnen Entwickler tatsächlich waren. Aus einem technischen Importer wurde so ein Compliance-Instrument. Erst als alle Header konform waren, wurden sie übernommen und AIM ging live.

Bestehende Header Import-Tool prüft Kodier- richtlinien Verstoß → Report an Kunde Compliance-Stand je Entwickler Entwickler korrigieren → erneut Alles konform → Import AIM geht in Betrieb
Der Import war kein Selbstzweck: Er machte für den Kunden erstmals sichtbar, wie es um die Kodierrichtlinien-Konformität der Schnittstellen wirklich stand — und wurde zum Hebel, um sie flächendeckend herzustellen.

Technik-Box: Wie eine Datenbank-Migration wirklich funktioniert

Ein Punkt, den viele unterschätzen — und den ich hier zum ersten Mal richtig lernte. In Django beschreibst du deine Daten als Python-models. Änderst du ein Modell (Feld hinzufügen, Typ ändern, Feld aufteilen), passiert Folgendes:

  • makemigrations erzeugt eine nummerierte, geordnete Migrations-Datei mit den nötigen Schema-Operationen (AddField, AlterField, …).
  • migrate wendet die offenen Migrationen auf die Datenbank an.

Der entscheidende Unterschied, den man erst in Produktion schmerzhaft merkt:

  • Schema-Migration ändert die Struktur (Spalten, Typen). Django generiert sie automatisch. Auf einer leeren Entwickler-Datenbank ist damit alles getan.
  • Datenmigration transformiert die vorhandenen Zeilen. Die schreibt man selbst — als RunPython-Schritt in derselben Migrations-Datei (vorwärts und rückwärts, damit sie reversibel bleibt). Beispiel: einen alten Datentyp umrechnen, ein Feld in zwei aufteilen, eine neue Pflichtspalte sinnvoll befüllen.
models.pyDatenmodell makemigrationserzeugt Migration Migrations-Datei (nummeriert) Schema-Op (auto): AlterField… Daten-Op (selbst): RunPythonvorwärts + rückwärts migrate→ Datenbank Leere Dev-Datenbank Schema-Migration reicht — es gibt keine Bestandsdaten. Hier funktioniert scheinbar „alles". Produktion mit Bestandsdaten Struktur ändern kann Daten „verwaisen" → Datenmigration überführt sie sauber. Unser Weg: Prod-Backup auf DEV/QAS testen, im Release-Wartungsfenster anwenden.
Genau so haben wir es gemacht: Vor jedem Release zogen wir ein Backup der Produktionsdaten auf DEV/QAS, programmierten die Datenmigration in der Migrations-Datei, testeten sie gegen echte Daten — und migrierten im Wartungsfenster auch die Produktion ins neue Schema, wo nötig. Die nummerierten Migrationen garantieren dabei Reihenfolge und Reproduzierbarkeit.

Der schwierige Start: drei Monate, ohne die Skills

Und hier kommt der ehrliche Teil. Mein Gesamtprojektleiter Harald Braun war Python-Enthusiast und hatte kurz zuvor Django für sich entdeckt. Ich bekam meine ersten beiden Mitarbeiter — darunter Marc Hirth — und wurde als kleiner technischer Projektleiter eingesetzt. Für mich war das ein Riesending: das erste Mal Verantwortung für Umsetzung, Personal, Kundenkontakt und Regelmeetings.

Nur: Wir waren keine Webentwickler. Django, Datenbanken, JavaScript, LDAP-Auth, Webserver — alles Neuland. Und wir hatten drei Monate Zeit, um mit einem Mockup zu beweisen, dass die Idee trägt, gegen echten Zeitdruck und den brennenden Wunsch, erfolgreich zu sein. Diese Anfangsphase war mit Abstand die härteste. Ich erinnere mich an unzählige Stunden Pair Programming mit Marc — Seite an Seite ausprobiert, wie man eine VM bestellt, Apache installiert, Firewall-Regeln freischaltet. Zwischen uns entstand eine Symbiose: Ich konnte tagelang weg sein und beim Zurückkommen sofort verstehen, was entstanden war — und umgekehrt.

Der erste, zugegeben etwas hässliche Prototyp überzeugte die Verantwortlichen auf Kundenseite. Und danach lief es — ehrlich gesagt — richtig gut.

Scrum, wie wir ihn wirklich gelebt haben

Prozesstechnisch war AIM mein Einstieg in Scrum und das agile Manifest, nach Jahren im Wasserfall. Ich machte meine Scrum-Master-Zertifizierung, setzte mein erstes Product Backlog auf und etablierte Requirements Engineering im Projekt.

Aber „nach Lehrbuch” war das nicht — und genau das ist die ehrliche Lektion:

2-Wochen-Sprint Daily Standups täglich BacklogStories + Prioritäten (ich) Sprint PlanningZiel für 2 Wochen Sprint Reviewmit dem Kunden RetrospektiveAufwand vs. Nutzen … und von vorne
Alle Zeremonien gelebt — Planning, Daily, Review mit Kunde, Retro. Aber die Rollen haben wir „gepatcht": Ich brachte das gepflegte Backlog samt Prioritäten, den Rest handhabten wir als kleine Gruppe auf Augenhöhe. Prozess folgt Team, nicht umgekehrt.

Das eigentliche Aha war nicht die Methode, sondern der Rhythmus: alle zwei Wochen liefern statt nach Monaten. Kontinuierlich entwickeln und verbessern, bei jeder neuen Anforderung Aufwand gegen Nutzen abwägen — und sich an die Gegebenheiten anpassen, statt an einem alten Plan festzuhalten. Selbst Sprints, in denen wir Redesigns machten, akzeptierte der Kunde, weil am Ende jeder zwei Wochen ein greifbarer neuer Stand stand. Und ich glaube, dem Kunden gefiel besonders, dass sein eigenes Wissen mit dem Produkt zusammen wuchs — ein Vorteil, den kein fertiges Kommerzwerkzeug bietet.

Von Akzeptanz zu Pflicht — und in die Pipeline

In einer dreimonatigen Rollout-Phase machte ich die Handcode-Entwickler teils persönlich mit AIM vertraut. Kundenseitig wurde zunehmend eingefordert, Schnittstellen nur noch über AIM zu verwalten — der Umstieg wurde sogar getrackt. Wir krempelten gemeinsam den gesamten Handcode-Schnittstellenprozess um. AIM sollte laut Plan Teil der CI/CD-Pipelines werden — auch das erreichten wir, und mit den Pipeline-Ingenieuren setzten wir für AIM selbst einen Jenkins auf: automatisierte Unit-Tests, C0/C1-Testabdeckung von mindestens 80 %.

Nach der Embedded-Welt war dieses Tempo eine Offenbarung — externe Libraries, Staging-Umgebungen (lokal, Dev-VM, QM-VM, Produktiv), ein Wartungsfenster und zwei Stunden später geht’s weiter. Ich fing an, Open Source und die Ideen dahinter viel besser zu verstehen. Am Ende betreute AIM die Schnittstellen von rund 40–50 SWCs — ein Werkzeug, das wirklich benutzt wurde.

Wachstum verändert Dynamik

Mit dem Erfolg wuchs das Team — und mit dem Wachstum änderte sich die Dynamik. Was vorher eine eingespielte kleine Gruppe auf Augenhöhe war, wurde komplexer; etablierte Entscheidungen standen wieder zur Debatte. Mir fehlte damals schlicht das Handwerkszeug, so eine Dynamik konstruktiv zu moderieren — Konfliktmoderation lernt man nicht nebenbei. Aus einem Zustand, in dem ich fast täglich mit Energie zur Arbeit ging, wurde eine Belastung, und ich bat darum, das Projekt zu übergeben.

Nach außen liest sich der nächste Schritt schöner — „mehr Verantwortung nach dem AIM-Erfolg”, und das stimmt auch: Ich übernahm danach als Scrum Master und stellvertretender Projektleiter ein 8–13-köpfiges Team im ESP-Längsregler-Projekt. Aber ehrlich ist auch: Der Auslöser war eine Teamdynamik, die ich damals nicht steuern konnte. Meine Lektion — bis heute gültig: Ein wachsendes Team ist nicht automatisch ein besseres. Wachstum verändert Dynamiken, und Führung heißt, das aktiv zu gestalten. Das musste ich erst lernen.

Was ich mitgenommen habe

AIM war meine erste echte Führungsrolle — technisch, fachlich, personell. Drei Dinge sind geblieben:

  • Nähe schlägt Eleganz. Ein Service, der wirklich genutzt wird, entsteht näher an den Menschen, die ihn brauchen, als an der schönsten Architektur.
  • Rhythmus schlägt Plan. Alle zwei Wochen etwas Greifbares liefern verändert alles — für das Team und für das Vertrauen des Kunden.
  • Python & Django wurden eine Kompetenzlinie, die mich bis heute begleitet — Jahre bevor ich sie bei Bosch im WARP-Projekt wieder aufgriff.

Danke

Herzlichen Dank an Marc Hirth, meinen engsten Weggefährten bei AIM — für unzählige Stunden Pair Programming und die beste Symbiose, die ich in einem Team erlebt habe.

Herzlichen Dank auch an Harald Braun — für das Vertrauen und die Unterstützung während des AIM-Projekts und über meine gesamte Elektrobit-Zeit hinweg. Gerade mit AIM konnte ich dadurch wachsen wie nie zuvor.

Harald Braun auf LinkedIn · auf Xing

← Zurück zum Lab