In unserem Alltag sind wir regelmäßig mit Fragen zu Qualität von Software konfrontiert. Einerseits sind wir alle bei der Nutzung von Software in einer digitalen Welt darauf angewiesen, dass sie zuverlässig funktioniert. Anderseits sind wir bei der Entwicklung von digitalen Systemen verpflichtet, diesem Anspruch gerecht zu werden. Konkret müssen wir während der Entwicklung immer wieder prüfen, ob die von uns entwickelte Software die gesetzten qualitativen Ziele erreicht und mit welchen Mitteln wir diese Ziele erreichen können. Darüber hinaus werden wir gelegentlich gebeten, die Qualität von Anwendungen zu evaluieren.
In diesem Beitrag möchten wir am Beispiel des Android-Clients der luca App zeigen, wie wir bei einer solchen Anwendungsanalyse vorgehen, worauf wir im Besonderen achten und in welchen Bereichen es ggf. Verbesserungsmöglichkeiten gibt. Die luca App ist vermutlich die zurzeit am meisten diskutierte Anwendung in Deutschland und bietet daher einen idealen Ausgangspunkt, um das Vorgehen in der Entwicklung von qualitativer Software zu verdeutlichen. Wir möchten daher Wege aufzeigen, wie man Software entwickeln kann und sollte.
Um es gleich vorwegzunehmen: Dies ist keine Sicherheitsanalyse der luca App!
Sicherheit und Datenschutz sind wichtige Qualitätsmerkmale von Software und sollten in jedem Software-Lebenszyklus von Anfang an berücksichtigt werden. Andere haben hier schon wertvolle Beiträge zum Diskurs geleistet, die wir an dieser Stelle nicht wiederholen wollen. Wir konzentrieren uns in dieser Anwendungsanalyse auf Qualitätsmerkmale, die in Software-Design und Software-Architektur begründet sind und das, was allgemein als Software-Craftsmanship bezeichnet wird. Natürlich ist Sicherheit ein absolutes Querschnittsthema in der Software-Entwicklung und Sicherheitsprobleme können schlicht durch handwerkliche Fehler in der Entwicklung entstehen. Falls uns solche Probleme auffallen, weisen wir natürlich darauf hin.
Wir als BRICKMAKERS GmbH verstehen uns in erster Linie als Agentur für Digitalisierung. Durch eine ganzheitliche Beratung zum Einsatz digitaler Lösungsmöglichkeiten für ihr Problem, geben wir unserer Kundschaft einen entscheidenden Vorteil bei der Transformation hin zum digitalen Unternehmen. Natürlich besteht neben der Beratung und Konzeption digitaler Lösungen ein wesentlicher Teil unserer Arbeit aus der Entwicklung von Software.
Damit Software einen signifikanten Mehrwert für ein Unternehmen hat, muss sie jedoch qualitativ hochwertig sein. Aber was heißt das genau? Wann ist Software qualitativ hochwertig? Man könnte sich folgendes wünschen:
Software sollte…
… das Problem lösen, für welches sie entwickelt wurde. (Functional Suitability)
… für die Lösung des Problems nicht allzu lange benötigen. (Performance Efficiency)
… keine anderen Anwendungen kaputt machen. (Compatibility)
… einfach zu bedienen sein. (Usability)
… zuverlässig sein und keine neuen Probleme produzieren. (Reliability)
… sicher sein. (Security)
… mit vertretbarem Aufwand wartbar und erweiterbar sein. (Maintainability)
… gerne auch in anderen Kontexten verwendet werden können. (Portability)
Die genannten Punkte sind eine eher umgangssprachliche Formulierung des ISO 25010 Qualitätsmodells. Es soll hier nochmal verdeutlichen, dass Qualität allgemein und im speziellen auch bei Software immer ein Thema mit vielen Dimensionen ist. Jede dieser groben Kategorien ließe sich problemlos in weitere Unterkategorien zerlegen und für jede dieser Kategorien könnten eigene Analysen durchgeführt werden, die den Rahmen dieses Beitrags deutlich sprengen würden.
Wie bereits in der Einleitung angekündigt, wird sich diese Anwendungsanalyse nur mit Qualitätsmerkmalen befassen, welche im Software-Design, der Software-Architektur oder allgemein in der handwerklichen Umsetzung der Software begründet sind. Das sind insbesondere Teilaspekte von Functional Suitability, Reliability und Maintainability, welche besonders beim Betrieb von Software-Systemen von Bedeutung sind – natürlich auch für die Nutzung, schließlich besteht in beiden Fällen ein berechtigtes Interesse daran, dass eine Software tatsächlich einen funktionalen Mehrwehrt erbringt, also auch wirklich das intendierte Problem löst.
Der Quellcode des Android-Client der luca App wurde von der culture4life GmbH auf GitLab unter der GNU General Public License Version 3 veröffentlicht. Untersucht wird Version 1.6.6 der Anwendung.
Links:
Zur Analyse werden markt- bzw. branchenübliche und vor allem frei verfügbare Werkzeuge zur Software-Entwicklung und Qualitätssicherung verwendet. Interessierte können die Analyse mit folgenden Werkzeugen nachvollziehen:
Wenn wir uns während der Analyse auf Software-Metriken beziehen, können diese mit der Community-Edition von DisigniteJava nachvollzogen werden.
Metriken liefern ausschließlich Einsichten über strukturelle Eigenschaften von Code. Eine (gute) Metrik nimmt niemals eine Einteilung in gut oder schlecht vor. Die Ergebnisse von Metriken bedürfen immer einer kontextabhängigen Interpretation. Der Einsatz von Metriken ist vor allem dann sinnvoll, wenn sich (wie hier) zeitnah ein Überblick über eine fremde Software verschafft werden soll, also für Reverse-Engineering. Sie sind weniger sinnvoll als Zielmarken während der Entwicklung, weil sie oft den Blick für das eigentliche Problem verstellen. Hier ist eine konsequente Anwendung von Designprinzipien zielführender. Metriken können dennoch beim Reengineering oder Refactoring wertvolle Beiträge leisten. Man sollte sie sich nur nicht als absolute Ziele setzen.
Im ersten Schritt einer Anwendungsanalyse versucht man, sich einen groben Überblick über die zu analysierende Anwendung zu verschaffen und auf dem eigenen System zum Laufen zu bekommen. Hier stehen zunächst elementare Fragen im Vordergrund:
Mit welcher Programmiersprache wurde die Anwendung entwickelt?
Kompiliert der aktuelle Entwicklungsstand?
Im Fall des Android-Clients der luca App haben wir es mit einer „klassischen“ Android-Anwendung zu tun. Die verwendete Programmiersprache ist Java. Der untersuchte Entwicklungsstand kompiliert problemlos.
Die nächsten Fragen betreffen den groben Aufbau der Anwendung:
Ist eine Aufteilung von Application-, Business-, View- und Persistence-Logic erkennbar?
Werden strukturgebende Frameworks verwendet?
Die Frage nach der Aufteilung der verschiedenen Logiken zielt auf das Qualitätsmerkmal Maintainability. Eine adäquate Trennung dieser verschiedenen Anliegen erhöht die Modularität und Wiederverwertbarkeit einzelner Komponenten. So können z.B. zu einem späteren Zeitpunkt die verwendeten Technologien für die Gestaltung der Benutzeroberfläche oder zur Datenspeicherung einfacher ausgetauscht werden, ohne die Geschäftslogik ebenfalls anpassen zu müssen. Außerdem ermöglicht eine Trennung ein einfacheres Testen der Komponenten in Isolation oder Zusammenspiel, was dabei hilft, die funktionale Korrektheit der Software langfristig sicherzustellen.
Für die luca App zeigt ein Blick auf den Projektbaum in Android Studio eine grobe Trennung zwischen Application-, View- und Business-Logic. View-Logic wurde im Package de.culture4life.luca.ui
gruppiert. Business- und Application-Logic wurden jeweils in eigene Feature-Packages gruppiert. Auf den ersten Blick macht die Strukturierung einen ordentlichen Eindruck.
Die Frage nach strukturgebenden Frameworks dient der weiteren Aufklärung, wie die Anwendung aufgebaut ist. Hier kann schon klar werden, mit welchen Design-Patterns oder Programmierparadigmen bei der weiteren Analyse zu rechnen ist.
Die luca App verwendet hier außer ReactiveX keine externen Frameworks, die stark auf die Struktur die Anwendung Einfluss nehmen. Die Anwendung verwendet für die View-Logic das Standardmuster aus Aktivities, ViewModels und Fragments. Darüber hinaus kann innerhalb der Klassen mit reaktiver Programmierung gerechnet werden.
Eine weitere Möglichkeit, sich einer unbekannten Software zu nähern, sind Metriken:
Wie groß und komplex ist die Anwendung?
Um diese Frage zu beantworten, kommen folgende Metriken in Betracht: Number of Packages (NOP), Number of Types (NOT), Number of Methods (NOM), Lines of Code (LOC), McCabe Cyclomatic Complexity (CC).
Für die luca App ergeben sich folgende Werte als Summen über das gesamte Projekt:
NOP = 26
NOT = 110
NOM = 1368
LOC = 8053
CC = 1667
Setzt man diese Werte in Verhältnis, ergeben sich folgende Einsichten:
NOT / NOP = 4,23. Ein Package enthält im Schnitt 4 Typen, d.h. Classes oder Interfaces.
NOM / NOT = 12,44. Ein Typ enthält im Schnitt 12 Methoden.
LOC / NOM = 5,89. Eine Methode besteht im Schnitt aus ungefähr 6 Codezielen.
CC / LOC = 0.21. Ungefähr jede fünfte Zeile ist mit einer Komplexitätssteigerung zu rechnen.
Nachdem wir uns im ersten Schritt einen groben Überblick über die zu analysierende Anwendung verschafft haben, können wir in den folgenden Schritten nach konkreteren Qualitätseigenschaften suchen.
Im zweiten Schritt der Analyse fragen wir nach der Testabdeckung. Diese zahlt unmittelbar auf die Qualitätskategorie Functional Suitability ein. Macht die Anwendung das, wozu sie entwickelt wurde, und können wir das über den gesamten Entwicklungszeitraum sicherstellen?
Wichtige Hilfsmittel, um dies zu erreichen, sind automatisierte Tests. Dies können Unit-Tests, Integrationstests oder Systemtests sein. Die Testabdeckung zeigt wie viele Komponenten und mögliche Ausführungspfade der Anwendung durch einen automatisierten Test überprüft werden. Ideal ist eine Testabdeckung nahe 100%. Dies ist leider aus verschiedenen technischen Gründen nicht immer möglich, daher ist eher eine Testabdeckung über 80% erstrebenswert und i.d.R. erreichbar.
Entwicklungsmethoden wie Test-Driven-Development oder Behavior-Driven-Development können hier helfen, bereits während der Programmierung eine hohe Testabdeckung zu erreichen. Kombiniert mit Continuous-Integration können durch automatisierte Tests Regressionen erkannt und vermieden werden. Regressionen können z.B. entstehen, wenn während der Entwicklung einer neuen Komponente eine Bestandskomponente in unbeabsichtigter Weise modifiziert wird.
Die luca App verwendet Unit-Tests und spezielle Android-Tests, welche zusammen mit dem Emulator das Verhalten auf einem Smartphone simulieren.
Allerdings existieren nur sechs echte Testklassen. LucaUnitTest
und LucaInstrumetnationTest
enthalten selbst keine Testmethoden. Insgesamt bestehen die Tests aus 347 LOC. Im Verhältnis zur Gesamtgröße der Software von 8053 LOC ist das eher gering, was sich auch durch den Test-Coverage-Report von Android Studio bestätigt:
Insgesamt erzielt die luca App eine geringe Testabdeckung:
32,0% (33/103) der Klassen wurden getestet.
10,9% (203/1867) der Methoden wurden getestet.
12,3% (621/5059) der Codezeilen wurden getestet.
Auffällig ist, dass alles unterhalb des Package de.culture4life.luca.ui
keine Testabdeckung hat. Die gesamte View-Logic wurde also nicht automatisiert getestet. Es ist unklar, wie sichergestellt wird, dass die Benutzeroberfläche über den gesamten Entwicklungszeitraum so funktioniert wie erwartet.
Einschränkend muss man anmerken, dass Android-Tests aus technischen Gründen keine Testabdeckungsdaten erzeugen. Es ist jedoch realitätsfern zu erwarten, dass AsymmetricCipherProviderTest
mit 5 Testmethoden, CryptoManagerTest
mit 5 Testmethoden und AccessibilityServiceUtilTest
mit einer Testmethode, welche für automatisierte Testläufe ausgeschaltet wurde, signifikant zu einer höheren Testabdeckung beitragen.
Ein weiterer Schritt der Analyse kann nach einer Trennung von Bereitstellungsumgebungen fragen. Eine saubere Trennung von Development-, Staging- und Production-Umgebung hat auch Implikationen auf die Sicherheit des Gesamtsystems, zuvorderst dient es jedoch der Stabilisierung des Production-Systems. Es soll vermieden werden, dass Entwicklungsfehler den Live-Betrieb stören oder zum Abbruch bringen. Wir bewegen uns hier strenggenommen zwischen den Qualitätskategorien Functional Suitability, Reliability und Security.
Die luca App verwendet augenscheinlich eine Trennung zwischen Development- und Production-Umgebung:
Allerdings wirft die teilweise Umsetzung durch Hardcoding die Frage auf, wie in der Production-Umgebung sichergestellt wird, dass die Anwendung im Live-Betrieb nicht mit der Staging-Umgebung kommuniziert.
Wird die luca App auf dem Endgerät mit einem Debugger verbunden, verbindet sich die Anwendung mit einem anderen Server.
Außerdem wirft das Vorhalten eines Test-Accounts im Code die Frage auf, inwieweit der Android-Client tatsächlich mit einer funktionsfähigen Development-Umgebung entwickelt wird, die bereits registrierte Test-Accounts vorhalten könnte.
Idealerweise wäre hier das Vorhalten drei funktionsfähiger Gesamtsysteme wünschenswert:
Eine Development-Umgebung, in der neue Features entwickelt und getestet werden können.
Eine Staging-Umgebung, in welcher die Integration mit dem Production-System vorbereitet werden kann.
Eine Production-Umgebung für den Live-Betrieb.
Ein weiterer interessanter Schritt in der Analyse von Bestandscode ist die Untersuchung auf Code und Design Smells. Smells sind keine funktionalen Fehler oder Bugs. Eine Anwendung würde mit Smells genauso korrekt funktionieren wie ohne. Allerdings beeinträchtigen sie langfristig die Weiterentwicklung der Anwendung und beeinflussen daher das Qualitätsmerkmal Maintainability negativ.
Um Smells zu erklären kann man hier mehrere metaphorische Vergleiche zu verschiedenen Gewerken ziehen. Beispielsweise können wir Kabel irgendwie verlegen: in konzentrischen Kreisen quer über die Wand, vielleicht sogar in ästhetisch ansprechenden Mustern. Funktional würde der Strom immer noch ohne Probleme von A nach B fließen. Allerdings erschwert dies Wartung oder Erweiterung der Leitungen, etwa wenn wir einen Schaden reparieren müssen oder ein zusätzliches Kabel verlegen wollen. Nach einem längeren Zeitraum wissen wir wahrscheinlich nicht mehr, wo die Kabel genau entlanglaufen. Wir müssen zunächst mühsam herausfinden wie die Leitungen verlegt wurden, bevor wir mit unserem eigentlichen Vorhaben beginnen können. Um das zu verhindern, folgen wir Konventionen. Wir verlegen die Kabel in einfachen rechtwinkligen Bahnen entlang der Wandaußenseiten.
Ähnlich verhält es sich mit Code. Unsauberes Programmieren und willkürliches Design erschweren langfristig die Zusammenarbeit im Entwicklungsteam und die Erweiterbarkeit von Anwendungen.
Eine Möglichkeit Smells schon während der Entwicklung zu identifizieren und zu beseitigen ist das Verwenden von statischer Analyse. Android Studio liefert mit der Funktion Inspect Code bereits ein wichtiges Werkzeug zur Beseitigung von Smells, welches auf problematische Stellen im Code hinweist und Verbesserungsvorschläge gibt.
Die luca App weist hier insgesamt 755 Warnungen auf. Davon beziehen sich ungefähr je ein Drittel auf Android spezifische Probleme, auf allgemeine Probleme in der Verwendung von Java und auf Rechtschreibfehler.
Die 5 Errors unter der Android-Kategorie lassen sich auf Kompatibilitätsprobleme der verwendeten Shrinker-Konfiguration mit der Analyseumgebung zurückführen und sind nicht notwendigerweise Probleme der luca App selbst.
Auch Rechtschreibfehler sind nicht notwendigerweise ein Problem der Anwendung selbst, da die Analyse abhängig von der Wörterbucheinstellung der eigentlichen Entwicklungsumgebung ist und hier vermutlich auch mit den Einstellungen der Analyseumgebung zusammenhängen.
Die von Android Studio angezeigten Warnungen sind nicht zwangsläufig Probleme, sollten aber während der Entwicklung regelmäßig überprüft und ggf. unterdrückt werden, damit sie bei zukünftigen Analysen nicht erneut anschlagen. Dies ist hier nicht geschehen.
Aus der Android-spezifischen Kategorie beziehen sich 179 Warnungen auf Probleme, welche die Performance der Anwendung negativ beeinflussen können. Eine Warnung bezieht sich auf ein mögliches Sicherheitsproblem.
Aus der Java-spezifischen Kategorie beziehen sich 170 Warnungen auf toten Code, also Typen, Methoden, Felder, Variablen, etc., welche nicht verwendet werden und nichts mehr zur Funktionalität der Anwendung beitragen außer Platz wegzunehmen. 42 Warnungen beziehen sich auf Stellen, die mit sehr hoher Wahrscheinlichkeit Bugs zur Laufzeit verursachen können. 32 Warnungen beziehen sich auf die Verwendung von Konstrukten, dessen Entwicklerinnen selbst inzwischen davon abraten und deshalb als @Deprecated
markiert haben.
Bei eingehender Analyse können sich vermutlich einige Warnungen aus der Kategorie der wahrscheinlichen Bugs als False Positives herausstellen, sollten jedoch dringend einzeln geprüft werden.
Interessant ist, welche verwendeten Konstrukte hier Deprecation-Warnungen erzeugen. Es sind ausschließlich Konstrukte, welche von der luca App selbst definiert werden:
Das Vorhandensein dieser Warnungen alleine ist zunächst nicht negativ zu bewerten, falls die Entfernung dieser Konstrukte für die nächste Version der Anwendung vorgesehen ist. Sie ist sogar postiv zu bewerten, da so in der aktuellen Iteration von der weiteren Verwendung aktiv abgeraten wird.
Eine weitere Möglichkeit Smells schon während der Entwicklung zu identifizieren und zu beseitigen ist die Verwendung von PMD. Die Ergebnisse haben zum Teil Überschneidungen mit den Ergebnissen von Inspect Code. Zum Teil sind sie aber auch Geschmackssache. Wir beschränken uns daher auf die kritischsten Resultate.
Die Analyse der luca App mit PMD zeigt insgesamt 4726 Regelverstöße, davon entfallen ungefähr zwei Drittel auf die Kategorien Code Style und Documentation, ungefähr ein Drittel auf Design:
Die Kategorien Code Style und Documentation können hier vernachlässigt werden, da sich das Entwicklungsteam auch auf einen gleichwertigen, aber von PMD verschiedenen Code- und Dokumentationsstil geeinigt haben kann.
Interessanter ist hier die Untersuchung, welche Regelverstöße der Design-Kategorie auf tatsächliche Design Smells in der luca App hinweisen könnten:
Dazu betrachten wir die Unterkategorien zu den Anti-Patterns Law Of Demeter, Data Class und God Class.
Die Verstöße gegen das Law of Demeter lassen sich für die luca App auf die starke Verwendung von ReactiveX zurückführen. In klassisch objektorientierten Software-Designs würde der Verstoß auf ein Problem im Design der betroffenen Klasse hinweisen. Bei der Verwendung von ReactiveX sind Verstöße unvermeidbar, da ReactiveX im Programmierstil eine Verkettung von Methodenaufrufen einfordert, welche das Law of Demeter verbietet. Ob es sich hier um ein Problem handelt, hängt im Wesentlichen davon ab, wie man selbst zu ReactiveX und reaktiver Programmierung steht. Allerdings kann ReactiveX einen Programmierstil fördern, welcher Lesbarkeit und Verständlichkeit abträglich sein kann:
Das ist nicht notwendigerweise ein Ausschlusskriterium für ReactiveX. Vielmehr müssen während der Programmierung Maßnahmen ergriffen werden, um die Verständlichkeit des Quelltexts aktiv zu fördern, z.B. Extraktion der Lambda-Funktionen in Methoden mit beschreibenden Namen.
Die Verwendung von Data Classes oder Anemic Domain Models ist zwar aus der Perspektive der Objektorientierung, wie sie ursprünglich erdacht wurde, ein Anti-Pattern, wird aber heute als mögliche Kapselung von Daten akzeptiert. Ob es sich hier um ein Anti-Pattern handelt, hängt mittlerweile im Wesentlichen vom individuellen Designgeschmack des Entwicklungsteams ab. Andere Anti-Patterns erzeugen mit höherer Sicherheit gravierendere Probleme.
Die Verstöße gegen das Anti-Pattern God Class in der luca App sind real. Die aufgeführten Klassen sind alle sehr groß, sehr komplex und erledigen viele Aufgaben:
Dies ist im Wesentlichen im sehr kleinen Objektgraphen bzw. generellem Design der luca App begründet. Die Hauptklasse LucaApplication
instanziiert diverse „Manager“-Klassen, welche die gesamte Business-Logic enthalten und Aufgaben nicht an weitere, kleinere Klassen delegieren.
Ein Großteil dieser „Manager“-Klassen verstößt zusätzlich noch gegen die Regel über zu viele Methoden in einer Klasse, was den Verdacht, dass es sich um einen echten Verstoß handelt, erhärtet:
Die letzte Möglichkeit, Smells zu identifizieren, ist der der Einsatz von Metriken. Wir können hier z.B. nach sehr großen, komplexen oder inkohärenten Klassen oder sehr großen und komplexen Methoden suchen.
Interessant für die Untersuchung von Klassen sind folgende Metriken:
Lines of Code (LOC)
Number of Methods (NOM)
Weightet Methods for Class (WMC)
Lack of Cohesion of Methods (LCOM)
Sehr große Klassen nach LOC sind:
Sehr große Klassen nach NOM sind:
Sehr komplexe Klassen nach WMC sind:
Sehr inkohärente Klassen nach LCOM sind:
Es fällt auf, dass z.B. der CryptoManager
einerseits sehr groß (LOC = 384, NOM = 80), sehr komplex (WMC = 85) und gleichzeitig sehr inkohärent (LCOM = 7) ist. Dies bestätigt nochmal, dass der CryptoManager
tatsächlich eine God Class ist. Inkohärenz und God Class sind Indizien für eine ungute Akkumulation von verschiedenen Aufgaben in einer Klasse.
Ein Beispiel für eine solche zusätzliche Aufgabe ist die Migration von kryptographischen Einstellungen der Anwendung. Hier sollte ein Refactoring zur Auslagerung der Migrationslogik erfolgen, um für das Software-Design der Anwendung insgesamt eine feinere Granularität zu erreichen.
Ein anderes Beispiel für eine zusätzliche Aufgabe des CryptoManager
ist die Anhäufung von Utility-Methoden, welche ebenfalls in eine eigene Klasse ausgelagert werden sollten.
Interessant für die Untersuchung von Klassen sind folgende Metriken:
Lines of Code (LOC)
McCabe Cyclmatic Complexity (CC)
Besonders große Methoden nach LOC sind:
Besonders komplexe Methoden nach CC sind:
Es fällt auf, dass HistoryViewModel.createHistoryViewItem
die größte und komplexeste Methode ist. Die Größe und Komplexität wirken sich negativ auf die Verständlichkeit der Methode aus. In Kombination mit der fehlenden Testabdeckung für den kompletten View-Layer ergeben sich hier Risiken für Fehler. Niemand weiß, ob die Methode auch das tut, was sie tun soll, bzw. müsste man es mühsam von Hand ausprobieren. Aus der Cyclomatic Complexity von 9 ergeben sich 9 Testfälle, die jedes Mal geprüft werden müssten – unter der Annahme, dass die aktuelle Implementierung schon alle Rand- und Fehlerfälle berücksichtigt.
Hier sollte ein Refactoring entlang der switch-Fälle durchgeführt werden, welche einzelne Methoden extrahiert, um die Verständlichkeit der Methode zu erhöhen. Außerdem sollte die Logik zum Erzeugen von HistoryViewItem
-Objekten in eine eigene Klasse ausgelagert werden, um diese besser testen zu können.
Als letzten Analyseschritt der Anwendung betrachten wir noch den Einsatz gängiger Entwurfsmuster und -prinzipien, z.B. der Entwurfsmuster der „Gang of Four“, Clean Code bzw. den SOLID-Prinzipien. Entwurfsmuster sind bekannte Lösungen für häufige Probleme oder Aufgaben in der Programmierung und im Software-Design, bzw. im Fall von Entwurfsprinzipien eher eine Art goldener Regeln, die sich mit der Zeit bewährt haben. Der Übergang ist fließend. Ihr Einsatz erhöht auf Dauer die Wartbarkeit und Testbarkeit und zahlt damit direkt auf die Qualitätsmerkmale Functional Suitability und Maintainabiliy ein.
Ein Prinzip, welches im Code der luca App zumindest teilweise umgesetzt wurde, ist Inversion of Control:
Allerdings wurde hier Potential durch das großflächige Fehlen von Unit-Tests nicht voll ausgenutzt, obwohl es bekannt gewesen zu sein scheint, wie der DataAccessManagerTest
durch den Einsatz von Spies zeigt:
Der Einsatz weiterer Entwurfsprinzipien konnte in der luca App nicht festgestellt werden. Vielmehr, wie der Abschnitt „Code & Design Smells“ zeigt, wurde z.B gegen das Single Responsibility Principle durch das Arbeiten mit God Classes verstoßen.
Ein Einsatz gängiger Entwurfsmuster, der über die Anbindung an die View-Klassen von Android hinausgeht, z.B. zur Strukturierung der eigenen Business-Logic, konnte in der luca App nicht festgestellt werden. Dabei gäbe es hier Potential, beispielsweise könnte die sehr komplexe Creation-Logic der HistoryViewItem
-Objekte aus dem HistoryViewModel
in eine Factory-Klasse extrahiert werden, um das Design weiter zu entkoppeln:
Alternativ zum Factory-Pattern könnte auch das Builder-Pattern verwendet werden. Zusätzlich könnte der Einsatz des Strategy-Pattern das Software-Design noch weiter verfeinern. In beiden Fällen muss jedoch der zukünftige Entwicklungsplan für die betroffenen Stellen eingehend geprüft sowie Vor- und Nachteile gegeneinander abgewogen werden, was wir an dieser Stelle nicht leisten können.
Auffällig sind TODO-Kommentare im Code, welche darauf hindeuten, dass dem Entwicklungsteam funktionale Probleme der Anwendung aufgefallen sind, die jedoch noch nicht bearbeitet wurden. Es bleibt unklar, ob diese Probleme auch in ein Ticketsystem aufgenommen wurden, um sie zur späteren Untersuchung und Bearbeitung einzuplanen.
Wir haben gezeigt, wie mit Interesse an den grundlegenden Qualitätsmerkmalen von Software und gängigen Methoden zur Qualitätssicherung in der Software-Entwicklung, ausschließlich unter Einsatz von frei verfügbaren Entwicklungswerkzeugen, die Qualität einer Code-Basis evaluiert werden kann.
Der Android-Client der luca App zeichnet hier ein durchwachsenes Bild, bei dem leider die negativen Flächen überwiegen. Insbesondere das großflächige Fehlen von automatisierten Tests, die äußerst unklare Trennung der Bereitstellungsumgebungen, die vielfältigen Code und Design Smells sowie das Fehlen von gängigen Entwurfsmustern und das Nicht-Einhalten objektorientierter Designprinzipien lassen auf Probleme in der Qualitätssicherung schließen.
Das ist insofern erstaunlich, da Ansätze von Qualitätssicherungsmethoden – übrigens auch in den hier nicht betrachteten anderen Komponenten des luca-Systems – vorhanden sind, aber aus irgendeinem Grund nicht stringent weiterverfolgt wurden.
Unsere Handlungsempfehlungen für die Weiterentwicklung des Android-Clients der luca App sind:
Die Großflächige Einführung bzw. Nachziehen von automatisierten Tests.
Der Aufbau einer klaren und branchenüblichen Trennung zwischen Development-, Staging- und Production-Umgebung.
Der Einsatz von Entwicklungswerkzeugen zur Qualitätsanalyse des Codes, z.B. Android Studio oder PMD.
Ein Umfangreiches Refactoring der Codebasis unter Einbeziehung gängiger Entwurfsmuster, insbesondere die Auftrennung der „Manager“-Klassen unter Beachtung objektorientierter Designprinzipien und Best Practices.