Mit einer guten Fehlerkultur ans Ziel

Softwarefehler beheben und als Chance nutzen

Christian Kranert

© Shutterstock / alphaspirit.it

Bugs kosten Nerven, Mühe und Zeit, die wir immer für neue Funktionen besser investieren könnten. Das geht, wenn wir mit einer fehlerfreundlichen Kultur und einem effizienten Prozess an die Fehlerbehebung herangehen.

Das Telefon klingelt. Eine aufgeregte Stimme legt ohne Begrüßung sofort los: „Wir sind down! Was ist los!?“ Der Blick wechselt innerhalb einer Nanosekunde zum Performance-Monitor. Nüchtern zeigt der an: 0 Anfragen/Sekunde. Null, null, null! Der Puls explodiert und im gleichen Moment schießen die Gedanken in den Kopf: „So ein Mist! Was ist passiert? Da war von Anfang an so ein schlechtes Gefühl bei diesem Release!“ Im gleichen Moment wird die Tür aufgerissen, der CEO baut sich im Rahmen auf und ruft „Nicht schon wieder! Alleine die letzte Downtime hat uns Zigtausend gekostet!“

Okay zugegeben, nicht jeder Fehler mündet in einem solchen emotionalen Drama aber Fakt ist, dass jeder Fehler Schaden anrichtet. Manche in kleinem, andere in sehr großem Umfang. Ob eine Beeinträchtigung der Benutzererfahrung, eine Sicherheitsgefährdung, finanzieller Schaden durch entgangene Verkäufe, Rufschädigung durch schlechte Presse oder Schadensersatzforderungen – die Liste möglicher Auswirkungen ist unendlich lang.

In Fehlern liegt aber nicht nur die Gefahr von Schaden, sondern auch das Potential von Verbesserung. Wenn wir aus Fehlern lernen, dann begrenzen oder verhindern wir sogar den Schaden, der durch zukünftige Fehler entstehen könnte. Um von diesem Verbesserungspotential zu profitieren, muss es mit der richtigen Herangehensweise im Bug Management ausgeschöpft werden. Das ist keine Selbstverständlichkeit, denn allzu oft erfahren Bugs im Entwicklungsalltag eine geradezu stiefmütterliche Behandlung: sie werden – wenn überhaupt – nur andeutungsweise erfasst, mit möglichst wenig Zeitaufwand behoben und ohne davor oder danach darüber zu sprechen in ein Release überführt.

Fehler sind nicht böse

Die Ursachen dafür liegen auf der Hand: Fehler stehen stets in Zusammenhang mit Schuld. Schuld ist immer unangenehm, egal ob sie bei mir selbst oder jemand anderem zu suchen ist. Fehler fordern uns außerdem zu Rechtfertigungen heraus und halten uns davon ab, uns den Dingen zuzuwenden, die wirklich Spaß in unserem Beruf machen. Viele gute Gründe also, nur wenig Zeit mit ihnen zu verbringen.

Um diesem Umstand zu begegnen, ist es unerlässlich, ein anderes Bild von Fehlern zu entwickeln und dieses fest in der Teamkultur zu verankern. Fehler bergen das Potential, noch besser zu werden. Wer über sie offen spricht, sollte Anerkennung und Wertschätzung erfahren. Nur, wenn dieses Bild von dem gesamten Team und allen übergeordneten Instanzen mitgetragen wird, kann eine Fehlerkultur entstehen, die langfristig zu noch besserer Qualität und damit zufriedeneren Kunden beiträgt. Heimlichtuerei ist im Umfeld einer solchen Kultur genauso schädlich wie destruktive Schuldzuweisungen. Das heißt aber nicht, dass der Urheber eines Fehlers nicht offen darauf angesprochen werden darf. Es muss aber unbedingt im richtigen Rahmen – das heißt ohne bloß gestellt zu werden – geschehen und die Kritik muss immer in einer Art und Weise geäußert werden, die rein auf eine fachliche Verbesserung der betreffenden Person abzielt. Persönliche Angriffe verbieten sich von selbst.

In der Regel werden Bugs nicht von den Entwicklern selbst, sondern von Kunden, Service-Mitarbeitern oder Produkt-Managern entdeckt. Egal von wem sie gemeldet werden, es ist wichtig, dass diese Personen willkommen geheißen werden. Zugegeben, das fällt nicht immer leicht, besonders dann, wenn es häufig vorkommt. Selbst wenn wir eine solche Person noch so sympathisch finden, kommt sie ja augenscheinlich wegen eines Problems zu uns – die Folgen davon kennen wir ja schon. Doch wenn wir uns jetzt an das Leitbild des Verbesserungspotentials zurückerinnern, dann berichtet uns diese Person genau genommen nicht über ein Problem, sondern die Chance noch besser zu werden. Es ist sehr wichtig, dass der Person nie das Gefühl vermittelt wird, mit ihrem Anliegen unerwünscht zu sein, denn das wird unweigerlich dazu führen, dass weniger Bugs berichtet werden und folglich weniger Verbesserungspotential ausgeschöpft wird.

Das richtige Werkzeug

Ein ebenfalls sehr wichtiger Aspekt im Zusammenhang mit der Erfassung von Bugs ist der bereits erwähnte Bug Tracker. Ein Tool, in dem die Bugs initial erfasst, priorisiert, im weiteren Verlauf dokumentiert und schlussendlich statistisch ausgewertet werden können. Es gibt hunderte verschiedene Lösungen von sehr einfach bis sehr umfangreich und es würde den Rahmen dieses Artikels sprengen im Detail auf sie einzugehen. Wichtig ist, dass der Bug Tracker zu den Ansprüchen des Teams sowie des Projektes passt und daher sollte auf seine Auswahl und seine Konfiguration entsprechendes Augenmerk gelegt werden. Er muss es ermöglichen, den Überblick über alle ausstehenden Bugs zu behalten, sollte eine gewisse Individualisierbarkeit in Hinblick auf Prozess und Inhalt erlauben und auch als Dokumentationsplattform in Hinblick auf spätere Recherchen dienen. Meistens lohnt es sich aufgrund der nahtlosen Integration das Bug Tracking im Kontext einer bereits vorhandenen Plattform für das Requirements Engineering oder Source Code Hosting wie z.B. dem Issue Managment von Github zu etablieren.

Die fünf Schritte erfolgreichen Bug-Managements

Gehen wir endlich in die Praxis – wie gelingt es, den Schaden von Bugs zu begrenzen, sie angemessen zu priorisieren und aus ihnen zu lernen? Natürlich ist die konkrete Umsetzung stets individuell vom Produkt und Unternehmen abhängig und daher möchte ich die nun vorgestellte Herangehensweise lediglich als Anregung verstanden wissen, um sich mit einem für das jeweilige Unternehmen passenden Prozess auseinander zu setzen.

Ich gliedere den Prozess grundsätzlich in fünf Schritte: die Erfassung über diverse Kanäle, die Analyse, die Schadensbegrenzung, sowie die eigentliche Fehlerbehebung, und den wahrscheinlich wichtigsten Schritt, das Dazulernen. Schauen wir uns das im Detail an.

Bugs erfassen

Bevor ein Bug überhaupt erfasst werden kann, muss er dem Entwicklungsteam kommuniziert werden. Dafür muss ein Kommunikationsweg festgelegt werden, der den Entdeckern von Bugs bekannt sein muss. Das kann beispielsweise eine E-Mail-Adresse, ein Formular oder eine Hotline sein. In jedem Fall ist die Zahl der verschiedenen Kanäle möglichst zu begrenzen und sofern es mehrere gibt, sollten diese alle in einer einzigen Sammelstelle – dem Bug Tracker – münden, ansonsten wird eine konsequente Bearbeitung in den Folgeprozessen nicht möglich sein.

Wichtige Grundlage für die Einschätzung und Behebung eines Fehlers, sind die Informationen, die ihn beschreiben. Daher ist bei der Erfassung darauf zu achten, dass möglichst detaillierte Informationen festgehalten werden. Alle Entwickler kennen den nervigen Umstand, wenn diese auf eine viel zu schlichte Aussage reduziert wurden und ein Fehler beispielsweise mit den Worten „Die Buchung funktioniert nicht“ beschrieben wird. Wie ist „funktioniert nicht“ zu verstehen? Erscheint eine Fehlermeldung? Kann die Seite nicht geöffnet werden? Wird die Buchung vielleicht sogar erfasst, aber es erscheint keine Erfolgsmeldung? Solche Informationsmängel führen zu unnötigen Kommunikationsschleifen und können im schlimmsten Fall auch eine falsche Einschätzung des Problems nach sich ziehen. Es ist daher darauf zu achten, dass bei Erfassung stets alle verfügbaren Informationen festgehalten werden, was typischerweise folgende Inhalte umfasst:

  • Eine Beschreibung des konkreten Anwendungsfall in einer „Wenn… dann…“-Formulierung
  • Prozessdaten also z. B. Daten, die in ein Formular eingegeben wurden, bevor der Fehler auftrat
  • Fehlermeldung im Original-Wortlaut und falls vorhanden der Fehlercode
  • Screenshots, welche die Beschreibung ergänzen
  • Informationen zur Laufzeitumgebung beispielsweise Browser, Betriebssystem, Modell des Endgeräts
  • Falls vorhanden Laufzeitinformationen wie z. B. Log-Dateien oder Stack-Traces

Bezogen auf das Fallbeispiel der nicht funktionierenden Buchungsfunktion könnte eine qualifizierte Meldung wie folgt aussehen:

Wenn versucht wird, ein Hotel-Zimmer zu buchen und mit einer Kreditkarte zu bezahlen, dann kann zwar die Buchung abgeschlossen werden, der Kunde erhält jedoch keine Buchungsbestätigung, weder per E-Mail noch in der App.

Feel-Good Hotel, 15. – 17.09.2020, 2 Personen, Junior Suite
Kreditkarte: Visa, Prepaid-Rate
Firefox Version 79, Android 10

Sicher, nicht immer wären alle diese Informationen für die weitere Bearbeitung notwendig. Das ist jedoch meistens in dieser frühen Phase noch nicht absehbar und daher empfiehlt es sich konsequenterweise immer möglichst alle Informationen zusammentragen. Und ja, besonders dann, wenn die Bugs von externen Kontakten beziehungsweise Kunden gemeldet werden, wird man nicht jeden so weit erziehen können, dass ausschließlich mustergültige Fehlermeldungen im Postfach landen. In diesen Fällen empfiehlt es sich in Rücksprache mit den für die Analyse zuständigen Kollegen einen schlanken Kommunikationsprozess zu etablieren mit dessen Hilfe bedarfsgerecht weitere Informationen abgefragt werden können.

Bevor wir uns der Analyse zuwenden, sei an dieser Stelle auf einen typischen Fehler im Umgang mit Bugs hingewiesen. Es ist verlockend, kleine Fehler, deren Lösung offensichtlich erscheint und mit wenigen Zeilen Code umgesetzt werden kann, auf dem „kurzen Dienstweg“ ohne Erfassung zu beheben. Im Grunde wäre das eine effiziente Herangehensweise, welche Ressourcen schont und augenscheinlich das Problem trotzdem aus der Welt schafft. Problematisch daran ist, dass sämtliche Folgeprozesse – im schlimmsten Fall sogar die Qualitätssicherung – unterlaufen werden. Eine quantitative Auswertung, die wichtige Erkenntnisse über die Wirksamkeit der Qualitätssicherung liefert, ist ebenfalls nicht möglich. Nicht auszuschließen ist auch, dass ein Bug, der zunächst unkompliziert erschien, sich im weiteren Verlauf doch als größeres Problem entpuppt, das anders priorisiert und in der Planung des Sprints sinnvoll hätte miteingeplant werden müssen. Aus diesen Gründen sollten grundsätzlich alle Bugs völlig unabhängig ihres Umfangs konsequent erfasst und auf gut gemeinte Abkürzungen verzichtet werden.

Analyse

Ein neu erfasster Bug reiht sich in eine Liste weiterer offener Bugs ein. Um zu entscheiden, welche Priorität er in dieser Liste einnimmt, muss sich jemand, der mit dem Produkt und der Technik vertraut ist, damit auseinandersetzen. Die Frage, wann das geschieht und ob der Bug von einer Person, z. B. dem Product Owner oder dem gesamten Team, einer Analyse unterzogen wird, ist abhängig von dem jeweiligen Produkt und kann daher nicht pauschal festgelegt werden.

Wichtig ist in jedem Fall, dass es schnell geht. Hinter jeder Meldung könnte ein Millionen-Problem stehen, das mit jeder Minute noch teurer wird. Außerdem steigern lange Reaktionszeiten die Frustration der Kunden. Wer diese Erfahrung öfters macht, wird sich zukünftig nicht mehr die Mühe für eine entsprechende Meldung machen, sondern sich eher nach einem Alternativprodukt umsehen. Die Analyse innerhalb weniger Stunden sollte üblich sein, bei offensichtlich dringenden Fällen wie einem Komplettausfall, natürlich sofort.

Im Verlauf der Analyse kann das Fehlerbild weiter eingegrenzt werden, was einerseits für die Priorisierung und andererseits – zumindest in größeren Unternehmen – auch zur Klärung der Zuständigkeit von Bedeutung ist. Bezogen auf das Beispiel der defekten Hotel-Buchung stellen sich jetzt Fragen wie: Tritt das Problem immer oder nur unregelmäßig auf? Kann es mit dem Zahlungsmittel in Zusammenhang stehen? Ist es in jedem Browser oder nur im Firefox nachvollziehbar? Antworten auf diese Frage sind entscheidend für die Priorität des Bugs, denn je häufiger ein Problem auftritt, desto größer ist der anzunehmende Schaden.

Schadensbegrenzung

Doch der Schaden lässt sich in vielen Fällen begrenzen und damit sollte, nachdem durch die Analyse jetzt schon einige Erkenntnisse vorliegen, sofort begonnen werden. In diesem Schritt unterscheiden sich viele Unternehmen stark voneinander, da das Vorgehen maßgeblich von der jeweiligen Fehlerkultur geprägt wird. Die Schadensbegrenzung beginnt damit, den Fehler offen intern und gegebenenfalls auch extern zu kommunizieren. Auch nach vielen Jahren in diesem Beruf fällt es mir immer noch dann besonders schwer über einen Fehler zu sprechen, wenn ich noch gar keine Lösung parat habe. Doch es ist notwendig, denn es macht für den genervten Kunden, der die Service-Hotline wählt, einen großen Unterschied, ob man ihm als Auskunft geben kann, dass das Problem bekannt und eine Lösung bereits in Arbeit ist oder er das Gespräch mit dem Gefühl beendet, am Anfang einer langwierigen Lösungssuche zu stehen. Völlig unnötigen Aufwand für Doppelt-Erfassungen von Bugs erspart es obendrein.

Im besten Fall sorgt die frühe Kommunikation nicht nur für nüchterne Information, sondern liefert auch gleich eine Alternative zur Umgehung des Problems. Wenn die Analyse der defekten Buchungsfunktion beispielsweise ergeben hat, dass der Fehler nur in Verbindung mit Kreditkarten-Zahlung auftritt, könnte der Service-Mitarbeiter dem Kunden anbieten die Buchung zu stornieren und unter Verwendung eines anderen Zahlungsmittels erneut zu buchen. Mit viel Freundlichkeit und etwas Service-Geschick lässt sich eine solche Erfahrung sogar in ein „Wow-Erlebnis“ wandeln, das die Kundenbindung festigt.

In manchen Fällen lässt sich der Schaden auch durch die Einleitung von Sofortmaßnahmen begrenzen. Wenn beispielsweise die Zahlung per Kreditkarte vorübergehend deaktiviert wird, verhindert das weitere frustrierte Kunden und erspart dem Unternehmen Supportaufwand. Sofortmaßnahmen sind in den meisten Fällen eine Abwägungsfrage und erfordern daher eine schnelle Abstimmung im Unternehmen.

Fehlerbehebung

Man könnte jetzt annehmen, zur eigentlichen Fehlerbehebung gibt es nicht viel zu sagen. Der generelle Ablauf ist im Grunde immer gleich: Ein qualifizierter Entwickler ermittelt unter Einsatz verschiedener Hilfsmittel wie einem Debugger oder den Entwickler-Tools des Browsers die Ursache, recherchiert möglicherweise etwas zur Lösung, behebt das Problem durch Änderung des Quellcodes, ergänzt die Tests und übergibt die Sache dem weiteren Qualitätssicherungsprozess. Soweit, so gut – doch genügt das? Nicht ganz, denn um aus diesem Fehler zu lernen, braucht es unbedingt auch eine aussagekräftige Dokumentation. Der Aufbau dieser Dokumentation ist in der Regel identisch und besteht aus drei einfachen Teilen: Ursache, Auswirkung und Lösung. Und warum gerade diese drei?

Die Dokumentation der Ursache fördert die intensive Auseinandersetzung des Entwicklers mit dem eigentlichen Problem. Es kann nämlich, bedingt durch Umstände des Alltags, schnell passieren, dass Fehler nur oberflächlich betrachtet und behoben werden. So könnte man viele Probleme im Datenmodell auch mit einer Änderung des Quellcodes beheben, was oft weniger aufwendig, aber im Sinne der Softwarequalität falsch wäre. Die Tatsache, dass die Ursache dokumentiert werden muss, spornt zu einer einwandfreien Ursachenforschung an und hilft gleichzeitig bei der Nachvollziehbarkeit der Umstände im späteren Verlauf.

Die konkreten Auswirkungen eines Fehlers, die spätestens nach dessen Behebung auf dem Tisch liegen sollten, sind wichtig für die Nacharbeiten, die mit dem Bug in Zusammenhang stehen. Die Auswirkungen können sehr unterschiedlich sein, wie beispielsweise Fehler in Bestandsdaten, eine Beeinträchtigung der Benutzererfahrung im Design, eine Fehlermeldung oder schlicht eine defekte Funktion. Auch bei der Dokumentation der Auswirkungen werden Entwickler nochmals gefordert, sich intensiv mit dem Fehler auseinanderzusetzen, wodurch das Übersehen von Folgefehlern und unerkannter Nebeneffekte vermieden wird.

Und schlussendlich endet die Dokumentation mit dem Festhalten der eigentlichen Lösung. Das ist zum einen wichtig für den Fall, dass der Fehler nochmals auftreten sollte und zum anderen ist auch diese Information wieder wichtig, um später aus einem Fehler zu lernen. Das trifft insbesondere dann zu, wenn bei der Lösung auf unkonventionelle Mittel zurückgegriffen wurde, weil die Priorität hoch war, aber keine adäquate Lösung zur Verfügung stand. Ein typischer Fall sind Performance-Beeinträchtigungen, für deren Behebung eigentlich die Business-Logik geändert werden müsste, aber zur schnellen Behebung auf eine Abfrage direkt in der Datenbank zurückgegriffen wurde. Eine solche Lösung kann in besonders dringlichen Fällen notwendig sein, sollte aber auf keinen Fall so dauerhaft bestehen bleiben und daher dokumentiert und nachverfolgt werden.

Für jeden Bug diese drei Punkte zu dokumentieren, klingt zunächst vielleicht etwas aufwendig. Letztendlich genügen jedoch ein bis zwei Sätze pro Aspekt, die – sofern sie konsequent dokumentiert werden – zu einer deutlich besseren Qualität der Fehlerbehebung und langfristig zu einem wertvollen Datenfundus führen, mit dem ein neues Level der Softwarequalität erreicht werden kann. Genug geredet – schauen wir uns endlich an, wie die Dokumentation in unserem Beispiel, der defekten Buchungsfunktion aussehen könnte:

Ursache: Durch eine Anpassung der Gültigkeitsvalidierung von Kreditkarten, die mit Version 16.1 released wurde, entstand ein Fehler bei der Erkennung ungültiger Kreditkarten. Verursacht wurde dies durch ein Missgeschick bei der Abfrage des Kreditkarteninstituts in der Klasse CreditCardValidation, Methode GetType().
Auswirkung: Alle Buchungen, die seit dem Release am 16.06. mit einer Kreditkarte der Institute Visa oder MasterCard getätigt wurden, könnten eine ungültige Kreditkartennummer enthalten.
Lösung: Die fehlerhafte if-Abfrage wurde angepasst und mittels Unit-Test gesichert. Eine Liste aller betroffenen Buchungen wurde an die zuständige Abteilung weitergeleitet.

Besser werden

Und wenn der zugegebenermaßen manchmal etwas anstrengende Prozess der Fehlerbehebung durschritten ist, ist es an der Zeit, sich endlich dem „Besser werden“ zu widmen, um in Zukunft seltener diesen Weg gehen zu müssen. Es liegt besonders in der Verantwortung von Führungskräften, Scrum-Mastern oder anderen Personen mit Leader-Eigenschaften, den Prozess des Besser-werdens zu steuern und zu fördern. Nur dadurch kann das wertvolle Potential, das Fehler innehaben, freigesetzt werden. Nicht nach jedem Bug, aber in jedem Fall regelmäßig sollte sich ein Team mit den gemachten Fehlern auseinandersetzen. In Scrum-Teams gibt es mit der Retrospektive dafür einen regelmäßigen Termin, der sich geradewegs als Bühne anbietet. Und wie schon erwähnt: Es ist für Menschen nicht immer einfach über Fehler zu sprechen und daher ist unbedingt darauf zu achten, dass ich solchen Meetings eine konstruktive, freundliche Stimmung vorherrscht und alle Teilnehmer nur ein Ziel haben: Gemeinsam besser werden. Schuldsucherei, Pauschalisierungen und destruktive Kommentare dürfen hier keinen Platz haben.

Ziel der Besprechung sind Bewusstmachung und Verbesserungsmaßnahmen zu finden. Mit der Bewusstmachung werden zunächst einmal alle Mitglieder über den Fehler in Kenntnis gesetzt. Hier leistet, die Dokumentation, die im Rahmen der Fehlerbehebung erarbeitet wurde, ihren Dienst, indem sie Ursache, Auswirkung und Lösung des Fehlers vor Augen führt. Das hilft Entwicklern – auch wenn sie möglicherweise gar nicht an der Entstehung des Fehlers beteiligt waren – den Blick für alltägliches Fehlerpotential in ihrer Entwicklungsarbeit zu schärfen. Durch die transparente Darstellung, wird ihnen immer wieder vor Augen geführt, wie tückisch, schnell und unvorhersehbar sich Fehler einschleichen können. Denn eines haben alle Fehler gemeinsam: niemand hat damit gerechnet, sonst hätte man sie ja schließlich verhindert. Das beste Gegenmittel für diesen Umstand ist Erfahrung und genau die, lässt sich durch die regelmäßige Bewusstmachung verbessern.

Im zweiten Schritt diskutiert das Team mögliche Verbesserungsmaßnahmen für die Zukunft. Nicht in jedem Fall ist das notwendig, doch oft kann man mit einer neuen Konvention oder einem internen Guide gegenwirken und eine Wiederholung vermeiden. Hätte der Fehler durch einen Unit-Test verhindert werden können? Dann sollte die Konvention hinsichtlich Unit-Tests geprüft werden? Handelte es sich um ein Performance-Problem, das aus Unwissenheit über das Verhalten der verwendeten Bibliothek entstanden ist? Dann wäre es an der Zeit darüber einen kleinen internen Guide zu verfassen.

Zuletzt sei darauf hingewiesen, dass es auch Fehler gibt, die nicht im Team, sondern direkt mit dem oder den Urhebern besprochen werden sollten. Das ist insbesondere dann der Fall, wenn es sich um Verstöße gegen bereits bestehende Konventionen oder Prozesse handelt. In solchen Fällen ist eine Diskussion im Team nicht zielführend, stattdessen muss auf die zukünftige Einhaltung hingewirkt werden.

Fazit und Ausblick

Eine fehlerfreundliche Kultur ist Grundvoraussetzung für einen erfolgreichen Umgang mit Bugs. Mit einem passgenauen Prozess, der Team, Kunden und Produkt gerecht wird, können Fehler effizient behoben und in regelmäßig stattfindenden Besprechungen reflektiert werden. Das sorgt für ein größeres Risikobewusstsein im Team und mehr Erfahrung, was grundsätzlich die beste Vorsorge gegen Fehler ist. Doch nicht alle Unternehmen pflegen einen solchen Umgang mit Bugs, was über längere Zeit zu teils verheerenden Situationen führt, in denen Entwicklungsteams viel zu viel Zeit für die Behebung von Fehlern investieren. Wie der Ausweg aus solchen Situationen gelingt, sehen wir uns im zweiten Teil dieses Artikels an.

Geschrieben von
Christian Kranert

Christian Kranert begann seine Laufbahn im Jahr 2006 mit der Entwicklung von ERP-Systemen für die Automobilindustrie, wechselte nach einigen Jahren in die Online-Branche und entwickelt seit 2015 mit seinem Team die Cloud-Software studiolution.com. Schon immer begleitete ihn dabei die Begeisterung Probleme mit Software lösen zu können. Über die Zeit entwickelte er sich zum Softwarequalitäts-Enthusiast und Fan agiler Entwicklungsmethoden.

Kommentare

Hinterlasse einen Kommentar

avatar
4000
  Subscribe  
Benachrichtige mich zu: