Individuelle Softwarelösungen. Digitale Transformation. Agile Softwareentwicklung. Individuelle Softwarelösungen. Digitale Transformation. Agile Softwareentwicklung. 

Individuelle Softwarelösungen. Digitale Transformation. Agile Softwareentwicklung. Individuelle Softwarelösungen. Digitale Transformation. Agile Softwareentwicklung. 

19 min

Codeanalyse mit jQAssistant (Teil 2)

Ein umfangreicher Einblick in die eigene Codebasis ist unerlässlich, um die Qualität des Codes zu bewerten und zu verbessern. jQAssistant kann dabei helfen, diese Einblicke zu gewinnen und die Codequalität mit selbstdefinierten Regeln zu überwachen, die auf die spezifischen Anforderungen des eigenen Projekts abgestimmt sind.

Analyse und Überwachung der Codequalität mit jQAssistant

In diesem zweiten Teil unserer Serie fokussieren wir uns auf die Erstellung verschiedener jQAssistant Regeln zur Analyse der eigenen Codebasis. Wir werden tiefere Einblicke in unseren Code gewinnen, unsere Projektdokumentation um Code-Metriken erweitern und Build-Breaker erstellen, die sicherstellen, dass selbstdefinierte Qualitätsstandards eingehalten werden.

Im ersten Teil der Blogreihe habe ich angekündigt, dass wir uns in diesem zweiten Teil mit der Analyse und Überwachung der Codestruktur sowie der Einführung von Metriken beschäftigen werden. Wie sich herausgestellt hat, ist das Thema jedoch so umfangreich, dass wir uns in diesem Teil zunächst auf die Erstellung von jQAssistant-Regeln zur allgemeinen Analyse des Codes und zur Einführung von Metriken konzentrieren. Der nächste Teil wird dann ausführlich auf die Code-Struktur und Softwarearchitektur eingehen.

Dieser Blog-Artikel basiert auf dem ersten Teil der Reihe und setzt Grundkenntnisse in der Neo4j Abfragesprache Cypher  voraus. Wer bislang nicht mit den Grundlagen von Cypher  vertraut ist, dem empfehle ich einen Blick in die Cypher-Referenzkarte .

Den Code zu diesem Blog-Artikel findet ihr wieder auf GitHub .

Labels und Beziehungen

jQAssistant scannt verschiedenste Teile des Projekts und speichert die Informationen als Knoten und Beziehungen in der Neo4j-Datenbank. Jedem Knoten wird dabei mindestens ein Label zugewiesen, das die Knoten für die weitere Auswertung klassifiziert.

Eine Dokumentation der Labels, Eigenschaften und Beziehungen, die das Java-Plugin von jQAssistant verwendet, findet ihr in der jQAssistant-Dokumentation .

Wir werden in den folgenden Regeln häufig mit dem Label Type arbeiten. Damit ihr die Regeln besser nachvollziehen könnt, möchte ich hier kurz auf ein paar wichtige Aspekte eingehen.

Das Label Type wird an alle Knoten vergeben, die eine Klasse, ein Interface, ein Enum oder eine Annotation repräsentieren. Zur weiteren Klassifizierung werden dann noch die zusätzlich Labels Class, Interface, Enum, Annotation verwendet. Diese zusätzlichen Labels vergibt jQAssistant nur an Typen, die Teil des gescannten Codes, also der eigenen Anwendung sind. Externe Typen, wie zum Beispiel aus der Java Standardbibliothek, erhalten nur das Label Type.

Das bedeutet, dass wir mit MATCH (class:Type:Class) alle Klassen unseres eigenen Projekts abfragen können, während MATCH (type:Type) alle Typen, also auch referenzierte Typen aus externen Bibliotheken, zurückgibt.

Auch Methoden werden als eigene Knoten mit dem Label Method in der Datenbank abgelegt. Hierbei beschränkt sich jQAssistant allerdings wieder auf den gescannten Code der eignen Codebasis. Somit können Abfragen wie (type:Type)-[:DECLARES]->(method:Method) nur Typen und Methoden aus dem eigenen Projekt zurückgeben. Hier ist dann keine weitere Einschränkung auf Klassen, Interfaces, Enums oder Annotationen notwendig.

Es empfiehlt sich generell immer, die Ergebnisse der Cypher-Anfragen kritisch zu hinterfragen und mit erwarteten Ergebnissen zu vergleichen.

Regeln strukturieren

Im ersten Teil haben wir nur eine einzige Regel erstellt, um die Funktionsweise von jQAssistant zu demonstrieren. Wenn wir im Laufe der Zeit mehr Regeln für unser Projekt erstellen, empfiehlt sich eine Strukturierung der Regeln in verschiedene Gruppen.

Fürs Erste genügt die Strukturierung in die folgenden beiden Gruppen. Im weiteren Verlauf der Blogreihe werden wir jedoch noch weitere Gruppen hinzufügen.

  • common: Für alle Regeln, die die Daten in der Neo4j-Datenbank um zusätzliche Informationen anreichern, welche von weiteren Regeln aus anderen Gruppen genutzt werden können.
  • metrics: Für alle Regeln, die generelle Metriken über den Code erheben.

Die Regeln einer Gruppe werden dann in einer eigenen Datei gespeichert. Die Dateinamen spiegeln dabei den Gruppennamen wider. Somit ergibt sich folgende Struktur:

/jqassistant/index.xml
/jqassistant/common.xml
/jqassistant/metrics.xml

Die Datei index.xml dient als Einstiegspunkt für jQAssistant. Hier wird die default-Gruppe definiert, die die concepts und constraints aus den anderen Gruppen importiert.

index.xml
<jqassistant-rules
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://schema.jqassistant.org/rule/v1.10"
        xsi:schemaLocation="http://schema.jqassistant.org/rule/v1.10 https://schema.jqassistant.org/rule/jqassistant-rule-v1.10.xsd">

    <group id="default">
        <includeConcept refId="common:*"/>
        <includeConcept refId="metrics:*"/>
        <includeConstraint refId="metrics:*"/>
    </group>

</jqassistant-rules>

Wenn in der Konfigurationsdatei .jqassistant.yml nicht anders definiert, führt jQAssistant alle Regeln aus der Gruppe default automatisch aus.

Metriken für die eigene Codebasis

Grundlegende Metriken wie Zeilenlänge (Lines of Code), Zyklomatische Komplexität (Cyclomatic Complexity) und Anzahl der Methoden-Parameter hat vermutlich jeder schon einmal gehört. Viele Entwicklungsumgebungen oder statische Code-Analyse-Tools wie SonarQube  bieten bereits eine Vielzahl solcher Metriken an und weisen automatisch auf Verstöße gegen gängige Best Practices hin.

Diese Tools sind äußerst hilfreich, können jedoch nur bedingt auf die spezifischen Anforderungen des eigenen Projekts angepasst werden. Mithilfe von jQAssistant können wir eigene Metriken definieren, die genau auf unser Projekt und dessen spezielle Anforderungen zugeschnitten sind.

Die folgenden Regeln sind daher eher als Anregung zu verstehen und sollten vor der Anwendung auf das eigene Projekt auf ihre Aussagekraft und Relevanz überprüft werden.

Wenn nicht anders angegeben, werden alle folgenden Regeln in der zuvor erstellten Datei metrics.xml abgelegt.

Zeilenanzahl und Zyklomatische Komplexität

Die Metriken Zeilenanzahl (Abkürzung LoC von Lines Of Code) und Zyklomatische Komplexität  (Abkürzung CC von Cyclomatic Complexity) sind zwei der bekanntesten Metriken zur Bewertung der Code-Qualität. Code-Teile mit hoher CC und LoC neigen dazu, besonders unverständlich zu sein. Aber auch Code-Teile mit hoher CC und niedriger LoC sind problematisch, da sie oft sehr kompakt und schwer zu ändern sind.

Die folgende Regel gibt eine Liste der Typen mit der höchsten Zyklomatische Komplexität und Zeilenanzahl zurück.

Regel für Java Typen mit den höchsten LoC und CC
<concept id="metrics:locAndCc">
    <description>Types with the highest LoC (lines of code) and aggregated CC (cyclomatic complexity)</description>
    <cypher><![CDATA[
        MATCH
            (type:Type)-[:DECLARES]->(method:Method)
        RETURN
            type.fqn AS Type, sum(method.effectiveLineCount) AS LoC, sum(method.cyclomaticComplexity) AS CC
        ORDER BY
            LoC DESC, CC DESC
        LIMIT
            20
    ]]></cypher>
</concept>

Nodes mit dem Label Method besitzen bereits die Eigenschaften cyclomaticComplexity und effectiveLineCount. Diese Eigenschaften werden von jQAssistant automatisch berechnet und können direkt abgefragt werden. Die aggregierten Werte werden für den full qualified name (fqn) jedes Java Type (Klasse, Interface, Enum oder Annotation) in der Abfrage zurückgegeben und nach LoC und CC absteigend sortiert.

Bei der Betrachtung der Metriken LoC und CC ist es wichtig zu beachten, dass es keine allgemeingültigen Werte gibt, die als „gut“ oder „schlecht“ gelten. Die Werte sollten immer im Kontext des Projekts betrachtet werden. Es ist jedoch sinnvoll, ein besonderes Augenmerk auf die Code-Teile zu legen, deren Werte im Vergleich zu anderen Teilen des Projekts besonders hoch sind.

Die beiden Metriken LoC und CC werden oft zusammen betrachtet, da sie in einer gewissen Beziehung zueinander stehen. Die Arbeiten von Jay [1] und Landman [2] legen mit größerem Betrachtungsbereich (Methode, Klasse, Package, …) eine steigende Korrelation zwischen CC und LoC nahe. Dies bedeutet, dass die Zyklomatische Komplexität mit zunehmender Anzahl von Codezeilen ansteigt, was die einzelne Aussagekraft der Metrik einschränkt.

Bei der Anwendung dieser Analyse im eigenen Projekt sollte man sich vorher über den Fokus und die Aussagekraft der ermittelten Metriken im Klaren sein. Möchte man mögliche Codestellen für ein Refactoring identifizieren, so empfiehlt sich vermutlich eine Auswertung auf Methoden- oder Klassen-Ebene. Aber auch eine Betrachtung auf Package-Ebene kann sinnvoll sein, um die Struktur des Projekts zu analysieren.

Methoden mit den meisten Parametern

Wenn eine Methode viele Parameter entgegennimmt, kann das ein Hinweis darauf sein, dass die Methode zu viele Aufgaben erfüllt und möglicherweise aufgeteilt werden sollte, um Wartbarkeit und Testbarkeit zu verbessern.

Wenn alle übergebenen Parameter in einem engen Zusammenhang stehen, kann es jedoch auch sinnvoll sein, die Parameter in einem eigenen Objekt zu kapseln und dieses als Parameter zu übergeben.

Regel für Methoden mit den meisten Parametern
<concept id="metrics:MethodsWithHighestNumberOfParameters">
    <description>Methods with the highest number of parameters</description>
    <cypher><![CDATA[
        MATCH
            (type:Type)-[:DECLARES]->(method:Method)-[:HAS]->(parameter:Parameter)
        RETURN
            type.fqn AS Type, method.name AS Method, count(parameter) AS parameterCount
        ORDER
            BY parameterCount DESC
        LIMIT
            20
    ]]></cypher>
</concept>

Die oben gezeigte Regel zählt die Anzahl der Parameter für jede Methode und gibt eine sortierte Liste der Methoden mit den meisten Parametern zurück.

Klassen mit den tiefsten Ableitungshierarchien

Eine Ableitungsbeziehung zwischen Klassen führt zu einer Kopplung, welche die Wartbarkeit des Codes unter Umständen erschwert. Ableitung ist an sich nichts Negatives, es sollte jedoch bewusst eingesetzt werden.

Die folgende Regel liefert eine Übersicht der Klassen mit den tiefsten Ableitungshierarchien.

Regel für Klassen mit den tiefsten Ableitungshierarchien
<concept id="metrics:ClassesWithDeepestInheritanceHierarchy">
    <description>Classes with the deepest inheritance hierarchy</description>
    <cypher><![CDATA[
        MATCH
            path=(class:Class)-[:EXTENDS*]->(super:Class)
        RETURN
            class.fqn AS Class, length(path) AS Depth
        ORDER BY
            Depth DESC
        LIMIT
            20
    ]]></cypher>
</concept>

Durch die Verwendung des *-Operators in der Beziehung [:EXTENDS*] können wir Ableitungsbeziehungen beliebiger Länge abfragen. Die Länge des Pfades entspricht dann der Tiefe der Ableitungshierarchie.

Typen mit den meisten Abhängigkeiten

Wenn wir einen Typen in unserer Codebasis ändern, betrifft das auch alle anderen Typen, die von ihm abhängen.
Eine kleine Änderung an einer Codestelle kann somit unter Umständen viele weitere Änderungen nach sich ziehen.

Um diese Typen mit vielen Abhängigkeiten zu identifizieren, zählt die folgende Regel die Anzahl der Beziehungen zu anderen Typen.

Regel für Typen mit den meisten Abhängigkeiten
<concept id="metrics:TypesWithHighestNumberOfRelations">
    <description>Types which are related the most</description>
    <cypher><![CDATA[
        MATCH
            (:Package)-[:CONTAINS]->(type:Type)<-[relation]-(:Type)
        RETURN
            type.fqn AS Type, count(relation) AS Relations
        ORDER BY
            Relations DESC
        LIMIT
            20
    ]]></cypher>
</concept>

Da jQAssistant nur die Packages des gescannten Codes als Knoten in der Datenbank speichert, können wir uns die Beziehungen zwischen den Typen und den Packages zunutze machen, um nur unsere eigenen Typen zu identifizieren. Mit MATCH (package:Package)-[:CONTAINS]->(type:Type) können wir alle Typen innerhalb eines Packages abfragen. Da alle Packages Teil unseres eigenen Projekts sind, werden also nur eigene Typen gefunden.

Meist geänderte Typen

In jedem länger laufenden Projekt gibt es Codestellen, die man immer wieder anfassen muss und wiederum andere Teile, an denen man fast nie vorbeikommt. Diese Hotspots mit hoher Änderungshäufigkeit sollten besonders gut strukturiert und getestet sein.

Durch die Einbindung des Git-Plugins  ist jQAssistant auch in der Lage, den .git-Ordner eines Projekts zu analysieren und legt verschiedene Information über die Versionshistorie des Projekts in der Neo4j-Datenbank ab. Diese zusätzlichen Informationen erlauben eine Vielzahl von neuen Analysen, z. B. können wir eine Metrik über die Änderungshäufigkeit von Typen erstellen.

Um das Plug-in einzubinden und entsprechend zu konfigurieren, muss die Datei .jqassistant.yml wie Folgt angepasst werden.

.jqassistant.yml
jqassistant:
  plugins:
    - group-id: de.kontext-e.jqassistant.plugin
      artifact-id: jqassistant.plugin.git
      version: 1.12.0

  scan:
    include:
      files:
        - ../.git

Der Pfad zum .git-Ordner muss je nach Projektstruktur entsprechend angepasst werden.

Die vom Git-Plugin  eingesammelten Informationen beziehen sich auf alle Dateien im Repository und haben standardmäßig keine Beziehung zu den Java-Typen des Projekts.

Um eine solche Beziehung herzustellen, definieren wir eine neue Regel in der Datei common.xml.

Regel für die Verbindung von Git-Dateien und Typen
<concept id="common:ConnectGitFilesAndTypes">
    <description>Create a CONTAINS connection between .java Files and the contained Types`</description>
    <cypher><![CDATA[
        MATCH 
            (gitFile:Git:File), (type:Type:File)
        WHERE 
            gitFile.relativePath =~ '.*' + replace(type.fileName,'.class','.java')
        CREATE 
            (gitFile)-[:CONTAINS]->(type)
        RETURN 
            gitFile.relativePath, type.fileName
    ]]></cypher>
</concept>

Durch einen Vergleich des Dateipfades der Git-Datei mit dem Dateinamen des Java-Typs finden wir die entsprechenden Paare und erstellen eine neue CONTAINS-Beziehung zwischen den beiden Knoten.

Die neue CONTAINS-Beziehung kann nun in weiteren Analysen genutzt werden, um von geänderten Dateien auf die betroffenen Java Typen zu schließen.

Die folgende Regel zählt die Anzahl der Commits, in denen die Datei eines Typen geändert wurde und gibt die Typen mit den meisten Änderungen aus.

Regel für die meist geänderten Typen
<concept id="metrics:MostChangedTypes">
    <requiresConcept refId="common:ConnectGitFilesAndTypes"/>
    <description>Most changed Types</description>
    <cypher><![CDATA[
        MATCH
            (commit:Git:Commit)-[:CONTAINS_CHANGE]->(:Git:Change)-[:MODIFIES]->(:Git:File)-[:CONTAINS]->(type:Type)
        RETURN
            type.fqn AS Type, count(commit) AS NumberOfCommits
        ORDER BY
            NumberOfCommits DESC
        LIMIT
            20
    ]]></cypher>
</concept>

Um sicherzustellen, dass die Regel common:ConnectGitFilesAndTypes bereits ausgeführt und die Beziehung zwischen Git-Dateien und Typen hergestellt wurde, definieren wir mit requiresConcept eine Abhängigkeit zu dieser Regel.

Langsame Tests

Je größer ein Projekt wird, desto mehr Testfälle werden geschrieben und die Ausführung der Tests kann immer länger dauern. Ein Überblick über die langsamsten Tests kann bei einer Optimierung der Testlaufzeit eine große Hilfe sein.

Das Java-Plugin  von jQAssistant sucht standardmäßig nach Maven Surefire Reports und speichert die Informationen über die Testfälle in der Neo4j-Datenbank. Durch eine Sortierung nach der Ausführungszeit der Testfälle können wir die langsamsten JUnit Tests in unserem Projekt identifizieren.

Regel für langsame Tests
<concept id="metrics:SlowTests">
    <description>Slowest test cases</description>
    <cypher><![CDATA[
        MATCH 
            (testCase:JUnit:TestCase)
        RETURN 
            testCase.className AS Class, testCase.name AS MethodName, testCase.time AS ExecutionTimeInSeconds
        ORDER BY 
            testCase.time DESC
        LIMIT
            20
    ]]></cypher>
</concept>

Testabdeckung

Das Maß der Testabdeckung gibt an, wie viele Codezeilen durch automatisierte Tests abgedeckt sind. Eine hohe Testabdeckung ist ein Indikator für eine gute Testqualität und kann helfen, Fehler frühzeitig zu erkennen und die Wartbarkeit des Codes zu verbessern.

Ein beliebtes Tool zur Messung der Testabdeckung ist JaCoCo . JaCoCo ist ein Open-Source-Tool, das die Testabdeckung von Java-Code misst und verschiedene Metriken wie Anweisungsabdeckung, Zweigabdeckung und Zyklomatische Komplexität bereitstellt. Auch das weitverbreitete Analyse-Tool SonarQube  nutzt JaCoCo zur Messung der Testabdeckung.

Auch für jQAssistant gibt es ein JaCoCo-Plugin , das die JaCoCo-Reports in die Neo4j-Datenbank importiert und verschiedene Metriken zur Testabdeckung bereitstellt.

Um JaCoCo in ein Maven-Projekt zu integrieren, muss das JaCoCo-Maven-Plugin wie folgt in der pom.xml hinzugefügt werden:

pom.xml
<build>
    <plugins>
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.8.12</version>
            <executions>
                <execution>
                    <id>prepare-agent</id>
                    <goals>
                        <goal>prepare-agent</goal>
                    </goals>
                </execution>
                <execution>
                    <id>report</id>
                    <phase>test</phase>
                    <goals>
                        <goal>report</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Bei jeder Ausführung von mvn test wird das JaCoCo-Maven-Plugin die Testabdeckung messen und die Ergebnisse in einem XML-Report unter target/site/jacoco/jacoco.xml speichern.

Nun fehlt noch die Einbindung des JaCoCo-Plugins in jQAssistant. Dazu muss die .jqassistant.yml-Datei wie folgt angepasst werden:

.jqassistant.yml
jqassistant:
  plugins:
    - group-id: de.kontext-e.jqassistant.plugin
      artifact-id: jqassistant.plugin.jacoco
      version: 1.12.0

  scan:
    include:
      files:
        - target/site/jacoco

    properties:
      jqassistant.plugin.jacoco.filename: jacoco.xml

jQAssistant scannt nun den JaCoCo-Report und speichert Informationen zur Testabdeckung in der Neo4j-Datenbank.

Statt nur die Testabdeckung für das gesamte Projekt zu betrachten, können wir durch diese zusätzlichen Informationen auch spezifischere Regeln erstellen, die die Testabdeckung für bestimmte Teile des Projekts analysieren.

Als Beispiel konzentrieren wir uns wieder auf Klassen mit hoher zyklomatischer Komplexität. Bereits im Kapitel Zeilenanzahl und Zyklomatische Komplexität haben wir diese Klassen identifiziert. Nun können wir auch die Testabdeckung dieser Klassen analysieren.

Die folgende Regel erzeugt eine Liste der Klassen mit hoher zyklomatischer Komplexität und deren Testabdeckung in Prozent.

Regel für Testabdeckung für Klassen mit hoher zyklomatischer Komplexität
<concept id="metrics:CcTestCoverage">
    <description>Test coverage of classes with high cyclomatic complexity</description>
    <cypher><![CDATA[
        MATCH 
            (class:Jacoco:Class)-[:HAS_METHOD]->(method:Jacoco:Method)-[:HAS_COUNTER]->(counter:Jacoco:Counter)
        WHERE 
            counter.type = 'COMPLEXITY'
        WITH 
            class, sum(counter.covered) AS Covered, sum(counter.covered) + sum(counter.missed) AS CC
        RETURN 
            class.fqn AS Class, CC, Covered * 100 / CC AS CoverageInPercent
        ORDER BY
            CC DESC, CoverageInPercent DESC
    ]]></cypher>
</concept>

Neben der Komplexität ermittelt JaCoCo auch die Testabdeckung unter anderem auch auf Basis von Zeilen oder Abzweigungen. In der Dokumentation des JaCoCo-Plugins  findet ihr eine Übersicht über die verfügbaren Metriken.

Je nach Projekt und Anforderungen können hier verschiedenste Regeln erstellt werden. Bei der Verwendung einer Schichtenarchitektur oder Zwiebelarchitektur könnte man zum Beispiele unterschiedliche Grenzwerte für die Testabdeckung in den verschiedenen Schichten festlegen. Diese Art von Analysten der Struktur und Architektur eines Projekts ist Thema des nächsten Teils dieser Blogreihe.

Regeln als Build-Breaker

Alle bisher erstellten Regeln sind Concept-Regeln, die entweder Daten in der Neo4j-Datenbank anreichern oder verschiedene Informationen abfragen, um eine Übersicht über unseren Code zu erhalten.
Es ist jedoch auch möglich, Regeln zu erstellen, die als Constraint-Regeln definiert sind und bei Verletzung den Build abbrechen.

Die meisten der in diesem Blog-Artikel gezeigten Concept Regeln dienen dazu, sich einen Überblick über den eigenen Code zu verschaffen und mögliche Problemstellen zu identifizieren.
Durch kleine Anpassungen können diese Regeln jedoch auch als Constraint-Regeln definiert werden, die den Build abbrechen, wenn bestimmte Bedingungen verletzt sind.

Als Beispiel seht ihr hier eine Constraint-Regel, die überprüft, dass es keine Methoden mit zehn oder mehr Parametern gibt.

Regel für Methoden mit mehr als zehn Parametern
<constraint id="metrics:MethodsMustHaveLessThan10Parameters">
    <description>Breaks the build if there are methods 10 parameters or more</description>
    <cypher><![CDATA[
        MATCH
            (class:Class)-[:DECLARES]->(method:Method)-[:HAS]->(parameter:Parameter)
        WITH
            class, method, count(parameter) AS parameterCount
        WHERE
            parameterCount >= 10
        RETURN
            class.fqn AS Class, method.name AS Method, parameterCount
    ]]></cypher>
</constraint>

Wenn die Cypher-Abfrage einer Constraint-Regel ein Ergebnis zurückgibt, wird der Build abgebrochen. 

Mithilfe von Constraint-Regeln können wir also harte Grenzen für unser Projekt festlegen und sicherstellen, dass bestimmte Regeln eingehalten werden.

Dokumentation

Bereits im ersten Teil haben wir die Ergebnisse der jQAssistant Regeln in die AsciiDoc-Dokumentation eingebunden. Durch die folgende Anpassung in der Datei src/docs/asciidoc/documentation.adoc können wir die neu erstellten Constraint- und Concept-Regeln in die Dokumentation einbinden.

= Projektdokumentation
:toc: left
:toclevels: 4

== jqAssistant
=== Constraints
include::jQAssistant:Summary[constraints="*"]

=== Metrics
include::jQAssistant:Rules[concepts="metrics:*", leveloffset=+3]

Da fehlschlagende Constraint-Regeln den Build abbrechen und dadurch auch die Erstellung der Dokumentation verhindert wird, betten wir in der Dokumentation über include::jQAssistant:Summary[constraints="*"] eine Übersichtstabelle aller Constraint-Regeln ein.

Darunter folgt dann die Einbindung aller Concept-Regeln aus der Gruppe metrics. Bei der Einbindung von jQAssistant-Regeln in die Dokumentation wird der Regelname standardmäßig als Überschrift der Ebene 0 dargestellt. Durch die Angabe leveloffset=+3 wird die Überschriftebene der Regeln um drei Ebenen erhöht, sodass die Regeln unterhalb der Überschrift Metrics eingerückt werden.

Ausblick

Bisher haben wir den Code nur als ganzes betrachtet und die Struktur der Klassen und Packages außer Acht gelassen. Vor allem in größeren Projekten ist eine klare Strukturierung der Anwendung jedoch unerlässlich. Auch diese Struktur und Softwarearchitektur kann mit jQAssistant analysiert und überwacht werden. Dies wird der Fokus des nächsten Teils dieser Blogreihe sein.

Quellen

[1] Jay, Graylin & Hale, Joanne & Smith, Randy & Hale, David & Kraft, Nicholas & Ward, ;Charles. (2009). Cyclomatic Complexity and Lines of Code: Empirical Evidence of a Stable Linear Relationship. JSEA. 2. 137-143. 10.4236/jsea.2009.23020.

[2] Landman, Davy & Serebrenik, Alexander & Vinju, Jurgen. (2014). Empirical Analysis of the Relationship between CC and SLOC in a Large Corpus of Java Methods. Proceedings – 30th International Conference on Software Maintenance and Evolution, ICSME 2014. 10.1109/ICSME.2014.44.