logo
falkoriemenschneider.de
Software: Projektmanagement, Architektur, Methoden.
home
blog

04. nov
04. okt
20. sep
25. aug
08. jun
30. mai
03. mai
02. feb
06. jan

2008

Weblog Archiv 2009

Theda, 4.11.2009

Am 30.10.2009 ist unsere Tochter Theda Johanna Riemenschneider um 21:04 Uhr zur Welt gekommen. Sie wog 3535g und ist 55cm lang. Wer Kinder hat und bei einer Geburt dabei war, dem brauche ich nichts zu sagen. Es ist von den Erlebnissen, die wir in unserer Gesellschaft so haben, das wohl wichtigste und um Längen beeindruckendste.

Meine Frau und ich sind kurz vor der Geburt in dem Bonner Gemeinschaftskrankenhaus St. Elisabeth angekommen, keine zwei Stunden später war unsere Tochter da. Das Familienzimmer, das wir auf der Neugeborenenstation bezogen haben, hat uns in den ersten Tagen sehr viel Sicherheit gegeben. Alles was man braucht inkl. medizinischer Betreuung, sehr freundlichem Rat durch die Schwestern und Anmeldungsservice fürs Standesamt ist dort zusammen.

Betreut wurden wir vorher, während der Geburt und jetzt durch den Bonner Hebammenladen, die mit dem Krankenhaus zusammenarbeiten. Sowohl bei den Hebammen als auch im Krankenhaus haben wir uns sehr wohl gefühlt! Jetzt genießen wir die neue Zeit zu dritt...


Oostende, 4.10.2009

Da die Geburt unserer Tochter für Ende dieses Monats vorhergesagt ist, wollten wir die letzten Tage zu zweit noch mal für einen Ausflug nach Belgien nutzen. Diesmal hatte meine Frau Judith Oostende angepeilt, das ehrwürdige Seebad an Flanderns Küste.

Nach einem Stopp in Antwerpen zwecks Einkauf bei Stephan Schneider fanden wir Oostende in dem typisch belgischen Flair vor. Die Innenstadt war mit Fahrgeschäften förmlich ausgestopft und die Küste in gewohnter Manier verbaut. Was uns jedoch nicht davon abhielt, zwei noch sehr erholsame Tage dort zu verbringen. Selbst ein Minigolfplatz tauchte in De Haan aan Zee unverhofft auf, übrigens einer der wenigen Orte, an dem die Bauten am Strand weniger als fünf Stockwerke haben...


Performance Tipps zu Web Services, 20.9.2009

Kommunikation über SOAP ist große Mode geworden, nicht zuletzt, da jede bedeutende Technologieplattform einen mehr oder minder ausgereiften SOAP-Stack bereithält. Nun ist SOAP fix auf XML gebunden, besser gesagt: auf das XML Infoset. Und auch REST, mit dem wegen seines einfachen Grundprinzips so mancher liebäugelt, kommt praktisch nicht ohne XML aus, obwohl beliebige Formate zulässig sind.

Das Verarbeiten von XML ist teuer, Remote Kommunikation ist sowieso teuer, aber wir nehmen das in Kauf. Es ist für betriebliche Informationssysteme üblich, sparsamen Umgang mit Rechenzeit und Speicher zugunsten von Interoperabilität, Portabilität und Wartbarkeit entweder zu opfern oder mit mehr "Blech" zu ersetzen. Auch wenn Techniker gerne das Gesicht verziehen, angesichts anhaltend hoher Personalkosten und stetig fallender Preise pro Taktzyklus und Speichereinheit macht das Sinn.

Manch ein Projektteam treibt das sorglose Spiel aber zu weit. Es entsteht Software, von der maßgeblich der Eindruck bleibt, dass sie nicht lasttauglich oder zu langsam ist und schlecht skaliert. Die ersten beiden Fragen, die ich stelle, wenn ich mir eine Meinung in einem solchen Fall bilden will, sind:

  • Wie sieht das Kommunikationsverhalten zwischen den Teilsystemen aus?
  • Wie sehen die Zugriffe auf Datenbanken aus?

Zur ersten Frage treibt mich immer das folgende Bild, das die Aufrufzeiten für verschiedene Technologien vergleicht:

Vergleich

Man erkennt, dass ein Nachrichtenversand über das Netzwerk verglichen mit lokalen Methodenaufrufen extrem teuer ist, und solange wir in einem System davon unnötig viele haben, brauchen wir nicht weiter nach Optimierungsmöglichkeiten zu suchen. Durch Reduktion der Anzahl können wir ein gewaltiges Potential ausschöpfen. Abgesehen davon, dass diese Maßnahme meist aufwändig ist, weil sie verlangt, die beteiligte Software stark zu verändern, ist die Idee trivial, und hat insbesondere mit Web Services und XML nicht viel zu tun. Wo können wir aber speziell bei Web Services ansetzen, wenn diese erste Maßnahme noch nicht reicht?

Hier meine zweitbesten Ideen:

HTTP 1.1 und Keep-Alive verwenden
Der TCP Verbindungsaufbau selbst kostet bei jedem Austausch Zeit, die enorm ansteigt, wenn z.B. SSL/TLS im Spiel ist, denn zur Aushandlung des symmetrischen Sitzungsschlüssels ist asymmetrische Kryptographie erforderlich. Durch Keep-Alive, das bei HTTP 1.1 default ist, kann man die Häufigkeit der Aushandlung reduzieren.

Schemavalidierung in Produktion abschalten
Während Integrationsstests ist Schemavalidierung hilfreich, um die Übereinstimmung der versendeten Nachrichten mit den Angaben aus den Schnittstellenspezifikation festzustellen. Ist ein System aber getestet, so bringt die Validierung auf den meisten Kommunikationsstrecken keinen Wert mehr. Da sie teuer ist, kann man sie auch abschalten.

Kompression bei Kapazitätsproblemen
Ist Netzwerkkapazität ein Flaschenhals, dann können sich die beteiligten Teilsysteme auf GZip Kompression und/oder Fast Infoset einigen, was sich vor allem bei großen Nachrichten deutlich auswirkt. Die Kompression benötigt natürlich mehr Rechenzeit, das muss man dann abwägen.

Teildekodierung von Nachrichten
Gerade bei Diensten, die im wesentlichen als Router fungieren, ist eine vollständige Deserialisierung großer XML Nachrichten nur teurer Luxus. Um inhaltsbasiert Entscheidungen zu treffen, reichen XPath Anfragen auf der nackten XML Nachricht.

MTOM für umfangreiche Binärdaten
Binärdaten in Base64 sind zwar in XML zulässig, aber bei Mengen über 100K eine ziemliche Belastung für XML Parser. MTOM verwendet das MIME Multipart Verfahren, um die Binärdaten aus XML auszulagern, ohne dass die Anwendung diese Zerlegung merkt.

Einsatz von WS-Security überdenken
Die Signaturberechnung und Verschlüsselung bei WS-Security verwendet asymmetrische Verfahren, die rechenintensiv sind. Gerade Verschlüsselung kann man auch durch SSL/TLS erreichen. Oder man richtet durch Firewalls oder VPN Vertrauenszonen ein, in denen keine Sicherheitsmaßnahmen auf dem Application Layer mehr erforderlich sind.

Einsatz von Reliable Messaging überdenken
Fragt man den unbedarften Anwender, dann darf natürlich keine einzige Nachricht verloren gehen, die Kommunikation muss jederzeit vollständig zuverlässig sein. Folgt man diesem hehren Ziel, dann zwingt das zum Einsatz von Messaging Lösungen, die jede Nachricht transaktionsgesichert auf einen Plattenspeicher schreiben. Das ist zwar sicher, aber eben auch teuer. Was kostet es tatsächlich, wenn bestimmte Nachrichten verloren gehen?

Ganz gleich, was wir am Ende umsetzen, es ist entscheidend, Performanceprobleme früh zu entdecken, denn gerade eine Restrukturierung von Teilsystemen, so dass weniger Aufrufe stattfinden, will man nicht erst nach einem Integrationstest beginnen. Frühes Entdecken gelingt, wenn man

  • so früh wie möglich zwei oder mehr Teilsysteme auf eigener Hardware unter Last bringt und den Durchsatz beobachtet (z.B. mit JMeter), und dies bzgl. Mengengerüste und Anforderungen an die Antwortzeiten bewertet,
  • Messpunkte definiert und Werte in Monitoren sammelt und ausgibt, um herauszufinden, wo die Zeit verbraucht wird,
  • im Zweifel verschiedene Optimierungsideen ausprobiert, um die Wirksamkeit einschätzen zu können. Da lernt man dann, welchen Spielraum man im Falle eines Falles noch hat.
Auch hier also gilt wie an so vielen Ecken in der Softwareentwicklung: Es braucht früh nur ein paar Tage, um sich späte Monate mühsamer Strafarbeit zu ersparen.


Verteilung auf Teufel komm' raus?, 25.8.2009

Die Idee ist verführerisch: ein Service, einmal implementiert und in Betrieb genommen, bleibt als kleiner, autonomer Funktionsbaustein der Unternehmens-IT stets zur Verfügung. Er ist durch WSDL (fast) selbstbeschreibend und man kann ihn aus jedem Consumer heraus ansprechen. Künftige Anwendungen bedienen sich dieses Dienstes einfach, und schon verkürzt sich der Zeitraum, mit dem ein Unternehmen seine IT an geänderte Bedürfnisse anpassen kann.

Nicht wenige Anwendungen werden heute mit Begeisterung nach SOA Prinzipien gebaut. Es findet eine Zerlegung in funktionale Komponenten statt, und -- schwupps -- jede Komponente ist ein eigenständiger Service mit einem entsprechenden Kontrakt. Dieses Vorgehen führt zu hochgradig verteilten Systemen. Doch Verteilung erzeugt immer Mehrkosten in Form von Komplexität, Laufzeiteinbußen und wahrscheinlich zusätzlichen Betriebskosten, z.B. durch erforderliche Sicherheitsmaßnahmen, aufwändigere Installation, zusätzliches Monitoring und Application Management (vgl. hierzu Eight Fallacies und Eight Fallacies explained).

Hohe Verteilung

Mit SOA scheinen diese realen Probleme wieder in den Hintergrund zu treten, doch sie sind weiterhin vorhanden. Es ist also gefährlich, ohne klare Kriterien, wie Komponenten verteilt werden sollten, nur nach funktionalen Gesichtspunkten zu entscheiden. Aber wie können solche Kriterien aussehen? Die mir bekannte Literatur zu Software Design und SOA gibt mir keinen ausreichenden Aufschluss, also unternehme ich hier mal einen eigenen Versuch:

Wenn wir in einem groben Design eines neuen Systems nach Prinzipien wie Separation of Concerns mehrere funktionale Komponenten identifiziert haben, wie können wir dann entscheiden, wie wir diese auf verschiedene Teilsysteme verteilen wollen?

Schritt 1:
Dem ersten Reflex folgend ist mein Ausgangspunkt: Verteile nicht! Ich habe in den letzten Jahren mehr Probleme durch unnötige Verteilung kommen als gehen sehen. Damit einer Komponente der ehrwürdige Status eines Dienstes im Sinne einer SOA verliehen wird, muss ihre Autonomie erst begründet werden.

Schritt 2:
Welche Gründe kann es trotzdem für die Eigenständigkeit einer Komponente zur Laufzeit geben?

  1. Die Bedeutung und Lebensdauer der Komponente weicht deutlich von der des zu bauenden Systems ab:
    • Sie existiert längst in Form einer bestehenden Applikation oder eines Service.
    • Sie soll unabhängig von der Entwicklung ausgetauscht oder eigenständig in völlig anderen Zusammenhängen eingesetzt werden.
    • Sie ist für sich so groß und/oder inhaltlich so eigenständig, dass sie als autonomes Teilsystem behandelt werden soll.
  2. Die Komponente ist in nicht-funktionaler Hinsicht besonders:
    • Sie wird an einem anderen Ort laufen (z.B. in einem mobilen oder entfernten Client).
    • Es gelten besondere Anforderungen hinsichtlich Durchsatz, Antwortzeitverhalten, Skalierbarkeit, Verfügbarkeit, Ausfallsicherheit oder Verwaltbarkeit.
    • Sie ist bzgl. technologischer Plattform oder verfügbarer Kommunikationstechnologien besonders.

Schritt 3:
Welche möglichen Umstände je Komponentenpaar sprechen wiederum gegen die Verteilung?

  1. Zwei Komponenten müssen miteinander sehr häufig Daten austauschen.
  2. Zwei Komponenten müssen transaktional zusammenwirken.
  3. Zwei Komponenten pflegen in einem fachlichen Prozess einen gemeinsamen Zustand.

a) und b) "ziehen" Komponenten also in verschiedene Teilsysteme, c) bis e) "drängen" sie wieder zusammen oder veranlassen uns zumindest, über Workarounds nachzudenken.

Ich plädiere also dafür, solche Komponenten in einem Teilsystem zusammenzufassen, denen Fachlichkeit, Lebenszyklus und nicht-funktionale Anforderungen gemeinsam sind. Ein solches funktional und nicht-funktional kohäsives Stück Software kann dann natürlich nach außen verschiedene Servicekontrakte anbieten, d.h. wie verschiedene Services wirken. Innerhalb dieses Teilsystem gelten weiterhin die üblichen Regeln modularen Software Designs.

Geringere Verteilung

Das Design eines verteilten Systems ist also schwieriger, da mehr Gesichtspunkte zu berücksichtigen sind als in nicht verteilten Systemen. Man sollte sich allein deshalb gut überlegen, ob SOA mit allen Konsequenzen das richtige Paradigma für eine neu zu bauende Anwendung ist. Wer glaubt, Verteilung würde die verabscheuungswürdigen "Monolithen" und "Silos" der "alten Welt" in Zukunft verhindern, der wird sich in einer noch unangenehmeren Softwarewelt wiederfinden, in der sich die Probleme unwartbaren Programmcodes mit den Schwierigkeiten verteilter Systeme verbündet haben.


Ötztal, 8.6.2009

Die Schwangerschaft meiner Frau hielt uns nicht von einer Woche Wandern im östereichischen Ötztal ab. Wir ließen es aber deutlich ruhiger angehen als in unserem letzten Tirolurlaub, zumal Anfang Juni noch nicht viele Bergbahnen im Sommerbetrieb sind. Statt also viele hundert Höhenmeter zu laufen, verlegten wir uns mehr auf ausgedehnte Spaziergänge und nutzten meist nachmittags die in Längenfeld gelegende Therme zum Schwimmen und Entspannen.


Zürich, 30.5.2009

Auf unserem Weg ins Ötztal haben wir uns für einen Besuch von Freunden und deren Wahlheimat Zürich entschieden. Die Stadt ist wirklich ein hübscher Flecken Erde, wobei die Sonne an diesem Tag sich mächtig ins Zeug legte, um auch die letzte Ecke zum Strahlen zu bringen.

Dank der Beachtung der nötigen Höflichkeit und Zurückhaltung gelang es unseren Freunden sogar, einen Tisch in der legendären Kronenhalle zu reservieren.


Antwerpen, 3.5.2009

In diesem Mai haben wir Judiths Onkel, der für uns im September 2008 ein sehr schönes Wanderwochenende in der Luxemburger Schweiz organisiert hatte, sowie seine Frau und meinen Schwiegervater nach Antwerpen eingeladen. Zwecks Übernachtung hatte meine Frau die Zimmer in Aan de Keizer BnB gebucht, um von dort aus ein kleines Touristenpflichtprogramm zu absolvieren: Plantin-Moretus-Museum, Rubenshuis, Onze-Lieve-Vrouwekathedraal und einige Verkaufshäuser für Mode sowie der Besuch guter Restaurants.


Meine erste selbstgebaute Sprache, 2.3.2009

Während der Feiertage des Jahreswechsels 08/09 wollte ich Xtext ausprobieren, denn aufgrund der Arbeiten unserer Kollegen in Kiel und Stuttgart sowie meiner Lektüre der Veröffentlichungen von Martin Fowler und Martin Ward hielt ich es für dringend erforderlich, mir selbst ein praktisches Gefühl für LOP, also den Bau von domänenspezifischen Sprachen zu verschaffen.

Wie aufwändig ist sowas? Welche "besonderen Fähigkeiten" werden verlangt? Und wie gut funktionieren die Werkzeuge dazu, allen voran natürlich oAW und Xtext?

Meine persönlichen Voraussetzungen halte ich für durchschnittlich. Sehr gute OO Programmierkenntnisse, aber kaum theoretisches Wissen zum Compilerbau. Praktischer Generatorbau auf Basis von oAW hatte ich auch erst im Rahmen eines itemis internen Powerworkshops besser kennengelernt, Xtext war mir jedoch völlig neu. Dafür kannte ich die von mir gewählte Domäne ganz gut.

Domäne: User Interface
Wer sich schonmal mit der Architektur von User Interfaces beschäftigt hat, wird wahrscheinlich schon über Fowlers PresentationModel gestolpert sein. Die Idee ist, kurzgesagt, Daten und Verhalten soweit wie möglich aus den View-Klassen herauszuziehen und in einer eigenen Modell-Klasse zu vereinen. Diese ist unabhängig von UI-Technologie und daher leichter zu warten und besser zu testen, als wenn alles zusammen an einem Ort codiert ist. Bildlich kann man sich das so vorstellen:

Idee PresentationModel

Durchaus Popularität findet dieses Rich Client Muster lustigerweise im Web Frontend Bereich, nämlich bei JavaServer Faces. Hier heißt das PresentationModel dann BackingBean, aber die Rollenverteilung bleibt wie bei Fowler beschrieben.

Die Anwendung dieses Musters führt dazu, dass man je fachlichem "Formular" zwei Klassen schreiben muss: die eine enthält Daten und Logik, die andere die Widgets und das Layout. Zusätzlich braucht's noch ein Stückchen konfigurierenden Code, der die Instanzen der beiden zusammenführt. Die folgenden Kästen zeigen das Model und die View.

Presentation Model:

public class EmployeeDetailsModel extends SingleViewPresentationModel {	
	public EmployeeDetailsModel (Employee employee) {
		super();
		this.employee = employee;
		initialize();
	}
	
	public void initialize () {
		registerField(new DefaultField(TITLE,
			String.class,
			ManagedField.GET,ManagedField.SET));
		registerField(new DefaultField(EMPLOYEE_FIRSTNAME,
			String.class,
			ManagedField.GET,ManagedField.SET));
		registerField(new DefaultField(EMPLOYEE_LASTNAME,
			String.class,
			ManagedField.GET,ManagedField.SET));
		registerField(new DefaultField(EMPLOYEE_POSITION,
			String.class,
			ManagedField.GET,ManagedField.SET));
		registerField(new DefaultField(OKBUTTONTEXT,
			String.class,
			ManagedField.GET,ManagedField.SET));
	}
	protected Employee employee;
	public Employee getEmployee () {
		return employee;
	}
	public void setEmployee (Employee employee) {
		this.employee = employee;	
	}
	protected String title;
	public String getTitle () {
		return title;	
	}
	public void setTitle (String title) {
		this.title = title;	
	}
	protected String okButtonText;
	public String getOkButtonText () {
		return okButtonText;	
	}
	public void setOkButtonText (String okButtonText) {
		this.okButtonText = okButtonText;	
	}

Java Swing View:

public class EmployeeDetailsView extends JDialog implements ManagedView {

	public EmployeeDetailsView () {
		super();
		initialize();
	}
	
	public void initialize () {
			JPanel contentPane = new JPanel();
			double sizes[][]={
				{10,80,layout.TableLayout.FILL,10},
				{10,22,22,22,layout.TableLayout.FILL,32,10}
			};
			contentPane.setLayout(new layout.TableLayout(sizes));	
			contentPane.add(getFirstnameLabel(), "1,1");
			contentPane.add(getEmployeeFirstnameTextfield(), "2,1");
			contentPane.add(getLastnameLabel(), "1,2");
			contentPane.add(getEmployeeLastnameTextfield(), "2,2");
			contentPane.add(getPositionLabel(), "1,3");
			contentPane.add(getEmployeePositionTextfield(), "2,3");
			contentPane.add(getPanel3Panel(), "1,5,2,5");
			setContentPane(contentPane);
			setTitle("");
			setMinimumSize(new Dimension(300,150));
			setModal(true);
			pack();
	}

	private ViewSupport viewSupport;
	public void setup(ViewSupport vs) {
		viewSupport = vs;
		viewSupport.register(EmployeeDetailsModel.EMPLOYEE_FIRSTNAME, 
       new TextfieldSynchronizer(getEmployeeFirstnameTextfield()));
		viewSupport.register(EmployeeDetailsModel.EMPLOYEE_LASTNAME, 
		   new TextfieldSynchronizer(getEmployeeLastnameTextfield()));
		viewSupport.register(EmployeeDetailsModel.EMPLOYEE_POSITION, 
		   new TextfieldSynchronizer(getEmployeePositionTextfield()));
		viewSupport.register(EmployeeDetailsModel.OKBUTTONTEXT, 
		   new ButtonTextSynchronizer(getOkButton()));
		viewSupport.register("ok", 
		   new SwingManagedAction(getOkButton(),""));
		viewSupport.register("cancel", 
		   new SwingManagedAction(getCancelButton(),"Cancel"));
		viewSupport.register(EmployeeDetailsModel.TITLE, 
		   new WindowTitleSynchronizer(this));
		viewSupport.registerComponents(this);
	}
	
	public Object getComponent () {
		return this;
	}
	
	private JLabel firstnameLabel;
	public JLabel getFirstnameLabel () {
		if (firstnameLabel == null) {
			firstnameLabel = new JLabel();
			firstnameLabel.setText("First name");
		}
		return firstnameLabel;	
	}

	private JTextField employeeFirstnameTextfield;
	public JTextField getEmployeeFirstnameTextfield () {
		if (employeeFirstnameTextfield == null) {
			employeeFirstnameTextfield = new JTextField();
			employeeFirstnameTextfield.setName("employeeFirstname");
		}
		return employeeFirstnameTextfield;	
	}

	. . .
	
}

Gedanklich bleiben die Klassen natürlich eine Einheit, und es wäre praktisch, ein Formular fachlich kompakt spezifieren zu können, so dass die architekturgemäße saubere Trennung in zwei Artefakte von einem Generator übernommen wird. Meine User Interface Language sollte z.B. folgende Ausdrücke erlauben, die ich für UIs immer noch als ausreichend intuitiv erachte, also z.B. für obiges Beispiel dann so:

form EmployeeDetails (
	binding(employee:de.itemis.hoa.cleanmvc.samples.bo.Employee)
	dialog(title:String size[300 150])
	layout(table columns[gap label fill gap] rows[gap input input input fill button gap])
	elements(
		c("1,1    ") label "First name"
		c("2,1    ") textfield binding(employee.firstname:String)
		c("1,2    ") label "Last name"
		c("2,2    ") textfield binding(employee.lastname:String)
		c("1,3    ") label "Position"
		c("2,3    ") textfield binding(employee.position:String)
		c("1,5,2,5") panel flow right ( 
			button binding(okButtonText:String) >ok 
			button "Cancel" >cancel 
		)
	)
)
Vom Wunsch zur Wirklichkeit
1. Hello World
Ich habe erstmal ohne lange zu überlegen den New Xtext Project Wizard verwendet. Und siehe da: das Eclipse Projekt Grundgerüst hatte ich nach gefühlten drei Mausklicks zusammen. Ich habe in der Xtext Grammatikdefinition zwei, drei Ausdrücke hingeschrieben, über Rechtsklick Generate Xtext Artifacts ausgelöst, der Grammatik entsprechend ein "Programm" formuliert und im Generator in einem Template zur Verifikation durch das Programm definierte Elemente ausgegeben. Nach Starten des oAW Workflow hatte ich meine "Hello World" Testausgabe. Das alles hat nicht länger als 30 Minuten gedauert.

2. Struktur
Obwohl meine Ideen von der UI-Language zu Beginn viel rudimentärer waren als im oben dargestellten Beispiel, war ich mir ziemlich sicher, dass ich mit dem einfachen oAW Setup hier nicht auskomme. Konkret fehlte mir eine Modell-zu-Modell (M2M) Transformation, die mir aus der sehr kompakten Form-Beschreibung den fachlichen Extrakt jeweils für die View und das PresentationModel in eigene Modellelemente überführt. Daher habe ich ein neues Ecore basiertes Metamodell namens MVC begonnen und eine eigenständige Extensions-Datei uil2mvc.ext angelegt, mit der aus dem textbasierten UIL Modell ein MVC Modell wird, das die Codegenerierung stark vereinfacht. Für den korrekten oAW Workflow musste ich dann etwas basteln, da ich mich zu wenig damit auskannte. Aber insgesamt hatte ich ca. zwei Stunden später tragfähige Strukturen aus Workflow, Modelltransformation, Generator unterstützenden Extensions und Xpand Templates geschaffen, die ich "nur noch" mit Leben zu füllen hatte.

3. Vorbild
Ich hatte die oben angedeutete Architektur aus View und PresentationModel schon etwa drei Jahre als Prototyp in der Schublade liegen, trotzdem musste ich diesen noch stärker strukturieren, um mir klar zu machen, wie im Detail der generierte Code aussehen soll. Also habe ich refaktorisiert bis das Ideal erreicht war, denn ich wollte auf keinen Fall Javacode erzeugen, der nicht nach sorgfältig handgeschriebenem aussieht.

4. Viele Iterationen
Der Rest war iterative Fleißarbeit: Wunschausdruck formulieren, UIL Grammatik anpassen, MVC Metamodell erweitern, Transformation schreiben und Xpand Templates erweitern. Nach und nach kamen die generierten Artefakte dem Vorbild nahe.

Fazit
Was als Experiment und Werkzeugtest gestartet war, zeigte schon nach zwei Tagen nützliche Ergebnisse. Und es macht Spaß, da die gute Eclipse Integration von Xtext und oAW flüssiges Arbeiten fördert. Der Einstieg war deutlich leichter als ich erwartet hatte, allerdings kannte ich die grundsätzliche Verwendung der oAW Bausteine bereits aus einem Workshop.

Magische Fähigkeiten musste ich nicht entwickeln, die Xtext Grammatik ist schnell gelernt, und sowohl Extend als auch Xpand erweisen sich bei intensiver Arbeit wie eine gut bestückte und sich selbst erweiternde Werkzeugkiste. Es gilt hier noch viel stärker als bei sonstiger Programmierung, dass man redundanzfrei codieren muss, denn nur dann ergibt sich eine elegante Sammlung von Extensions für die Transformation und Generierung.

Es hat sich als unumgänglich erwiesen, eine glasklare Architektur in einem sauberen Prototypen zu haben, sonst wird man zum Kapitän ohne Kompass. Zudem muss man bewusste Entscheidungen treffen, wo man Komplexität unterbringen will:

  • in der domänenspezifischen Sprache
  • im Framework, das den generischen Teil der Architektur in der Zielsprache enthält
  • in Extensions zur M2M Transformation
  • in Extensions zur Generatorunterstützung
  • in den Xpand Templates
  • im Generat
Die DSL ist natürlich der völlig falsche Ort, genau die sollte so intuitiv wie möglich sein, sie ist schließlich das neue Frontend, das wir Entwicklern anbieten wollen. Nach meiner jungen Erfahrung scheiden für mich außerdem die Xpand Templates und auch das Generat aus, beide sollten einfach sein. Stattdessen ziehe ich das Framework in der Zielsprache sowie die Extensions vor.

Abschliessend kann ich sagen, dass da ein fantastischer Werkzeugkasten verfügbar ist, um Projekten in Sachen Produktivität und Durchsetzung von Architekturen ohne Monate dauernde "Forschungsarbeit" zu einem echten Quantensprung zu verhelfen.


Code Reviews, 6.1.2009

Seit ich Software entwickle, hat sich nichts daran geändert, dass der Code das langlebigste maßgebliche Resultat von Softwareprojekten ist. Dokumentation ist wichtig, nutzt aber wenig, wenn die Codebasis faulig oder zerbrochen ist, denn die Mühe bei der Fehlersuche oder Erweiterung wächst und oder schrumpft mit dem Grad der Verrottung des Codes und nicht mit der Menge Prosa, mit dem das (traurige) Werk beschrieben ist.

Toolgestützte, statische Codeanalyse z.B. bzgl. Paketabhängigkeiten oder CCN hilft, manche Auffälligkeiten sehr schnell zu finden. Aber eben nicht alle. So Dinge wie chaotisches Logging mit mißverständlichen Meldungen, löchriges Exception-Handling, mögliche Race Conditions, subtile Formen von Codeduplikation, Algorithmen mit hoher Laufzeitkomplexität, Overengineering oder schlicht aussageloses Javadoc sind auch 2009 kaum durch Maschinen aufdeckbar. Und die genannten Probleme sind meist nicht im Handstreich zu beseitigen, sondern können eine teure Überarbeitung erfordern.

Es gibt leider manche Programmierer, die diese Fehler haarsträubend konsequent einbauen. Das sind meist diejenigen Kollegen, die noch lernen. Solche Programmierer sind nicht in Ihrem Team? Sicher? Na, dann führen Sie wohl öfter Code Reviews durch, denn die Erfahrung ist im Code ablesbar, so einfach ist das. Nie Reviews durchzuführen heißt dagegen, auf Glück zu bauen, was in der Softwareentwicklung selten gut geht.

In den Code Reviews, die ich durchführe, finde ich die folgenden Punkte besonders häufig:

  • Exception Handling ist uneinheitlich, Exceptions werden verschluckt, an zu vielen Stellen geloggt oder überall gefangen und weitergeworfen.
  • Klassen, die von mehreren Threads verwendet werden, taugen nicht dafür und erzeugen nicht reproduzierbare Fehler.
  • Ressourcen wie Connections oder Streams werden nicht sicher freigegeben.
  • Referenzen werden nicht aus statischen Instanzvariablen entfernt und erzeugen Speicherlecks.
  • Singleton Anti-Pattern wird verwendet, mindert so Testbarkeit und begünstigt Speicherlecks.
  • Synchronisierte Klassen (z.B. Vector, StringBuffer) werden unnötigerweise verwendet, und beeinträchtigen Performanz.
  • Paketabhängigkeiten weisen Zyklen auf oder geben die Architektur nicht wieder.
  • Code wird dupliziert.
  • Komplexität der Lösung geht weit über das Problem hinaus.
  • Imports, Klassen, Methoden und Variablen werden nicht verwendet und verstellen die Sicht auf das Notwendige.
  • Kommentare sind nicht hilfreich, missverständlich oder falsch.
  • JDK 5 Syntax (z.B. Enums, for mit Collections oder Generics) wird nicht verwendet.
  • Loglevel werden uneinheitlich verwendet, Meldungen sind wenig hilfreich.
  • Klassennamen enden auf ...Helper oder ...Util und enthalten meist verlegenheitsplatzierten Code.
Code Reviews durchführen ist einfach und verglichen mit anderen QS Maßnahmen billig. Man bekommt dadurch in einem Rutsch
  • ein Signal, dass Qualität zählt,
  • eine lernende Entwicklungsmannschaft,
  • ein Bild von der inneren Codequalität,
  • nach Beheben der gefundenen Punkte besseren Code und
  • Kostenersparnis, weil die so gefundenen Fehler nicht den Umweg über den Systemtest gehen müssen.
Zur Durchführung braucht man
  • einen oder mehrere Prüfer für wenige Personentage,
  • zu untersuchenden Code und
  • eine Tabelle oder Bugtracking Tool, um die Fundstellen zu protokollieren.
Der Prüfer sollte langjährige Erfahrung mit der Programmiersprache und einige Monate Erfahrung mit der Plattform (z.B. Java EE, Eclipse RCP u.ä.) haben. Geben Sie ihm den Auftrag, dass er die Probleme finden und den Entwicklern helfen soll, daraus zu lernen. Denn dann machen diese die Fehler hoffentlich kein zweites Mal.

Was ist mit der Prüfgrundlage, bspw. ein Code Styleguide, oder eine projektspezifische Beschreibung, worauf zu achten ist? Hm, schwierig. Schon sinnvoll, wenn es Eigenheiten des Projekts gibt, die sich niemand selbst herleiten kann, z.B. dass alle Instanzvariablen mit einem Unterstrich beginnen sollen, oder man immer Spaces statt Tabs zu verwenden hat. Aber dieser Styleguide kann nur ein Add-on auf den gesunden Verstand sein, sonst müsste man statt den Code zu prüfen erstmal eine dreibändige Abhandlung über das Programmierhandwerk verfassen. Vielmehr Sinn macht dagegen der Verweis auf einige Bücher, z.B.:

  • J.Bloch – Effective Java: A Programming Language Guide
  • M.Fowler et al – Refactoring
  • B.Goetz et al – Java Concurrency in Practice
  • R.C.Martin – Agile Software Development