Wann ist Software-Sanierung sinnvoll?
Ursprünglich haben Sie ein kleines Programm zur Vereinfachung der Abläufe geschrieben und dies über Jahre weiterentwickelt, so dass es inzwischen wesentlich für Ihre Geschäftsabläufe geworden ist. Aber nun stellen Sie immer wieder fest „geht nicht“ oder „zu aufwändig“, wenn weitere Änderungen notwendig sind?
Das ist ärgerlich, aber ein Stück weit ein natürlicher Prozess, denn auch Software kommt in die Jahre: Sei es, dass die eingesetzte Technologie nicht mehr unterstützt wird und eine Migration erforderlich macht – oder der Code ist so gewachsen, dass bereits kleine Änderungen große Fehler an unerwarteter Stelle produzieren. Vielleicht war die Anwendung auch nur als kleines Tool geplant. Aber es funktionierte so gut, dass inzwischen immer mehr Funktionalitäten oder Schnittstellen gewünscht werden – die Umsetzung übersteigt allerdings die internen Kapazitäten oder Kompetenzen?
Wie auch immer der Hintergrund ist: Die gute Nachricht ist, dass wir einiges für Sie und Ihre Legacy Systeme (Altsysteme) tun können! Nach einer Analyse der Ist-Situation und neuen Anforderungen können wir Ihnen die Möglichkeiten zur Sanierung Ihrer Software aufzeigen.
Nehmen Sie Kontakt mit uns auf und wir schauen, wie wir Ihnen helfen können!
Was wir für Ihre Software tun können und wie
Im Folgenden zeigen wir Ihnen die häufigsten/typischen Handlungsfelder bei der Software-Sanierung auf:
I. Trennen von Code der Bedienoberfläche und der Geschäftslogik
Programmcode für die Bedienoberfläche sollte getrennt sein von Programmcode, der Geschäftsvorfälle implementiert. Durch diese Trennung wird eine zweischichtige Struktur erzeugt, was folgende Vorteile hat:
- Testbarkeit: Der Code der Geschäftslogik kann separat getestet werden.
- Trennen von Aufgaben: Änderungen in der Oberfläche können getrennt von Änderungen in der Geschäftlogik gemacht werden. Die Fehleranfälligkeit bei Änderungen wird damit vermindert.
- Modularisierung: Die Geschäftlogik kann sowohl für mehrere verschiedene Benutzer-Oberflächen als auch für automatische Vorgänge benutzt werden.
- „Don’t Repeat yourself“: Geschäftvorfälle werden nur einmal implementiert und können von verschiedenen Oberflächen (z.B. aus verschiedenen Fenstern heraus) ausgeführt werden. Die singuläre Existenz von Code erleichtert Änderungen und Tests und vermindert so den Aufwand für die Qualitätssicherung.
II. Modularisierung
Die Architektur sollte so beschaffen sein, dass zusammengehöriger Programmcode und Daten in Modulen zusammengefasst werden. Jedes Modul befasst sich mit einem abgegrenzten Aspekt (Separation of Concerns). Die Module sollten klar definierte, schlanke Schnittstellen aufweisen und keinen direkten Durchgriff auf enthaltene Daten zulassen. Durch diese Kapselung wird es möglich, Module getrennt voneinander zu entwickeln, zu testen und auch nötigenfalls auszutauschen.
Die Vorteile der Modularisierung sind:
- Testbarkeit: Schnellere Testbarkeit durch klare, schlanke Schnittstellen.
- Transparenz: Die Zuständigkeit jedes Moduls ist klar abgegrenzt, dadurch bessere Wartbarkeit.
- Team: Teammitglieder können an verschiedenen Modulen arbeiten, ohne sich zu behindern, erzielen dadurch schnellere Ergebnisse.
III. Variablen und Konstanten
Globale Variablen sind eine häufige Fehlerquelle. Sie bergen die Gefahr, dass nicht genau definiert ist, wie und aufgrund welchem Anlass die Variable geändert wird und wo sie überall benutzt wird. Auch kann es in komplexer werdenden Programmen leicht zu einer Unklarheit über die Bedeutung einer globalen Variable kommen. Schließlich machen globale Variablen Programmcode schlecht les- und damit verstehbar.
Durch die Eliminierung globalen Variablen werden die verarbeiteten Daten so gespeichert, dass sie im Zugriff der Programmteile sind, die sie brauchen, nicht aber anderswo her.
Konstante Werte sollten im Programmcode an einer einzigen Stelle festgelegt und als Symbol definiert werden, anstatt sie zu wiederholen. Damit wird die Fehleranfälligkeit verringert.
Die Vorteile einer Eliminierung von globalen Variablen und die Einführung von Symbolen für Konstanten sind:
- Stabilität: Potentielle Fehlerquellen werden vermieden
- Lesbarkeit: Der Code wird besser les- und verstehbar
- Transparenz: Der Fluss der Daten wird transparent und nachvollziehbar
IV. Parameter und Rückgabewerte
Methoden und Klassen sollten per Parameter und Rückgabewerte miteinander Daten austauschen, anstatt globale Objekte oder globale Variablen zu benutzen. Auch sollten die übergebenen Werte nur das enthalten, was auch verwendet wird.
Die Vorteile der sauberen Datenkommunikation per Parameter und Rückgabewert sind:
- Transparenz: Der Datenfluss ist schnell klar, der Code dadurch leicht lesbar, was die Weiterentwicklung beschleunigt.
- Datensicherheit: Eine im Code versteckte Nutzung oder Beeinflussung von Daten ist nicht möglich, dadurch verminderte Fehleranfälligkeit.
V. Mehrfachen Code zusammenfassen
Programmcode sollte nicht kopiert werden. Wenn die gleiche Funktionalität an mehreren Stellen benötigt wird, so sollte diese Funktion in einem Modul ausgelagert und so von allen Stellen benutzbar gemacht werden („DRY“ Regel: Don't repeat yourself).
Durch die Zusammenfassung von mehrfachem Code ergeben sich folgende Vorteile:
- Änderbarkeit: Der betreffende Code kann geändert werden, ohne dass die Gefahr besteht, dass derselbe Code an anderen Stellen vergessen wird. Daraus ergibt sich eine Verringerung des Aufwands und der Fehleranfälligkeit.
- Weniger Code: Der Umfang des Code wird verringert, was die Komplexität verringert und damit auch die Zeit, die es braucht, den Code zu verstehen.
VI. Datenbankzugriffe kapseln und sicher machen
Datenbankzugriffe sollten auf eine einheitliche Art gekapselt werden. Anstatt die Zugriffe an vielen Stellen im Code zu verstreuen, sollte es dafür spezielle Datenzugriffsmodule geben. Die Datenbankzugriffe sollten in Transaktionen gekapselt werden, um die Datenkonsistenz sicherzustellen. Es muss sichergestellt werden, dass in Datenbankzugriffen verwendete Werte nicht zu Sicherheitsproblemen durch z.B. SQL Injection führen – etwa durch die Verwendung von parametrisierten Datenbank-Statements.
Durch diese Maßnahmen wird erreicht:
- Transparenz: Alle stattfindenden Datenbankzugriffe sind leicht auffindbar, das erleichtert die Lesbarkeit des Codes.
- Testbarkeit: Die Datenbankzugriffe sind einzeln testbar, das erhöht die Qualität.
- Transaktionssicherheit: Geschäftsvorfälle finden ganz statt oder gar nicht, aber nicht teilweise, dadurch werden Wartungs- und Stillstandskosten durch Daten-Chaos bei Störungen vermieden.
- Sicherheit: Angriffe durch SQL-Injection werden unterbunden, dadurch Schutz vor Ausfällen durch Fahrlässigkeit oder Sabotage und vor unbefugtem Datenabfluss.
VII. Fehlerbehandlung
Fehler sollten vom Programmcode unterschieden werden in
- zu erwartende Fehler, z.B. falsche Benutzer-Eingaben, die zu einer spezifischen Meldung führen und für die ein Ausweg vorgesehen ist (Korrektur der falschen Eingabe), und
- unerwartete Fehler, die vom Programm nicht speziell behandelt werden und zu einer allgemeinen Meldung und einem Abbruch der Operation führen sollen, z.B. eine Netzwerkstörung oder Speichermangel.
Für die Behandlung von Fehlern sollte das Sprachelement der Exception verwendet werden. Exceptions sollten nur da behandelt werden, wo sinnvoll reagiert werden kann – das ist in der Regel in der Bedienoberfläche.
Dadurch werden folgende Vorteile erreicht:
- Einheitliche Fehlerbehandlung: Alle Fehler, die auftreten können, werden auf definierte Weise behandelt. Es gibt keine unterschiedlichen Arten der Präsentation von Fehlern, was zu einem verbesserten Benutzer-Erlebnis führt.
- Nichtberücksichtigung von Fehlern im regulären Programmablauf: Durch Exceptions wird der Programmcode von Fehlerbehandlungs-Code befreit. Da der Programmcode sich so auf das Wesentliche, den normalen Ablauf, konzentriert, wird er übersichtlicher, was die Wartung erleichtert.
VIII. Logging
Das Programm sollte meist wichtige Ereignisse und, je nach Einstellung, auch andere Ereignisse, in einem einheitlichen Logfile festhalten.
Durch den Einbau von Logging wird Folgendes erreicht:
- Erleichterte Fehlersuche: Das Logfile kann für die Fehlersuche wertvolle Hinweise geben. Bei Fehlern im Feld können viele Fehler durch das Logfile identifiziert werden, was zeitraubende Analysen vor Ort spart.
- In Verbindung mit Exceptions und Stack Trace lassen sich unerwartete Fehler oft recht genau lokalisieren, was ebenfalls die Fehleranalyse erleichtert.
IX. Einführen von automatischen Tests für die Geschäftlogik (Unit Tests)
Die Geschäftslogik kann, wenn sie von der Bedienoberfläche getrennt ist (siehe Abschnitt I.), einzeln getestet werden. Idealerweise ist sie auch modularisiert (siehe Abschnitt II.). Diese Tests können automatisiert werden (Unit Tests).
- Sicherheit: Der Testlauf kann sehr oft erfolgen. So können Programmierer sich sehr schnell absichern, dass Änderungen nicht neue Defekte eingeführt haben. Dadurch sinkt der Aufwand für neue Funktionen.
- Weniger manuelle Tests: Manuelle Testläufe von Berechnungen und anderen Geschäftsvorfällen können stark vermindert werden. Das spart viel Zeit.
- Dokumentation durch Tests: Was automatische Tests testen und welche Ergebnisse die Tests haben, wird dokumentiert. So kann man nachlesen, welche Funktionen in Ordnung sind – und welche nicht.
X. Einführen von automatischen Tests für die Oberfläche (UI-Tests)
Die Bedienoberfläche und mit ihr das ganze Programm kann mit automatischen Tests ausgestattet werden. Dabei werden Benutzeraktionen simuliert. Auf diese Weise kann die gesamte Anwendung auf Herz und Nieren getestet werden.
Die Vorteile davon sind:
- Weniger manuelle Tests: Durch die verringerte Notwendigkeit von manuellen Tests Kostenersparnis.
- Qualität: Die automatischen Tests erhöhen die Qualität, da auftretende Fehler schnell erkannt werden.
- Änderungen: Bei Änderungen können Programmierer mutig sein und ihre Änderungen einbauen, ohne allzu viel über Nebeneffekte nachdenken zu müssen, da die UI-Tests Fehler zu Tage bringen würden. Dadurch schnellere Implementierung von Änderungen.
Weiterführende Links