ChatGPT in der Industrie - Teil 8
Codestrukturen bewerten und verbessern
Eignen sich gpt-4o und Google Gemini auch dafür, die Codequalität und die Codestrukturen zu bewerten? Oder sollte der Entwickler hierfür doch besser auf klassische Software-Werkzeuge zurückgreifen? Fragen, die eine genauere Betrachtung wert sind.
Teil 7 dieser Serie zeigte, wie sich mit dem LLM gpt-4o architektonische Strukturen in einer Software erkennen und als UML-Diagramm visualisieren lassen. Im Hinblick auf die Architekturdokumentation ist das durchaus nützlich. Dieser Artikel geht darauf ein, wie gut sich mit gpt-4o und mit Google Gemini auch die Qualität des zugrundeliegenden Codes beurteilenen lässt.
Ein wesentlicher Aspekt der Software-Entwicklung ist die kontinuierliche Verbesserung und Optimierung des Codes, der Codestrukturen und der Architektur. Für diese Aufgabe stehen klassische Software-Analysewerkzeuge zur Bestimmung von Qualitätsmetriken zur Verfügung. Es kann zum Beispiel ermittelt werden, wie komplex die Software ist, also wie schwierig diese zu verstehen und zu verändern ist. Eine andere übliche Metrik wäre etwa der Maintainability Index, der eine Kombination aus mehreren Metriken darstellt. Dieser Index gibt an, wie leicht eine Software gewartet werden kann, beziehungsweise wie hoch das Fehlerrisiko bei einer Änderung ist. Diese Analysewerkzeuge sind allerdings meist teuer oder für das Entwicklungsteam nicht zugänglich. Günstig wäre es, wenn sich auch LLMs mit Chat-GPT und Co. zur einfachen Codebewertung verwenden ließen.
In Bezug auf quantitative Metriken ist das nur sehr eingeschränkt möglich. Denn ein LLM ist kein Berechnungstool, keine Wissensdatenbank und kein deterministischer Regelautomat. Dies bedeutet, dass LLMs nur sehr bedingt nützlich sind, um Codemetriken für die Codebeurteilung zu ermitteln und zu berechnen. Schauen wir uns dazu einige Bewertungsexperimente mit dem LLM ‚gpt-4o‘ von OpenAI und dem LLM ‚Gemini‘ von Google an.
Quantitative Bewertung
Für unseren Versuch verwenden wir die vergleichsweise einfach zu ermittelnde »Cyclomatic Complexity« (CC) nach McCabe. Diese Metrik misst die Anzahl der unabhängigen Ausführungspfade in einem Softwareprogramm und dient als Indikator für dessen Komplexität. Je höher die Komplexität, desto aufwendiger und risikoreicher werden Änderungen und Fehlersuchen in dem entsprechenden Code. Die Interpretation und Einschätzung der konkreten Werte werden wie folgt empfohlen:
- 1 - 10: Einfacher Code, geringes Änderungsrisiko, leicht verständlich, gut testbar, potenziell fehlerfrei
- 11 - 20: Komplexerer Code, moderates Risiko, höherer Einarbeitungs- und Testaufwand
- 21 - 50: Komplexer Code, hohes Änderungsrisiko, erheblicher Einarbeitungs- und Testaufwand
- > 50: Untestbarer Code, sehr hohes Änderungsrisiko, potenziell fehlerbehaftet
Die Versuche werden in der Programmiersprache Python durchgeführt. Das Versuchsobjekt ist der quelloffene Code der Bibliothek ‚Chainlit‘. Die Referenz in Bezug auf die Codemetriken ist das Python-Software-Analysewerkzeug ‚Radon‘. Die Versuche mit den LLMs werden jeweils dreimal ausgeführt, um auch die Varianz innerhalb der LLM-Antworten aufzuzeigen. Vor den eigentlichen Versuchen mit Quellcode wurde jeweils mittels eines eigenen Prompts geprüft und sichergestellt, dass gpt-4o und Gemini die Begriffe und die dazugehörigen Verfahren zur Ermittlung der Cyclomatic Complexity nach McCabe prinzipiell kennen. Der verwendete Prompt wurde, um eine gegenseitige Beeinflussung auszuschließen, jeweils in einem neuen Chat durchgeführt. Bei Gemini war es erforderlich, jeweils im Incognito-Modus des Webbrowsers einen neuen Tab zu verwenden. Das Antwortverhalten hatte ohne diese strikte Trennung der Fragen darauf hingedeutet, dass die unterschiedlichen Chats nicht komplett rückwirkungsfrei voneinander sind.
Der Test-Prompt lautet wie folgt:
You are an expert code analyst with deep knowledge of cyclomatic complexity (CC) analysis. Please determine the McCabe CC of this code in the following format:
total_cc = sum of all cyclomatic complexities of all methods in the given file
num_methods = total number of methods detected in code
num_methods_greater_0 = number of methods with a CC greater than zero
average_cc = total_cc / num_methods_greater_0
```python
<code to be analyzed comes here>
```
Die Ergebnisse in Tabellenform
| Versuchsdatei | Werkzeug | Cyclomatic Complexity (Summe) | Cyclomatic Complexity (Durchschnitt) | Anzahl der gefundenen Funktionen | Anzahl der Funktionen mit CC > 0 |
|---|---|---|---|---|---|
| Server.py (948 LOC) | gpt-4o (1. Test) | 89 | 3,0 | 30 | 30 |
| gpt-4o (2.Test) | 107 | 3,8 | 28 | 28 | |
| gpt-4o (3. Test) | 81 | 2,9 | 28 | 28 | |
| Gemini (1. Test) | 29-49 | 2,1-3,5 | 14 | 14 | |
| Gemini (2. Test) | - | - | - | - | |
| Gemini (3. Test) | - | - | - | - | |
| Radon (McCabe) | 116 | 3,9 | 30 | 30 | |
| Step.py (475 LOC | gpt-4o (1. Test) | 51 | 2,8 | 18 | 18 |
| gpt-4o (2.Test) | 50 | 2,8 | 18 | 18 | |
| gpt-4o (3. Test) | 60 | 4,0 | 15 | 15 | |
| Gemini (1. Test) | 18 | 2 | 12 | 9 | |
| Gemini (2. Test) | 12 | 1,5 | 10 | 8 | |
| Gemini (3. Test) | 12 | 1,71 | 8 | 7 | |
| Radon (McCabe) | 83 | 4,2 | 20 | 20 |
In der Tabelle fallen sofort die deutlichen Abweichungen der von den LLMs ermittelten Werte zum Metriken-Tool Radon auf. Zum einen ist die systematische Unterbewertung im Gegensatz zu Radon augenscheinlich. Die Erklärung dafür ist, dass der einfache Prompt die Bestimmung der McCabe-CC nicht exakt in jedem Punkt und Parameter definiert. Somit unterscheidet sich die Berechnungsvorschrift zur Ermittlung der CC in Radon und den LLMs allein schon auf algorithmischer Ebene. Dies führt in unseren Beispielen zu tendenziell zu niedrigeren LLM-Werten. Das ist ein nicht überraschendes Problem, denn es ist nicht anzunehmen, dass exakt der gleiche, fest codierte Algorithmus von Radon auch dem Modellwissen entspricht. Wer sich hier dem Metriken-Tool seiner Wahl besser annähern will, muss die im Tool codierten Analysevorschriften explizit im Prompt aufführen. Da die Abweichungen systematischer Natur sind, sind die Größenordnungen in sich einigermaßen konsistent und somit im Sinne einer groben Hausnummer dienlich. Wobei hier sicherlich immer ein erfahrener Entwickler oder eine Entwicklerin ein kritisches Auge darauf werfen sollte, bevor konkrete Schlüsse aus den LLM-Metriken gezogen werden.
Zum anderen fallen die sehr stark schwankenden Werte auf. Damit kann gpt-4o oder Gemini eher nur als grobe Richtung als einer konkreten Metrik dienen. Wer also genau seinen Code vermessen will, wird um die am Markt befindlichen Architektur- und Codeanalyse-Werkzeuge nicht herumkommen. Der Einsatz von gpt-4o oder Gemini ist zum aktuellen Stand der Technik nur für eine ganz grobe Einschätzung oder in Ermangelung eines Metriken-Tools als notdürftiger Ersatz denkbar.
Qualitative Bewertung
Im Gegensatz dazu können wir bei der qualitativen Bewertung von Software-Strukturen und -Architekturen mehr Hilfestellung von den LLMs erwarten. Auch wenn die Antworten und Einschätzungen der LLM nur als Formulierungsvorschlag und nicht als absolute Wahrheit verstanden werden sollten, so ist es doch überraschend, was mit den entsprechenden Prompts erreicht werden kann. Zwei Beispiele zur Bewertung der Softwarestrukturen verdeutlichen die Fähigkeiten von gpt-4o:
- Single Responsibility Principle (SRP): Eine Klasse sollte nur eine Verantwortung haben, das heißt nur einen Grund, sich zu ändern. Dies verbessert die Wartbarkeit und Verständlichkeit des Codes.
- Separation of Concerns (SoC): Dieses Prinzip besagt, dass ein Programm in verschiedene Bereiche (Concerns) unterteilt werden sollte, die jeweils eine bestimmte Funktion oder Aufgabe übernehmen.
Diese beiden Prinzipien helfen, Software-Architekturen zu entwerfen, die robust, wartbar und erweiterbar sind, indem sie die Komplexität reduzieren und die Wiederverwendbarkeit erhöhen.
Der Code für unser Anwendungsbeispiel ist wieder das Backend der quelloffenen Software Chainlit. Der verwendete Testprompt analysiert in einem Schritt die gesamte Codebasis und generiert daraus eine Bewertung mit potenziellen Verbesserungsvorschlägen. Der ungekürzte Prompt umfasst in unserem Testsetup 6443 Zeilen. Der konkrete Test-Prompt, der sich für diese Aufgabe bewährt hat, lautet wie folgt:
# RULES OF AN EXPERT ARCHITECTURE ANALYST
You are a helpfull architecture analyst who is familiar with be following best practices of software architecture:
1. **Separation of Concerns**: Divide the system into distinct sections, each responsible for a specific aspect or concern, to reduce complexity and improve maintainability.
2. **Single Responsibility Principle**: Ensure each component or module has one, and only one, reason to change by focusing on a single responsibility or functionality.
# GIVENS
<your code comes here> Testprompt mit der gesamten Codebasis des Chainlit Backends wird aus Platzgründen nicht dargestellt
# Intention
generate proposals in order to enhance the quality of the given code with respect to follow strictly the »single responsibility principle« and the »separation of concerns«
# COMMANDS FOR ARCHITECTURE QUALITY ANALYSIS AND IMPROVEMENT
Your job is now:
* Remember you are strictly following your given RULES and SKILLS AS AN EXPERT ARCHITECTURE ANALYST
* Silently analyze the intended architectural structure and behavior of the entire given code base in detail and also analyse any further documentation in detail if given.
* return a table of your five top rated proposed enhancements sorted by the »Architecture enhancement rating« and formatted in the following way (one example line is given as reference):
| Number | filename | method name | Line of code (from - to) | Architecture enhancement rating (0 - 1) | explanation of enhancement |
| 1 | file.py | some_method | 30-65 | 0.9 | {here comes an explanation that on the level of a precise and detailed refactoring prompt} |
* In case you think information is missing to generate a sufficiently precise formulation, return a warning »WARNING: information is missing to correctly fulfill the job!« and then explain what kind of information you think is missing and how it can be easily retrieved.
Das Ergebnis des Prompts, das hier nicht dargestellt wurde, ist eine Bewertungstabelle mit Verbesserungsvorschlägen für Refactorings zur Architekturverbesserung. Die Vorschläge sind in sehr vielen Fällen berechtigt und führen im Sinne der SRP und SoC zu einer modularen und besser strukturierten Software. In einem praktischen Anwendungsszenario würde nun eine Prüfung der Vorschläge durch einen menschlichen Experten stattfinden, um dann in einem Refactoring-Schritt den entsprechenden Code händisch zu verändern oder durch eine LLM automatisiert anpassen zu lassen. Allerdings ist wie bei LLMs immer zu beachten: Ein und derselbe Prompt mehrfach ausgeführt, wird unterschiedliche Vorschläge generieren. Somit sind die Vorschläge zwar sehr häufig nützlich, aber nie als vollständig oder als ab- solute Wahrheit zu betrachten.
Was die Ermittlung von Codemetriken anbelangt, sollte man sich nicht zu sehr auf die LLM-Ergebnisse verlassen. Wer mehr als eine ganz grobe Richtung der Codequalität benötigt, sollte die konkrete Analyse und Bewertung nach wie vor mit klassischen Softwarewerkzeugen ermitteln. Die mit LLMs generierten Vorschläge sind hingegen in Bezug auf die Verbesserung der Softwarestruktur immer einen Versuch wert. Ganz besonders, wenn der Kontext nicht zu groß gewählt wird, damit die LLM-Antwort nicht zu generische und damit unnütze Antworten liefert.
















