Wenn Unternehmen beschließen alte Software zu modernisieren, dann sind das oft Windows-Programme, die nun in die Unix-Welt kommen sollen - oder umgekehrt. Ein weiterer Grund ist, dass niemand im Unternehmen mehr die Programmiersprachen, Bibliotheken oder Entwicklungswerkzeuge benutzt: Die alten Entwickler sind inzwischen pensioniert, ins Management befördert oder haben das Unternehmen verlassen. Und schließlich hat natürlich eine Softwarelösung mit dem Look & Feel der 1980er Jahre weniger Marktchancen - egal wie aktuell ihre zugrunde liegenden Algorithmen auch sind.
Solche Migrationen bergen einige Risiken. Um sie kontrollierbar zu halten, bietet sich ein von Prinzipien geleiteter Prozess an, denn Softwareprojekte dieser Art nehmen schnell Größenordnungen jenseits von 20 Mannmonaten an. Eines der Basisprinzipien einer solchen Migration ist, nie an zu vielen Schrauben gleichzeitig zu drehen. Der Austausch einer Bibliothek für grafische Oberflächen wie etwa Motif oder MFC durch Qt kommt einer Herztransplantation gleich, und dabei sollte es in diesem Projekt auch bleiben. Wer im Laufe eines solchen Migrationsprojekts neue Features implementiert, läuft Gefahr, das Projektrisiko untragbar zu erhöhen.
Keine Fehler beheben
Auftraggeber überrascht gelegentlich, dass auch das Beheben von Fehlern Risiken für das Projekt birgt. Offenbar war der Fehler ja für die Benutzer der Originalversion erträglich, ansonsten hätten sie längst darauf bestanden, ihn korrigieren zu lassen. Also ertragen sie ihn auch noch ein paar weitere Monate, bis die Migration abgeschlossen ist. Dann erst sollten die Entwickler wieder an das Beheben von Fehlern denken und neue Features implementieren.
Das heißt natürlich nicht, dass Software-Restaurateure blind für die Fehler und Mängel der Software durch ihre Migration schreiten. Aber statt der Versuchung zu widerstehen, gleich einzugreifen, sollten sie alle festgestellten Mängel und Verbesserungsvorschläge systematisch festhalten und dann nach abgeschlossener Migration angehen. Ein Issue Tracking System wie Bugzilla oder Jira erweist sich dabei als nützlicher Helfer.
Betroffene Softwarebesitzer fragen oft, ob sie an dieser Stelle moderne UI-Konzepte wie Werkzeugleisten, Baumansichten oder Tooltips einführen sollten. Alte Bibliotheken wie Motif oder MFC kennen einige dieser UI-Elemente nicht. Wer der Versuchung widersteht, seine Oberfläche gleich mit diesen Neuerungen aufzupolieren, bleibt auf der sicheren Seite, andernfalls erhöht sich das Projektrisiko auf ungebührliche Weise. Benutzer sind jahrzehntelang ohne farbige Icons in den Menüs ausgekommen und können es noch ein paar weitere Monate. Natürlich gilt auch hier: Alle Ideen, die das UI verbessern, sollten die Projektbeteiligten gewissenhaft festhalten, hinterher systematisch durchsehen und gegebenenfalls umsetzen.
Die oft gehegte Hoffnung, einen möglichst großen Teil der Entwicklungsarbeit durch automatische Migrationswerkzeuge zu ersetzen, hat sich als trügerisch erwiesen. Jedes Werkzeug dieser Art deckt lediglich etwa 60 Prozent aller vorkommenden Fälle ab, wenn die Entwickler nicht prohibitiv viel Aufwand in die Entwicklung des Werkzeugs stecken. Die Fehlersuche in den verbleibenden 40 Prozent dauert erheblich länger, als wenn qualifizierte Entwickler manuell, systematisch und sequenziell - kurz und mit einem wohldefinierten Prozess - die Migration vornehmen.
Um eine Softwaresanierung trotz des aus dieser Erkenntnis folgenden höheren Personalaufwands sinnvoll durchzuführen, hilft es, den Entwicklern möglichst produktivitätssteigernde Hilfen an die Hand zu geben. Oft machen ganz einfache Mittel den Unterschied: Etwa das Wechseln auf Knopfdruck zwischen der alten Version, der Baseline, und der neuen, an der sie gerade arbeiten. Editormakros (siehe Kasten "Arbeiten mit Makros") schreiben oft vorkommende Konstrukte, etwa eine »XtAddCallback()«-Methode in Motif, durch einen »QObject::connect()«-Aufruf in Qt um.
|
Richtig verfasst, funktionieren gute Makros sowohl mit reinem Qt als auch mit KDE. Sie wissen gar nichts davon, ob die Anwendung etwa ein »QLineEdit« oder eine Subklasse davon einsetzt. Sie leiten lediglich aus dem Namen der Motif-Ressource ab, um was für ein Widget es sich handelt. Können sie das nicht selbst entscheiden, fragen sie nach. Will etwa ein Programmierer einen Knopf von Motif nach Qt wandeln, wandelt er eine Zeile der Form
XtVaGetValues(RecordBtn,XmNset,&on,NULL);
durch sein Makro in
on = ui->RecordBtn->isChecked();
um. Ein Motif-Eingabefeld
XmTextGetString(_nameText);
wandelt entsprechend das Makro in den Code
ui->_nameText->text();
um. In allen Fällen ist »ui« ein Zeiger auf eine Instanz einer von Qt Designer generierten Klasse. Die mit einem Unterstrich beginnenden Variablen überträgt das Makro. Bei einem Listenelement ist Motifs API mächtiger, aus
XmListSelectItem(_lstAvailableRibs,
xmstr, true);
werden diese drei Anweisungen:
QList<QListWidgetItem*> items =
ui->_lstAvailableRibs->findItems(
xmstr, Qt::MatchExactly);
Q_ASSERT(!items.isEmpty());
items[0]->setSelected(true);
Das Beispiel ist gleichzeitig eine Warnung, den erzeugten Code intensiv zu kontrollieren und zu testen, weil hier die Semantik von Motif- und Qt-Listwidgets weit auseinandergeht.
Makros sollen nie automatisch oder in einer Schleife ablaufen, sondern einzeln vom Programmierer aufgerufen werden. Sobald er in einem Codeschnipsel ein Muster erkennt, wendet er dort das Makro an und prüft unmittelbar, ob die Konvertierung korrekt war.
|
Erfahrene Entwickler lassen den Code nicht automatisch konvertieren, sondern führen die Makros unter ihren Augen aus. Sie analysieren den Code Zeile für Zeile und entscheiden, welchen Code sie übernehmen und welchen nicht. Die Makros dienen nur dazu, immer wiederkehrende Tastenkombinationen durch einen einzigen Tastendruck zu ersetzen. Welche Entwicklungsumgebung diese Hilfsmittel bereitstellt, ist weitgehend Geschmackssache. Ich nutze meist Xemacs-Makros, aber es sprechen auch keine technischen Gründe dagegen, sie in Eclipse, Qt Creator oder Kdevelop zu implementieren.
Gute Werkzeuge
Es hat sich übrigens anhand von Datenerhebungen herausgestellt, dass tiefe Kenntnisse der Entwickler über die Ausgangsplattform eher zweitrangig sind: Was einzelne Aufrufe oder Konstrukte bedeuten, lesen sie gegebenenfalls in der Dokumentation nach. Wer den Quellcode studiert, parallel dazu die Anwendung ausführt und ihr Verhalten beobachtet, erschließt sich schnell die Bedeutung eines unbekannten Funktionsaufrufs.
Als wichtiger erweist sich die Kenntnis der neuen Umgebung. Aber auch diese Fertigkeit tritt hinter das Wissen um Migrationstechniken und -prozesse der Zielumgebung zurück. Die Produktivität und die Codequalität unterscheiden sich weniger zwischen einem Entwickler ohne Qt-Kenntnisse und einem Entwickler mit Qt-Kenntnissen als zwischen einem Entwickler mit Qt-Kenntnissen, aber ohne Migrationserfahrung mit Qt als Zielplattform, und einem Entwickler mit Qt-Kenntnissen, der eben diese Migrationserfahrung besitzt. Der Ausspruch "It\'s not a tools problem!" fasst zusammen: Das Wissen um den Prozess ist wichtiger als das Wissen um das Werkzeug.
Ein weitere zentrale Frage einer jeden Migration ist die, wie man mit dem alten Code umgeht. Hier hat der Entwickler drei Strategien zur Auswahl: Er kann völlig von vorn anfangen und passende Schnipsel aus dem alten Code heraussuchen, die er in der neuen Codebasis weiterverwenden möchte. Zudem hat er die Option, mit Brückentechnologien zu arbeiten, die den Einsatz mehrerer, eigentlich konkurrierender Ansätze ermöglichen. Solche Integrationen existieren beispielsweise für Motif und Qt [1] sowie für MFC und Qt [2]. Und drittens hat er die Wahl, den alten Code zunächst komplett zu übernehmen und ihn Stück für Stück zu migrieren, indem er unpassende Codestücke entfernt und durch neue ersetzt.
Die erste Technik kommt einem völligen Neuschreiben sehr nahe, einer Strategie, vor der Joel Spolsky in seiner Kolumne "Joel on Software" eindringlich warnt. Als Beispiel führt er an, dass Netscape seinerzeit durch Neuschreiben des Navigators viel Zeit verlor und damit gewaltige Marktanteile an den Internet Explorer abgab [3]. Diese Technik ist zudem fehleranfällig, denn Entwickler werfen damit auch solchen Code weg, der eigentlich auch in der neuen Umgebung noch verwendbar wäre - etwa weil er gar nichts mit der Oberfläche zu tun hat und von vornherein auch auf dem neuen Zielsystem liefe.