Innovationen

Meine Ausbildung als Diplom-Biologe hilft mir dabei, vertrackte Probleme bei der Entwicklung unternehmenskritischer IT-Systeme auf innovative Weise zu lösen – natürlich immer in Kombination mit meinen praktischen Erfahrungen in zahlreichen Branchen, Technologien und Methoden. Beispiele für solche innovativen Lösungsideen, die sich auch tatsächlich bewährt haben, beschreibe ich in dieser Rubrik.

Innovation: Efficient Text Blocks for Web Forms

Summary

After the subject matter expert has approved the text change, the new content appears automagically on the online service page as fast as the CI/CD pipeline allows. No programmer or tester involved.

How this can be achieved is explained here with a simple proof of concept applying basic principles of software engineering. While this proof of concept is based on Confluence and Angular because of their widespread use, the underlying approach can easily be implemented on other platforms due to its simplicity.

When Does Text Matter?

Text is a major part of online services such as applying for a consumer loan or for a resident parking permit. Texts describe what the service offers, what data the customer has to provide, how privacy will be protected and so on.

It may require a lot of time and effort to keep texts up to date, for example with laws and regulations, new customer insight and changing market conditions.

In particular an efficient handling of text does matter for text-intensive online services that

  • are important to the service provider: Major failures can cause severe reputational damage or large financial losses.
  • require a lot of effort to develop and maintain: The team usually consists of more than 10 people.
  • are planned to have a long lifespan, typically many years.

The following software engineering principles help to master the texts of such services:

  1. Avoid manual redundancy
  2. Capture the logical essence
  3. Automate boring tasks
  4. Take advantage of synergies

The subsequent sections explain each of these points.

Avoiding Manual Redundancy

Redundancy is harmful, if it is created and maintained manually. In this case it is only a matter of time until things will become inconsistent. Inconsistency is the highway to hell for large amounts of text.

In order to avoid manual redundancy,

  • specify a text only in one place in a format that is complete and clear enough for using this text in many different contexts (see Capturing the Logical Essence)
  • use tools to propagate text changes to specification documents, web site code, PDF output, test parameters etc. (see Automate Boring Tasks)

Capturing the Logical Essence

A text block is not just a bunch of words, it has

  • a unique identifier that allows you to reference the text block
  • a sharp boundary that separates the text from the rest of the world
  • a logical structure consisting of headings, paragraphs, lists, links and so on.

For readers as well as for tools it is important to not hide the logical structure in a prose similar to Victorian novels but to make it explicit. This can be achieved by using a markup language like Markdown or HTML. Whatever format you choose, it should be supported by

  • editors suited for subject matter experts
  • tools that make it easy to read, write and transform this format
Confluence page defining a text block with Page Properties (subject matter expert, reviewer, status), description and content (embedded into an Excerpt macro).
Confluence page defining a text block.

In the proof of concept each text block is a Confluence page (see screen shot above), since pages offer many features for handling text:

  • Confluence provides an editor that makes it easy to specify the logical text structure.
  • Internally Confluence stores the pages as HTML, so that you can use many tools for further text processing.
  • Meta data enable you to manage text blocks easily and to configure a Confluence workflow for text review and approval. The above screen shot shows some of the meta data within the Page Properties macro: SME (subject matter expert), reviewer and status of the text block. In addition the page is labeled as “textblock”.
  • Confluence macros are available to seamlessly include a text block into specification documents (Excerpt macro and Excerpt Include macro). So you can specify a text in a single place and reuse it across the specification while Confluence handles the dependencies for you.
  • Confluence offers a REST API and a Python package to access the text blocks with tools for the automation of boring tasks.
Specification of an introductory page for "Application for a Foreign Freelancer Permit" in tabular form containing text blocks as content components embedded via Excerpt Include macro.
Text block included into the specification of the introductory page of an online service. The cursor is placed inside the text block so that the text block content (embedded via the Excerpt Include macro) is highlighted and surrounded by a blue border line.
Confluence traces the usage of text blocks: The section “Incoming Links” of the text block’s “Page Information” lists all pages that reference the text block (in this case via the Excerpt Include macro). You can find the “Page Information” in the “Advanced Details” section of the page menu.

Automating Boring Tasks

Using a specified text in implementation or testing requires schematic routine tasks: extract the text from the specification, reformat it, integrate it into source code or test scripts, review and test your results.

While such schematic routine tasks are boring and error-prone for humans, they are well suited for computers. You just need to apply your programming skills not only to the services you build but also to the development process itself.

Python Script

A simple Python script extracts text blocks from Confluence using Atlassian’s Confluence package. The script is generously commented in order to make it comprehensible for programmers who are not proficient in Python.

The extraction script provides text blocks in several formats:

  • HTML for Angular component templates
  • Plain text for Angular tool tips
  • Excel for review

A programmer who wants to use a text block in his Angular code just needs to insert a macro at the right place with the text block id as parameter: The rest is done automatically by the Angular ahead-of-time (AOT) compiler.

Using Text Blocks in Web Pages

The next sceen shot shows a demo implementation of an online service’s introductory page. How all the text content of this page is provided from Confluence by using extraction script and macro is described in the following sections.

Sceen shot of demo implementation.
Demo implementation of the introductory page of an online service using text blocks. The Login button’s tooltip is displayed since the mouse hovered over the Login button when the screen shot was taken (mouse not visible on screen shot).

The code below defines the introduction page of the “Application for a Foreign Freelancer Permit” (app.component.html). Some of the specified text blocks can be found here as app-tbl* elements representing Angular components with the text block content as component template.

<div>
   <div>
      <h1>{{title}}</h1>
   </div>

   <app-tbl001002></app-tbl001002>
   <app-tbl001></app-tbl001>
   <app-tbl001001></app-tbl001001>
   <app-tbl002></app-tbl002>
   <app-tbl003></app-tbl003>

   <div class = "button-row">
      <button mat-raised-button
            color = "primary"
            matTooltip = {{loginTooltip}}
            matTooltipClass = "matTooltipLinebreak"
            aria-label = "Login button with tooltip">
         Login
      </button>
      <button mat-raised-button
            matTooltip = {{registerTooltip}}
            matTooltipClass = "matTooltipLinebreak"
            aria-label = "Register button with tooltip">
         Register
      </button>
   </div>
</div>

Here as an example the text block component tbl001002.component.ts defining the element app-tbl001002:

import { Component, OnInit } from '@angular/core';
import { includeText } from '../includeText';

@Component({
  selector: 'app-tbl001002',
  //templateUrl: './tbl001002.component.html',
  template:	includeText("TBL001002"),
  styleUrls: ['./tbl001002.component.css']
})
export class Tbl001002Component implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}

The developer replaced the default template URL with a template filled by the includeText macro with the content of text block TBL001002.

IncludeText Macro

And here the definition of the includeText macro. All the work is done by the return statement:

// includeText.ts

import { texts } from '../../texts/texts'; // text definitions

/**
 * AOT macro for including reusable text blocks
 * @param	id 		Identifier of the text to include
 * @return	Text to include; unknown id returns 'undefined' value at build time
 */


export function includeText(id: string): string {
	return texts[id];
}

Actually at the time of writing, an AOT macro can consist only of a single statement – the return statement.

The includeText macro uses the dictionary “texts” defined in texts.ts. This file is generated by the above Python script:

// GENERATED CODE: DO NOT CHANGE MANUALLY!

export const texts: { [key: string]: string } =
{
"TBL001":
"<div tb_id=\"TBL001\" title=\"Privacy Statement\"><h2>We respect your privacy</h2><p>In this application form we will ask you only for personal data required to process your application. We strictly adhere to privacy laws and regulations. For further information, please see:</p></div>",

"TBL001001":
"<div tb_id=\"TBL001001\" title=\"Privacy Statement\"><ul><li><p>Specific <a href=\"https://www.to_be_defined.html\"><em>Privacy Statement for this Application</em></a></p></li></ul></div>",

"TBL001001_plain":
`- Specific Privacy Statement for this Application
`,

...

}

The includeText macro can be called wherever a string is allowed. In app.component.ts the text for login and register button are provided by calling the includeText macro with appropriate text block ids. Here the plain format of the text blocks is used instead of the html format:

/* Demo: reusable textblocks
*/

import { Component } from '@angular/core';
import { includeText } from './includeText';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Application for a Foreign Freelancer Permit';
  loginTooltip = includeText("TBL004_plain");
  registerTooltip = includeText("TBL005_plain");
}

Taking Advantage of Synergies

Development and maintenance of online services is a complex task requiring close cooperation across many disciplines. This means that a small change like introducing text blocks as a means of specification can have the potential to improve many other aspects of your project, if you take advantage of synergies.

Here some examples:

  • Optimize testing
    • On the one hand the text block tools have to be tested to ensure they work as intended.
    • One the other hand such unit tests make it obsolet to test whether the texts displayed on the web site have exactly the same content as the specified texts.
    • It remains to be tested that a specific text block is shown at the right place under the right condition.
      • Usually these requirements are much less subject to change than the text block content.
      • With unique ids it is no longer necessary for a text script to know the text content to find out where a text is located. Instead it can look for the text block’s id. The exctraction script generates the text block id into the div element that encloses the text block content.
        Example:
        <div tb_id=\”TBL001\” title=\”Privacy Statement\”>
  • Tracing text usage
    • Since each text has a unique id you can trace the usage of a text throughout the system (specification documents, source code, text scripts and so on).
    • This makes it easier to analyse the impact of text changes: In what contexts do you use a particular text block? Will the intended text change be compatible with all the contexts where the text is currently used?
  • Reduce time to market for text changes
    • Texts blocks can be used to reduce the time to market for text changes drastically (provided that you adapt the tool chain of your project accordingly):
      • After subject matter experts have edited a text block and reviewed / approved the change, the tool chain automatically extracts the new version from the specification and integrates it into the source code.
      • Meanwhile developers and testers can spend their time with tasks that are more challenging and not prone to automation.

What next?

If you would like to apply this approach to your own project:

  • You should agree with the stakeholders on a text management process: triggers for text changes, standards for a customer friendly language, reviews, approvals etc.
  • Concerning the extraction of text blocks from your Content Management System, you can take the above script as a starting point. However you should add some exception handling for robustness and implement unit tests to make sure it works as intended.

Perhaps this little example has whetted your appetite for more efficiency through software engineering in your project? In case you need some advice please feel free to contact me. I would also be glad to receive any feedback on the approach presented here.

Innovation: Efficient Text Blocks for Web Forms Read More »

Innovation: “Genom-Analyse” eines Fachkonzepts

Die Innovation

Verfahren aus der Genom-Analyse habe ich eingesetzt, um Redundanzen im komplexen Fachkonzept eines Großprojekts aufzuspüren. Die Analyse-Ergebnisse ermöglichten es dem Projekt, die Redundanzen zu priorisieren und ihre schrittweise Bereinigung zu planen. Diese Reduzierung hat dazu beigetragen, die Time-to-Market neuer fachlicher Anforderungen zu halbieren.

Das Problem

In diesem Projekt gab es viele Ursachen für eine lange Time-to-Market neuer Anforderungen. Mit der “Genom-Analyse” des Fachkonzepts wollte man die beiden folgenden Ursachen entschärfen

  • Beim Test neuer Releases tauchten oft so viele Fehler auf, dass das Team sich mächtig ins Zeug legen musste, um den Releasetermin nicht zu gefährden.
  • Auch kleine fachliche Änderungen konnten unverhältnismäßig aufwändig sein. Bei der Releaseplanung musste man entweder das System außerordentlich gut kennen oder die anstehende Änderung sehr sorgfältig analysieren, um nicht später bei der Umsetzung eine böse Überraschung zu erleben.

Eine Ursachenforschung ergab, dass diese beiden Probleme oft dieselbe Ursache hatten: Redundante Passagen im Fachkonzept wurden auf inkonsistente Weise geändert. Die Fachliche Chefdesignerin des Projekts vermutete, dass in den Hunderten von Seiten akribischer Fachkonzeptprosa noch viele verborgene Redundanzen auf ihre Chance lauerten, den Adrenalinspiegel des Teams in die Höhe zu treiben.

Sie bat mich daher um eine Redundanzanalyse des Fachkonzepts:

  • Welche Textblöcke sind redundant?
  • Welchen Text enthalten die verschiedenen Varianten?
  • An welchen Stellen kommt ein redundanter Textblock jeweils vor?
  • Wie ähnlich sind sich die verschiedenen Varianten eines Textblocks? Am besten wäre hier eine Kennzahl, die in die Priorisierung der Redundanzbereinigung einfließen kann.

Wir diskutierten diese Aufgabe eine Weile und erfuhr folgende Details über die Entstehung der gefürchteten Redundanzen:

  • Ein bestimmter Verarbeitungsschritt soll auch in einer weiteren Verarbeitung durchgeführt werden, z.B. wird eine Intervallberechnung für die Krankenversicherung auch für die Pflegeversicherung benötigt.
  • Nun kopiert man den betreffenden Abschnitt und passt die Kopie an die Besonderheiten der Pflegeversicherung an:
    • Vor allem tauscht man dabei die im Text referenzierten Attribute des fachlichen Datenmodells aus, denn die Daten zur Pflegeversicherung sind in speziellen Pflegeversicherungs-Attributen gespeichert.
    • Es kann aber auch sein, dass die Verarbeitung der Pflegeversicherung leicht derjenigen der Krankenversicherung abweicht und der kopierte Abschnitt entsprechend überarbeitet werden muss.
  • Wenn die Kopien eines redundanten Abschnitts geändert werden, z.B. wegen einer neuen fachlichen Anforderung, kann es vorkommen,
    • dass sich die überarbeiteten Kopien am Ende unterscheiden, etwa weil verschiedene Personen die Änderungen parallel durchgeführt haben, um sie schneller abschließen zu können oder
    • dass man Kopien übersieht, die daher auf dem alten Stand bleiben.

Die Lösung

Als Biologe ist mir eine Analogie ins Auge gesprungen zwischen dem redundanten Fachkonzept und dem Farbsehen beim Menschen. Und schon hatte ich den Schlüssel für die Lösung.

Spoiler alert: Im nächsten Absatz lüfte ich das Geheimnis. Wer den Zusammenhang selbst ausknobeln möchte, sollte daher nicht gleich weiterlesen.

Ein Mensch hat für das Farbsehen normalerweise drei Sehpigmente, deren Absoptionsmaximum im roten, grünen beziehungsweise blauen Bereich des Lichtspektrums liegt. Das rote und das grüne Sehpigment ähnlich einander stark: Tatsächlich zeigten genetische Analysen, dass zunächst das Gen für das grüne Sehpigment dupliziert wurde, genauer gesagt das Gen für den Proteinanteil des Pigments. Durch diese Mutation entstanden zwei identische Versionen desselben Pigments. Die beiden Kopien haben sich im Lauf einiger Jahrmillionen durch zusätzlich Mutationen immer weiter auseinander entwickelt. Also ähnlich wie die redundanten Abschnitte im Fachkonzept. Aufgeklärt hat man diese Evolutionsgeschichte durch die Analyse der Genome verschiedener Tierarten, insbesondere von Primaten. Ich brauchte also “nur” eine Art von “Genom-Analyse” für das Fachkonzept zu machen.

Wahl des Werkzeugs

Welches Werkzeug ich für die Redundanzanalyse am besten verwenden sollte, lag also auf der Hand:

  • Bei Genomanalysen werden vor allem Python und seine mächtigen wissenschaftlichen Bibliotheken eingesetzt.
  • Python verwendet man auch gern für Textanalysen.
  • Python kann gut mit regulären Ausdrücken umgehen, wie ich sie brauchen würde, um irrelevante Teile des Fachkonzepts auszublenden und Datenmodell-Referenzen zu normalisieren (mehr dazu gleich).
  • Python eignet sich als Interpreter-Sprache prima für Projekte, bei denen man zwar die grobe Zielrichtung kennt, sich aber den genauen Weg erst explorativ erarbeiten muss.

Python gehörte auch zur Infrastruktur meines Kunden und so konnte es losgehen. In den folgenden Abschnitten beschreiben ich Schritt für Schritt den Weg bis zur fertigen Redundanzanalyse.

Ausblenden irrelevanter Teile

Für die Redundanzanalyse musste man zunächst den Spezifikationstext aus dem UML-Modellierungswerkzeug exportieren, damit sich Python darüber hermachen konnte. Zuerst versuchte ich, aus dem Text die eigentliche fachlichen Verarbeitung zu extrahieren. Die Spezifikation war in purem Text ohne Formatierung verfasst und enthielt über die Beschreibung der fachliche Verarbeitung hinaus auch einführende Absätze als Einstiegshilfe für die Leser. Diese Absätze waren durch bestimmte Schlüsselwörter gekennzeichnet, sodass mein Python-Skript sie leicht von der weiteren Analyse ausschließen konnte.

Normalisierung der Datenmodell-Referenzen

Den restlichen Text spaltete das Skript in Absätze auf. In jedem Absatz suchte es nach Datenmodell-Referenzen, die durch typische Sonderzeichen zu erkennen waren, z.B. “#” als Trenner zwischen Klassen- und Attribut-Bezeichner. Diese Referenzen normalisierte das Skript, indem es die tatsächlichen Bezeichner durch die Standard-Bezeichner “Klassenname” und “Attributname” ersetzte.

Suche nach einem Maß für die Ähnlichkeit

Um redundante Abschnitte zu erkennen, musste das Skript Abschnitte paarweise vergleichen und ihre Ähnlichkeit mit einer normalisierten Maßzahl beziffern, die unabhängig vom Umfang der Abschnitte nur Werte zwischen 0 und 1 annimmt, wobei 1 für perfekte Übereinstimmung und 0 für kompletten Unterschied steht.

Ich stöberte in den Python-Bibliotheken und stieß schließlich auf eine Funktion zur Berechnung der “Levenshtein distance”, die auf der Anzahl von Einfügungen, Löschungen und Ersetzungen einzelner Buchstaben basiert, die man mindestens braucht, um den einen Partner des Vergleichspaars in den anderen umzuwandeln. Mit ähnlichen Abstandsmaßen ermittelt man in der Genom-Analyse, wie nahe zwei Individuen miteinander verwandt sind.

Experimente mit einem kleinen Ausschnitt des Fachkonzept lieferten vielversprechende Ergebnisse: Je höher die berechnete Ähnlichkeitszahl, um so ähnlicher war auch die fachliche Bedeutung.

Feinabstimmung der Ähnlichkeitsmessung

Umgekehrt traf das aber nicht immer zu: Manche Paare mit kleiner Ähnlichkeitszahl lagen inhaltlich näher beeinander als andere Paare mit hoher Ähnlichkeitszahl. Man sah auch sofort, dass es dafür zwei Ursachen gab:

  • Synonyme
    Zum einen wurden zwar unterschiedliche Wörter verwendet, diese hatten aber dieselbe Bedeutung. Ich ergänzte das Skript also durch eine Konfigurationsliste mit Synonymen und eine Vorverarbeitung, die verschiedene Wortvarianten durch eine Standard-Bezeichnung für den gemeinten Begriff ersetzte.
  • Füllwörter
    Manche Vergleichspartner unterschieden sich in der Verwendung von Füllwörtern, die den Text mehr oder weniger weitschweifig machen, sich aber nicht auf den fachlichen Sinn auswirken. Daher löschte das Skript nun auch solche Füllwörter in einer weiteren Vorverarbeitung.

Nach diesen Ergänzungen lieferte das Skript beim paarweisen Vergleich einiger handverlesener Abschnitte plausible Ähnlichkeitszahlen, sodass einem flächendeckenden Vergleich nichts mehr im Wege stand.

Ermittlung maximal großer Ähnlichkeitsbereiche

Der erste große Analyse-Lauf lieferte eine riesige Menge von Ähnlichkeitszahlen, denn die Abschnitte waren oft sehr kurz – viele bestanden nur aus einer einzigen Zeile, manchen nur aus einem einzigen Wort.

Solche Mini-Abschnitte sind für die Suche nach fachlichen Redundanzen nicht zu gebrauchen. Daher erweiterte ich das Skript so, dass es direkt aufeinander folgende Abschnitte, die ein bestimmtes Maß an Ähnlichkeit überschritten, zu einem einzigen längeren Abschnitt zusammenfasste.

Nach ein paar Probeläufen mit verschiedenen Schwellwerten für das “Maß an Ähnlichkeiten” bekam ich schließlich vernünftige Ergebnisse und lies das Skript dann wieder auf das gesamte Fachkonzept los. Am Ende lieferte es eine große Tabelle (in Python ein dataframe mit paarweisen Vergleichen.

Redundanzcluster

Jetzt musste ich noch eine Möglichkeit finden, die Vergleichspaare übersichtlich aufzubereiten. Das Fachkonzept-Team wollte ja sehen, welche Redundanzen besonders “schlimm” waren, sodass man sie möglichst bald bereinigen konnte. Dazu musste man die paarweisen Vergleiche zu Redundanz-Clustern zusammenfassen. Mit einem zweiten Skript wurden aus den paarweisen Vergleichen Cluster von untereinander sehr ähnlichen Abschnitten extrahiert (mithilfe eines weiteren Schwellwert-Parameters). Für jeden dieser Cluster lieferte das Skript folgende Informationen:

  • Eine eindeutige Cluster-Kennung
  • Einen zufällig herausgepickten Vertreter mit dem Textinhalt, sodass man den fachlichen Sinn erkennen konnte
  • Die Anzahl der Elemente des Clusters
  • Eine mittlere Ähnlichkeitszahl, die als Mittelwert aus den Ähnlichkeitszahlen der paarweisen Vergleiche zwischen den Elementen der Clusters berechnet wurde
  • Eine Kennzahl für die Redundanzbereinigung: je mehr Kopien und je umfangreicher die Abschnitte, um so größer die Kennzahl

Außerdem wurde eine Liste der Abschnitte erzeugt mit folgenden Informationen:

  • Eine eindeutige Kennung für den Abschnitt
  • Wo sich dieser Abschnitt im Fachkonzept befindet
  • Der Text des Abschnitts
  • Die Kennung des Clusters, zu dem der Abschnitt gehört

Manuelle Prüfung auf echte fachliche Redundanz

Beide Listen wurden dann in Excel geladen und dem Fachkonzept-Team zur Verfügung gestellt. Nun konnte die eigentliche Suche nach fachlichen Redundanzen beginnen. Dazu mussten Fachexperten prüfen,

  • ob sich die Varianten eines Abschnitts nur zufällig ähnelten: das kam vor allem bei Clustern mit kürzeren Abschnitten vor,
  • oder ob die Varianten eines Abschnitts aus fachlichen Gründen identisch sein sollten: nur in diesem Fall handelt es sich um eine echte fachliche Redundanz.

Hier ist eine weitere Analogie zur Genomanalyse interessant: In der Genetik bezeichnet man Gene als homolog, wenn sie wahrscheinlich einen gemeinsamen Ursprung haben, so wie die Gene für den roten und grünen Sehfarbstoff. Als homolog gelten Gene, die in mehr als 30 % ihrer Nukleotide übereinstimmen (Nukleotide sind quasi die Buchstaben des genetischen Texts).

Die fachlich redundanten Varianten eines Abschnitts könnte man also als “homolog” bezeichnen, während sonstige Ähnlichkeiten purer Zufall sind – oder handelt es sich vielleicht manchmal um einen Fall von “konvergenter Evolution”? Dabei entwickeln sich ähnliche Strukturen aus völlig verschiedenen Ursprüngen, z.B.

  • unsere Linsenaugen und
  • die Linsenaugen von Tintenfischen wie dem Octopus.

Die letzten gemeinsamen Vorfahren von uns und den Tintenfischen hatten nämlich kein Linsenaugen, höchstens ein paar lichtempfindliche Zellen.

Fazit

Dass ich bei der Redunanzanalyse des Fachkonzepts auf Analogien zur Genom-Analyse zurückgreifen konnte, hat mir Umwege erspart und die Analyse enorm beschleunigt, denn ich konnte mich aus einem wohlgefüllten Werkzeugkasten bedienen. Auf den Schultern von Riesen kann man eben weiter sehen, welche Art von Linsenaugen man hat.

Innovation: “Genom-Analyse” eines Fachkonzepts Read More »