Einsteiger-Tipps für Programmierer

Gastbeitrag von | 16.09.2021

Als Programmierer sollten Sie vor allem eine Sache kultivieren: Faulheit. Das soll nicht heißen, dass Sie sich nicht voll in eine Sache reinhängen oder nicht Ihren ganzen Jahresurlaub für Ihre neue Idee verwenden sollten. Es heißt eher, dass Sie sich beim Programmieren Arbeit sparen und Bestandteile wiederverwenden, verbessern und anpassen sollten, wo Sie nur können. Das spart Ihnen Zeit, die Sie für die komplexeren Probleme benötigen, und sorgt für eine strukturiertere Arbeitsweise und einen besseren Code.

Darüber hinaus ist Faulheit der beste Ideengeber für neue Applikationen oder neue Features. Der Computer selbst wäre wohl erst gar nicht entstanden, wenn Menschen nicht zu faul gewesen wären, langwierige Berechnungen von Hand durchzuführen. Höhere Programmiersprachen wie C++ wären nicht entstanden, wenn man nicht zu faul gewesen wäre, Bytecode Zeichen für Zeichen einzugeben. Und Netflix hätte es nicht gegeben, wenn sein Gründer nicht zu faul gewesen wäre, geliehene Videokassetten pünktlich zurückzugeben.

Nach diesem leicht ironischen Einstieg in die Geisteshaltung eines erfolgreichen Programmierers möchte ich Ihnen einige Tipps für den Einstieg in das Thema Programmierung bzw. C++ Entwicklung geben. Es geht um zwei konkrete und sehr nützliche Clean Code Prinzipien, um Recherche, Testing und praktische Tools. Los geht’s.

Die Entwicklung von Clean Code

Clean Code bezeichnet Code, der leicht zu verstehen, zu ändern, zu erweitern und zu warten ist. Er ist das Ergebnis von Werten, Prinzipien und erfolgreichen Praktiken der Softwareentwicklung. Es geht bei der Programmierung nicht darum, Herausforderungen extra schlau zu meistern, auf individuelle Art, besser und klüger als alle anderen Programmierer ein Problem gelöst hätten. Clever Code klingt im ersten Augenblick positiv, ist aber ein Synonym für eine vordergründig raffinierte Art, Software zu entwickeln, die jedoch für eine professionelle Programmierung nicht empfehlenswert ist.

Zwei von 19 Prinzipien con Clean Code möchte ich Ihnen kurz vorstellen:

DRY: Don‘t Repeat Yourself

Versuchen Sie sich als Programmierer möglichst selten zu wiederholen. Ähnlichen Code an mehreren Stellen zu haben, lässt sich nicht immer vermeiden, sollte aber immer mit Bedacht geprüft werden. Spätestens bei drei ähnlich geschriebenen Stellen müssen Sie überlegen, ob sich der Teil in eine separate Funktion oder eine Klasse auslagern lässt. Ich stelle mir immer die Frage “Wenn ich später einen Bug an dieser Stelle beheben müsste, wie hoch ist das Risiko, dass ich die anderen, gleichlautenden Stellen vergesse??” Sie ersparen sich dadurch eine Menge Arbeit.

KISS: Keep it simple and stupid

Das KISS-Prinzip ist ein Leitspruch, der nicht nur in der Softwareentwicklung hilfreich ist. “Halte es einfach und primitiv” bedeutet, beim Lösen eines schwierigen Problems die einfache Lösung der komplexen vorzuziehen. Klingt einleuchtend, aber gerade als Tüftler und Hacker lassen Sie sich von komplizierten Lösungen oft so leicht faszinieren, dass Sie den pragmatischen Weg übersehen. Das beste Beispiel hierfür ist leider nur eine urbane Legende und unwahr, sie trifft den Nagel aber dennoch auf den Kopf: Während des Wettrennens in das Weltall hatte die NASA festgestellt, dass normale Kugelschreiber in der Schwerelosigkeit nicht funktionieren würden und daher für eine Million Dollar einen Spezialkugelschreiber entwickelt. Die Sowjetunion habe dagegen einfach Bleistifte verwendet. Die Kunst beim Lösen schwieriger Probleme ist es also, die einfachen Lösungen nicht zu übersehen.

Beim Entwickeln für Software ist das KISS-Prinzip besonders wichtig, denn Software verändert sich. Sicher, wenn Sie ein Programm fertiggestellt haben, was Ihr Problem zufriedenstellend löst, könnte es Ihnen egal sein, ob Sie es besonders kompliziert und verschachtelt programmiert haben – Hauptsache, es funktioniert! In der Realität ist es jedoch selten so, dass ein Programm fertiggeschrieben und dann nie wieder verändert wird. Sobald Sie ein paar Monate später eine neue Funktionalität hinzufügen oder ein Bugfixing betreiben dürfen, wird eine überkomplexe Implementierung zum Verhängnis.

Leider ist die Frage, was genau denn eine unnötig komplizierte Programmierung ist und was nicht, überhaupt nicht trivial. Die Frage, wie man eine besonders gute Softwarearchitektur designt und wie man obige Prinzipien umsetzen kann, füllt ganze Bücher¹.

Die Internetrecherche als Wissensquelle für Programmierer

Böse Zungen behaupten, ein Programmierer sei eine Maschine, die Kaffee in Code umwandelt. Noch bösere Zungen behaupten, ein Programmierer sei eine Maschine, die Kaffee in Internetsuchbegriffe umwandelt. Da ist sicherlich etwas Wahres dran, denn durch die schiere Menge an Programmierforen und Open-Source-Projekten gibt es eine Fülle an frei verfügbarem Wissen im Internet.

Hinweis: Die allermeisten Seiten für Programmierer sind auf Englisch, daher hilft es sehr, Visual Studio auf Englisch umzustellen: Extras → Optionen → Umgebung → Internationale Einstellungen. Dadurch werden Compiler und andere Fehler in Englisch ausgegeben und sind leichter zu recherchieren.

In vielen Fällen werden Sie auf Artikel von Stack Overflow, ein Onlineforum zum Stellen und Beantworten von Programmierfragen², treffen. Leider müssen Sie gerade bei C++-Fragen akzeptieren, dass diese Sprache sehr formalisiert ist und die Antworten daher manchmal sehr mathematisch akkurat geschrieben sind. Meiner Meinung nach würde etwas mehr Pragmatik helfen, aber man gewöhnt sich daran. Vielleicht sehen Sie irgendwann ja auch ein paar unbeantwortete Fragen, für die Sie selbst eine Antwort schreiben könnten – die Seite lebt vom Austausch!

Des Weiteren sind die Seiten https://gitlab.com/explore und https://github.com/explore wahre Fundgruben an Open-Source-Projekten in den unterschiedlichsten Programmiersprachen. Wenn Sie sich Code von anderen Programmierern anschauen wollen, werden Sie hier fündig. Allerdings kann das sehr schnell wirklich komplex werden, und Sie stoßen auf einige Konzepte, die ich hier in meinen Ausführungen ausklammere.

Softwaretesting mit Unittests

Programmierer machen Fehler. Glücklicherweise können Sie technische Hilfe in Anspruch nehmen, um diese Fehler aufzuspüren. Neben dem Debugger oder selbstgeschriebenen Logging-Klassen zum Analysieren von Fehlern können Sie aber auch automatische Tests schreiben, die Fehler finden, bevor es Ihre Kunden tun. Besser noch – wann immer Sie einen Fehler behoben haben, können Sie einen dazugehörigen automatischen Test schreiben, der sicher stellt, dass der Fehler auch in Zukunft nicht mehr auftreten wird.

Für das Schreiben automatischer Tests gibt es eigene Bibliotheken und ganze Bücher, die sich nur damit beschäftigen³. Auch gibt es bestimmte Arten von Bugs, für die ein automatischer Test sehr aufwendig ist – beispielsweise Darstellungsfehler oder Fehlerbehandlungen selbst. Daher möchte ich Ihnen hier nur einen winzigen Einblick in eine spezielle Art von Tests geben, nämlich die Unittests.

Unittests (vom englischen “Unit” für “Einheit”) bedeuten, dass Sie Ihr Programm in kleinstmögliche Einheiten unterteilen und diese einzeln testen. Sie nehmen zum Beispiel jede Funktion, übergeben ihr jeweils ein paar Parameter und vergleichen, ob das Ergebnis dem erwarteten Wert entspricht.

Lassen Sie uns dafür einen “Selbsttest” schreiben, der einfach als Teil unseres Programms mitläuft. Das soll Ihnen ein Gefühl dafür geben, wie man als Softwaretester vorgeht. Im vorliegenden Beispiel wird die Implementierung der Fakultätsfunktion factorial() getestet:

UnitTest.cpp

#include <iostream>
#include <vector>
#include <exception>

int factorial(int number)
{
if (number == 0)
{
return 1;
}
return number * factorial(number - 1);
}

void unitTests()
{
// Test-Code für die Berechnung der Fakultät
const std::vector<int> tests = { 6, 0, 1 };
const std::vector<int> expected = { 720, 1, 1 };
for (int i = 0; i < tests.size(); i++)
{
int actual = factorial(tests.at(i));
if (actual != expected.at(i))
{
std::cerr << tests.at(i) << "! ergab "
<< actual << ", erwartet wurde "
<< expected.at(i) << std::endl;
throw std::exception("factorial() is broken!");
}
}
}

int main()
{
unitTests();

int number;
std::cout << Zahl zwischen 0 und 12 eingeben: ";
std::cin >> number;
std::cout << std::endl << "Fakult\204t: " << factorial(number)
<< std::endl;
return 0;
}
Im Unittest sollten insbesondere die Randfälle (im vorliegenden Fall 0 und 1) auftauchen. Wie Sie sehen, wird hier der Test immer am Anfang des Programms ausgeführt. In der Realität werden diese Tests natürlich vom eigentlichen Programm getrennt und separat nur auf dem Rechner der Entwickler ausgeführt, nicht bei jedem Endnutzer des Programms. Wenn sie aber so schnell ausführbar sind wie in diesem Beispiel, spricht der Einfachheit halber nichts dagegen.

Wenn Sie ein wenig mit Unittests Erfahrung gesammelt haben, möchte ich Ihnen empfehlen, das Ganze umzudrehen: Test-Driven Development heißt eine Methode, bei der man den automatischen Test zuerst schreibt und erst danach die Funktion oder Klasse, die getestet wurde.

  1. Schreiben Sie den Rumpf der zu testenden Funktion oder Klasse. Definieren Sie alle Eingabe- und Ausgabewerte, lassen die Implementierung zunächst jedoch weg. Falls ein Rückgabewert benötigt wird, geben Sie einen Dummy zurück, zum Beispiel: int sum(int a, int b) { return 0; }.
  2. Nun schreiben Sie den Test, der bestimmte Werte übergibt und das Ergebnis mit den erwarteten Werten vergleicht.
  3. Beim Schreiben des Tests werden Sie oft merken, dass Ihr ursprüngliches Konzept nicht vollständig war. Dadurch, dass Sie den Code in einer kontrollierten Umgebung benutzen, fällt Ihnen vielleicht auf, dass die Aufrufe zu verschachtelt sind oder dass Sie weitere Hilfsfunktionen haben möchten (die Sie natürlich auch testen wollen).
  4. Nachdem alles berichtigt wurde, führen Sie den Test aus. Er sollte fehlschlagen, denn die Funktion/Klasse ist ja noch nicht richtig implementiert. Das ist ein weiterer wichtiger Punkt, denn so stellen Sie sicher, dass Ihr Test auch wirklich einen Fehler finden kann (Teste den Test!).
  5. Nun können Sie sich an die eigentliche Implementierung machen. Danach sollte der Test bestanden werden.

 

Weitere Tipps und Tools

In diesem kurzen Abschnitt möchte ich Ihnen noch einige Denkanstöße und Schlüsselwörter liefern, die Sie bei Bedarf weiter recherchieren können. Das Themenfeld ist riesig.

Für den Entwicklungsprozess gibt es unzählige verschiedene Methoden. Relativ verbreitet und modern ist die agile Softwareentwicklung, bspw. basierend auf Scrum. Sie basiert auf der Erkenntnis, dass man in einem Softwareprojekt die Planungsphase und Entwicklungsphase nicht strikt voneinander trennen kann. Da zu Beginn eines Projekts noch nicht alle Anforderungen feststehen und sich einige Aspekte erst beim Durchführen ergeben, hat es keinen Sinn, eine mehrmonatige Planungsphase durchzuführen und dann ein Jahr lang weitestgehend ohne Kundenfeedback zu programmieren. Stattdessen geht man nach einer initialen, kurzen Planung möglichst schnell in die Entwicklung über, wiederholt und aktualisiert dafür dann aber die Planung regelmäßig. Üblich sind hier sogenannte Sprints von zwei oder vier Wochen, die mit einem Planungsmeeting beginnen, in dem die Entwicklungsziele für den Sprint festgehalten werden.

Anforderungen werden häufig in Form von User Storys formuliert. Diese Storys sollten an einem zentralen Ort gesammelt und dem Team zur Verfügung gestellt werden. Hierfür benötigen Sie ein sogenanntes Ticketsystem. Aus meiner Sicht eignen sich Trello oder GitLab sehr gut. In solchen oder ähnlichen Tools können Sie auch Bugs, also die Beschreibung von gefundenen, noch nicht behobenen Programmfehlern, speichern.

Dokumentieren Sie folgende Informationen im Bug Report:

  • In welcher Softwareversion tritt der Bug auf?
  • Mit welchen Schritten kann der Fehler reproduziert werden?
  • Welches Ergebnis hätte ich erwartet?
  • Welches Ergebnis habe ich bekommen?

Ein weiterer Punkt ist das gemeinsame Arbeiten am selben Code. Ein Programm zur Versionsverwaltung ermöglicht es, über einen Server Code auszutauschen. Sobald eine neue Funktion implementiert oder ein Fehler behoben wurde, lädt der Programmierer die Änderung mit einer kurzen Beschreibung hoch, sodass sie automatisch mit den Änderungen der anderen zusammengeführt wird. Meine Empfehlung hier ist Git.

Zuletzt benötigen Sie noch ein Tool zum Austauschen von Nachrichten. E-Mails werden schnell unübersichtlich, daher empfehle ich Ihnen, ein Onlineforum aufzusetzen oder einen teamorientierten Chatdienst wie bspw. Slack oder Element.io zu verwenden. Wichtig ist nur, dass Sie Unterhaltungen in Threads organisieren können und sie leicht wiederzufinden sind.

 

Hinweise:

Interessieren Sie sich für weitere Tipps aus der Praxis? Testen Sie unseren wöchentlichen Newsletter mit interessanten Beiträgen, Downloads, Empfehlungen und aktuellem Wissen.

[1] Bspw. Clean Architecture – Das Praxis-Handbuch für professionelles Softwaredesign von Robert C. Martin
[2] Stack Overflow veranstaltet jedes Jahr eine Umfrage unter Programmierern über Technologien, Methoden und Tools. Hier finden Sie eine Auswahl der Ergebnisse aus dem Jahr 2020.
[3] Bspw. The Art of Unit Testing von Roy Osherove

Dieser Artikel ist ein Auszug aus dem neuen Buch “C++ Schnelleinstieg – Programmieren lernen in 14 Tagen” von Philipp Hasper. Alle Infos zum Buch, das Inhaltsverzeichnis und eine kostenlose Leseprobe finden Sie hier beim Fachbuchverlag für IT, Business und Fotografie mitp.

C++ Schnelleinstieg - Blog - t2informatik
Philipp Hasper
Philipp Hasper

Philipp Hasper ist Gründer eines Augmented-Reality-Startups und erfahren in der akademischen und industriellen Entwicklung von KI-Technologien. Er entwickelt mit C++, Java, Python und Typescript und hat bei zahlreichen Open-Source-Projekten mitgewirkt.