Entwicklungs-Tools Wie sich der Entwicklungsprozess für Embedded-Software optimieren lässt

Einfachheit ist der Schlüssel zu guten, qualitativ hochwertigen und leicht wartbaren Produkten. Damit ist schon die halbe Arbeit bei Test und Debugging getan. Für den Rest gibt es leistungsfähige Tools. Dabei gilt: Je mehr sich automatisieren lässt, desto mehr wird der Entwickler von langweiligen und ermüdenden Tests oder Fehlersuchen entlastet.

Es gibt viele verschiedene Vorgehensmodelle für die Software- Entwicklung: Wasserfall, V-Model, Agile, Extreme Programming – die Liste lässt sich beliebig fortsetzen. Sie alle bieten ihre Vor- und Nachteile, und die Verfechter eines jeden Modells können viele Stunden über die jeweiligen Vorzüge diskutieren. Anstatt die große Methodologie-Debatte fortzuführen, beschreibt dieser Beitrag gemeinsame Maßnahmen, die während des Software-Lebenszyklus durchgeführt werden können – unabhängig von der verwendeten oder empfohlenen Methodologie. Außerdem werden Wege aufgezeigt, wie sich die Software-Entwicklung straffer gestalten und wie sich Zeit einsparen lässt. Die Vorteile schneller und effizienter Software-Lebenszyklen sind bekannt. Entscheidend ist, dass die schnelle Auslieferung hochqualitativer Produkte mehr Gewinne für ein Unternehmen bedeutet und die Arbeitsplätze für Entwickler sicherer macht.

Dieser Beitrag definiert „Time to Market“ als den Zeitraum, der zwischen der Spezifikation und der Auslieferung des Produkts liegt. Die Fertigung in hohen Stückzahlen bedeutet für Hersteller, dass sie die Phase des Geldausgebens (und Verlusts) für ein Produkt verlassen und endlich Gewinne erzielen. Dabei kann jedem Hersteller ein Lieferstopp drohen, sobald fehlerhafte Produkte auf den Markt kommen, die durch mangelnde Zeit- und Qualitätssicherung unbedingt zum geplanten Zeitpunkt erscheinen sollen. Dies kann erhebliche Probleme verursachen. Die Gewinnerzielung verzögert sich oder fällt sogar aus – oder noch schlimmer: Der Ruf eines Unternehmens wird beschädigt. Software- Hersteller wollen daher hohe Qualität nicht aus Zeitgründen opfern.

Zu den wesentlichen Faktoren, die Embedded-Software-Projekte verzögern, zählen:

  • schlechte oder unvollständige Anforderungsdefinitionen,
  • sich ändernde Anforderungen („Feature Creep“ – Überfrachten des Funktionsumfangs),
  • unvollständige Tests,
  • Software-Fehler (Bugs).

Aspekte hinsichtlich der Anforderungen sollten sicher nicht übersehen werden, führen aber unweigerlich zu Diskussionen über Methodologien, und dies würde hier zu weit führen. Der Punkt „unvollständige Tests“ ist hier aufgeführt, da Fehler (Bugs) dadurch unerkannt bleiben und später mittels Debugging aus dem System beseitigt werden müssen. Eine Umfrage unter den Teilnehmern der Embedded Systems Conference 2006 in San Jose kam zu dem Ergebnis, dass das Debugging die Zeit raubendste und teuerste Phase bei der Software-Entwicklung ist. 63 % der Befragten erklärten, das Debugging sei das größte Problem während ihrer Arbeit – doppelt so problematisch wie jede andere Aufgabe.

Die Aufgabe, Software-Fehler zu minimieren, ist ein großes Problem für Software-Entwickler und muss deshalb mit Nachdruck angegangen werden. Bei jedem Schritt des Entwicklungsprozesses müssen Entwickler alles tun, um Software-Fehler zu vermeiden, aber auch optimale Methoden finden, um Fehler zu beseitigen. Unternehmen müssen eruieren, was man dafür bereits in der Designphase tun kann, und nach Möglichkeiten suchen, die verschiedenen Phasen zu automatisieren, sowie fortschrittliche Tools für das Debugging einführen. Werden alle diese Techniken zusammen eingesetzt, ist der Erfolg bei der Reduzierung von Software-Fehlern groß.

Einfachheit hilft, den Überblick zu behalten

Ein effizientes Programmdesign zeichnet sich durch minimale Komplexität aus. Dies ist einfacher gesagt als getan. Wenn Entwickler mit ihrer Arbeit beginnen, ergibt sich meistens zuerst ein komplexes, verschachteltes und verwirrendes Gebilde. Ein überschaubares und straffes Design benötigt viele Durchläufe und gemeinsame Anstrengungen, um Funktionalität zu streichen, die nicht benötigt wird. Hier mehr Zeit zu verbringen, um das Design näher zu analysieren und nach Vereinfachungen zu suchen, wird sich später auszahlen. Ein minimales und straffes Design lässt sich auf lange Sicht auch einfacher warten.

Eine gute Aufteilung in Komponenten ist ein weiterer wichtiger Aspekt in dieser Phase. Wird das Design eines Subsystems zu komplex, sollte es in einfach verständliche Komponenten aufgebrochen werden. Als hilfreich erweist sich, wenn ein Entwickler jede Code-Zeile einer einzigen Komponente genau kennt oder dafür verantwortlich ist. Das zugrundeliegende Betriebssystem kann bei der Komponenteneinteilung unterstützen und die Regeln bei der Kommunikation der verschiedenen Komponenten überwachen. Ein Microkernel-Betriebssystem mit Speicherschutz und guten Möglichkeiten zur Abgrenzung der Programmteile ermöglicht den Entwicklern folgende Vorgehensweisen:

  • Nutzung von Prozessen, um Barrieren zwischen verschiedenen Komponenten zu schaffen und unbeabsichtigte Interaktion zu verhindern, die nur schwer verständlich und schwer zu debuggen ist.
  • Nachrichtengesteuertes Übertragen von Daten zwischen den Prozessen.
  • Nutzung gemeinsamen Speichers verhindern.
  • Anzahl der Threads in einem Prozess minimieren. Wann immer möglich sollte das Ziel ein Thread pro Prozess sein, um die Komplexität zu minimieren.
  • Das Prinzip des Least Privilege einführen. Benötigt eine Komponente keinen Zugriff auf eine Ressource (z.B. Dateisystem oder TCP/IP-Stack), sollte auch der Zugriff auf diese Ressource nicht erlaubt sein.

Ist ein System in Komponenten aufgeteilt, lassen sich später auftretende Software-Fehler einfacher isolieren und beherrschen. Die Fehler sind einfacher behebbar und beeinträchtigen keine anderen Komponenten im System. Mit einem gut definierten, nachrichtengesteuerten API für die Komponente lassen sich komplette Testfälle einfach entwickeln, die für Regressionstests erneut verwendbar sind.