Introduction to the Elm Programming Language

Standard

designer drawing website development wireframeElm is an interesting new programming language. Its goal is to make web GUI programming a great experience. It compiles the source code to JavaScript, HTML, and CSS.

Elm is a functional programming language. Its main influences are Haskell, OCaml, SML, and F#. Elm is strongly statically typed and it uses type inferencing. You don’t have to specify the type for variables and parameters like in C# or Java. Elm is built on the Haskell platform and has a Haskell-like syntax, though semantics are closer to OCaml.

I want to show you some basic features of Elm in this post. I’ll write some more posts about Elm in the next few weeks.

Hello, world!

You will find an online editor on the Elm homepage where you can try out the programming language. Just click “Try” and you’ll get to the online editor. On the left side you’ll see the editor and on the right side the result when you run the program. You can run the program by clicking on “Compile” (below the editor).

We’re learning a new language and the first program in a new language is usually a “Hello, world” program. It looks like this in Elm:

    main : Element
    main = plainText "Hello, world!"

main defines the entry point of the program. It is of type Element which represents a rectangle on the screen with a known width and height. Elements are combined to define complex widgets and layouts. The type is defined in the library Graphics.Element which is imported automatically when the program starts.

The first line of our program is the type signature. It defines the name and its type separated by a colon. The entry point has the name “main” and Element is its type. The type signature is optional.

The second line is the code. "Hello, world!" is a string. The function plainText converts the string to an element. You can see, that you don’t need braces for calling a function.

Here is another version of the hello world program:

    main = asText "Hello, world!"

Did you notice the difference? The second program prints the string “Hello, world!” with quotes and the first program without quotes. The first version uses the function plainText to convert the string to an element. plainText takes a string and creates the Element from the string value.

The second program uses asText which takes any value and uses its string representation for creating the Element. The string representation of a string includes the quotes and the value does not.

    main = asText (4,5)

This program prints the string representation of the tuple, “(4,5)”. A tuple is an ordered list of elements. The tuple above contains two integer values, 4 and 5. If you change asText to plainText you’ll get a type error. plainText expects a string as parameter, but (4,5) is a tuple.

Elm is reactive

Elm has an interesting type called Signal. A signal is a value which changes over time. Let’s see it in action:

    import Mouse

    main : Signal Element
    main = lift asText Mouse.position

The first line imports a library for working with mouse input called Mouse. The second line is the optional type signature. As you can see the type of the entry point main has changed to Signal Element. This means that output of the program can change over time.

The Mouse module contains a signal called position of type Signal (Int,Int). Every time the mouse is moved its value changes. The value is of type (Int,Int) which is a tuple with two integer numbers. It represents the current mouse position on the screen.

The function lift takes a function as first argument and a signal as second element. Every time the signal is updated the new value is applied to the function and its return value is used as new value for the signal returned by lift. We use asText as function so that lift converts each tuple with the mouse positions to a string when the mouse position changes.

I showed you how a basic hello world program looks like in Elm and I introduced you to Elm’s concept of signals. I will focus on the functional nature of the language in my next post about Elm.

The 25 Most Interesting Links about ASP.NET vNext and the K Runtime

Standard

What is nextMicrosoft revealed its plans for the next generation of .NET at the TechEd North America in May. The next version of ASP.NET will bring some interesting features for ASP.NET web developers:

  • It will be fully open source.
  • ASP.NET MVC, ASP.NET WebAPI and SignalR will be integrated into one framework.
  • Multiple versions of the runtime can be installed side-by-side.
  • It runs on Mono, on both Linux and Mac OS X.
  • No dependency on System.Web.
  • Better support for development with text editor and command line tools.

I collected some links for more information and details about this topic.

Blog posts about ASP.NET vNext and the K Runtime

Videos about ASP.NET vNext and the K Runtime

Blog Reorganisation

Standard

BlogI migrated my blogs back to WordPress. I started blogging with self-hosted WordPress blogs about 8 years ago. Some years ago I began using Disqus for comments and switched one blog to Octopress. I migrated both blogs to Pelican one year ago. Now I’m back at WordPress. While I was migrating the blogs I decided to merge my two blogs andreas-schlapsi.com (English blog) and andreas-schlapsi.at (German blog) to one blog at schlapsi.com and redirected the URLs from my old blogs to this one. I plan to write here in English. Maybe I’ll write something in German here every now and then.

Why did I switch to WordPress again? Jeff Atwood said, a blog without comments is not a blog and not having a decent comments system was one of the biggest disadvantages when I used a static site generator. I didn’t want to save the comments on another system operated by a commercial company like Disqus.

I found a way to write my posts in Markdown on WordPress. Not being able to write in Markdown was the biggest issue I had with WordPress and the number one reason why I migrated my blog away from WordPress.

I wanted to improve the themes of my old blogs, but I didn’t have enough time to work on them. I guess it’s easier to use one of the available WordPress themes instead of creating my own theme.

But there are also some downsides: first I need to run server software which needs more server performance than a static site. There are solutions which can improve the situation.

Second I still don’t like the editor. There are blog editors out there. Maybe I’ll try out some of them.

Of course, I had a look at Ghost, but it wasn’t as mature as WordPress. It didn’t have a comment system, too. It looked very nice and promising, though.

Official End of the RowTest Extension

Standard

I published my RowTest extension for NUnit in 2007. I developed it to experiment
with the NUnit extension mechanism which was new in NUnit 2.4. It got some users
and the extension was included in NUnit 2.4.8. NUnit 2.5 had the new Test Case feature
which was built on the experience gained by the RowTest extension.

To switch from RowTest to TestCase you just have to replace the RowTest attribute by the TestCase attribute.

Since the RowTest extension isn’t needed anymore I stopped its development long time ago. I just didn’t publish an official statement about its end. I will remove the RowTest project page on my blog, but the project pages on Google Code and Launchpad will still be available:

Von Rot zu Grün – Green Bar Patterns

Standard

Ich wende testgetriebene Entwicklung bereits seit über 10 Jahren an. Kurz zur Erinnerung: Bei TDD (Test-Driven Development) erfolgt die Implementierung in vielen kleinen Iterationen, die aus drei Teilen bestehen:

  • Schreibe einen Test für das erwünschte Verhalten. Der Programmcode erfüllt diesen Test noch nicht oder es gibt ihn noch gar nicht. Manche Testrunner stellen diesen Zustand durch einen roten Balken dar. Daher wird diese Phase oft mit Red bezeichnet.
  • Ändere oder schreibe den Programmcode mit dem kleinstmöglichen Aufwand, damit er den Test erfüllt. Manche Testrunner stellen diesen Zustand durch einen grünen Balken dar. Daher wird diese Phase oft mit Green bezeichnet.
  • Refaktorisiere den Code. Entferne Code-Duplizierung, führe Abstraktionen ein, wenn nötig, usw. Danach führe die Tests nochmal aus. Der Code muss die Tests noch immer erfüllen. Diese Phase wird oft mit Refactor bezeichnet.

Ich habe am Beginn viel über TDD gelesen. Meistens waren es Artikel im Internet. Es gab noch nicht viele Bücher über dieses Thema. Eine Ausnahme war Kent Becks Test-Driven Development. Es ist ein vergleichsweise dünnes Buch, das ich sehr schnell gelesen hatte.

Vor kurzem habe ich das Buch wieder gelesen und dabei wurden mir einige Details wieder bewusst. Dazu gehören die sogenannten “Green Bar Patterns”. Diese Patterns beschreiben, wie wir den Programmcode ändern können, damit er die Tests erfüllt:

  • Triangulation
  • Fake It (‘Til You Make It)
  • Offensichtliche Implementierung

Triangulation

Bei der Triangulation verallgemeinern wir den Code erst, wenn wir zwei oder mehr Tests geschrieben haben. Daraus ergibt sich folgender Ablauf:

  • Zuerst schreiben wir einen Test.
  • Dann machen wir die kleinstmögliche Änderung am Programmcode, damit er diesen Test erfüllt, z.B. geben wir nur den erwarteten Wert zurück.
  • Dann schreiben wir noch einen Test.
  • Erst jetzt abstrahieren wir die Implementierung.

Nehmen wir an, dass wir eine Funktion schreiben wollen, die zwei Zahlen addiert. Der erste Test könnte so aussehen:

    [Test]
    public void Sum_3plus4_Returns7()
    {
      Assert.That(Plus(3, 4), Is.EqualTo(7));
    }

Dann würde diese Implementierung ausreichen, um den Test zu erfüllen:

    public int Plus(int a, int b)
    {
      return 7;
    }

Um den Code verallgemeinern zu können, benötigen wir einen weiteren Test:

    [Test]
    public void Sum_9plus15_Returns24()
    {
      Assert.That(Plus(9, 15), Is.EqualTo(24));
    }

Jetzt können wir die allgemeine Implementierung schreiben:

    public int Plus(int a, int b)
    {
      return a + b;
    }

Beide Tests müssen zu diesem Zeitpunkt funktionieren.

Triangulation wird gerne verwendet, weil die Regeln sehr klar sind. Kent Beck empfiehlt diese Methode nur dann, wenn man sich über die korrekte Implementierung völlig unsicher ist.

Fake It (‘Til You Make It)

Auch bei “Fake It” machen wir nach dem ersten Test nur die kleinstmögliche Änderung am Programmcode, um den einen Test zu erfüllen. Im Unterschied zur Triangulation schreiben wir kein zweites Testbeispiel, sondern verallgemeinern den Code im Refaktorisierungsschritt.

  • Zuerst schreiben wir einen Test.
  • Dann machen wir die kleinste Änderung am Programmcode, damit dieser den Test erfüllt, z.B. durch Rückgabe des erwarteten Werts.
  • Jetzt abstrahieren wir die Implementierung durch Refaktorisierung.

Der erste Test und die zugehörige Implementierung für das obige Beispiel sieht wieder so aus:

    [Test]
    public void Sum_3plus4_Returns7()
    {
      Assert.That(Plus(3, 4), Is.EqualTo(7));
    }

    public int Plus(int a, int b)
    {
      return 7;
    }

Im Refaktorisierungsschritt eliminieren wir die Code-Duplizierung zwischen Test und Implementierung und ersetzen sie durch den richtigen Code:

    public int Plus(int a, int b)
    {
      return a + b;
    }

Wichtig bei “Fake It” ist, den Refaktorisierungsschritt ernst zu nehmen. Nach meiner Beobachtung wird gerne aufs Refaktorisieren vergessen, obwohl es einen so wichtigen Beitrag zum Software-Design darstellt.

Offensichtliche Implementierung

Bisher bestand die Implementierung nach dem ersten Test daraus, dass wir den erwarteten Rückgabewert als Konstante zurückgeben. Aber müssen wir das tun, wenn doch ganz klar ist, wie die Implementierung aussieht? Vor allem in unserem Beispiel mit der Addition zweier Parameter? Natürlich nicht. Wenn vollkommen klar ist, wie die Implementierung aussehen soll, schreiben wir sie einfach.

In unserem Beispiel sieht das so aus:

    [Test]
    public void Sum_3plus4_Returns7()
    {
      Assert.That(Plus(3, 4), Is.EqualTo(7));
    }

Und dann schreiben wir gleich:

    public int Plus(int a, int b)
    {
      return a + b;
    }

Wann verwendet man welche Methode?

Viele TDD-Neulinge fangen mit der offensichtlichen Implementierung an und verzetteln sich dann, weil sie zu große Schritte machen. Andere tendieren zur Triangulation und wundern sich, warum TDD so mühsam ist.

Tatsächlich haben alle drei Methoden ihre Berechtigung. Wir können mit “Fake It” anfangen und zur offensichtlichen Implementierung wechseln, wenn wir merken, dass der Code sehr einfach ist. Wenn die Tests öfter unbeabsichtigt rot bleiben, dann sind wir zu schnell unterwegs und sollten zu “Fake It” oder Triangulation wechseln.

Oft wissen wir nach längerem Nachdenken noch nicht, wie der Code aussehen soll. Dann werden wir mit der Triangulation beginnen und wenn wir sicherer geworden sind, zu einer schnelleren Methode wechseln.

Wenn der Code Schleifen oder Verzweigungen enthalten soll, dann ist er sehr wahrscheinlich nicht mehr einfach. Offensichtliche Implementierung ist dann offensichtlich die falsche Methode.

Linktipp: Das Pac-Man-Dossier

Standard

Vor kurzem habe ich einen interessanten Link entdeckt: The Pac-Man Dossier. Darin erzählt Jamey Pittman, was er über eines der populärsten Arcade-Spiele, Pac-Man, zusammengetragen hat:

  • die Hintergrundgeschichte von Namco, Pac-Mans Designer Toru Iwatani und der Entwicklung von Pac-Man,
  • eine detaillierte Beschreibung des Gameplays,
  • Details zum Pathfinding,
  • das Verhalten der Ghosts und
  • eine Beschreibung des “Split-Screens” in Level 256.

Pac-Man sollte endlos weiterspielbar sein, d.h. es sollte keine maximale Punkteanzahl geben. Allerdings gibt es einen Fehler im Pac-Man-Code, der dazu führt, dass das Level 256 nicht zu Ende gespielt werden kann. Die Details über diesen Bug und einen Bugfix beschreibt Don Hodges auf seiner Webseite. Dank dieses Fehlers kann man in Pac-Man maximal 3.333.360 Punkte erreichen.

Im Original-Pac-Man-Code gibt es einen weiteren Fehler, der die Pathfinding-Algorithmen der Ghosts Pinky und Inky betrifft. Auch darüber hat Don Hodges auf seiner Webseite eine Fehlerbeschreibung und eine Lösung geschrieben.

Wer vom Z80-Assembler-Code noch nicht genug hat, kann sich ja anschauen, was Alessandro Scotti über die Pac-Man-Hardware herausgefunden hat.

Chad Birch hat in einem Blogpost das unterschiedliche Verhalten der vier Ghosts Blinky, Pinky, Inky und Clyde analysiert. Einfache Regeln führen zu einem anscheinend komplexen Verhalten.

Tyler Neylon hat sich an eine Reimplementierung des Spiels gewagt. Der Anlass dafür war eine Bemerkung von ihm, dass die alten Spiele heute viel einfacher zu implementieren seien, sogar an einem Tag. Er hat es geschafft und Pacpac als Open-Source veröffentlicht.

Vor drei Jahren hat Google anlässlich des 30. Geburtstags von Pac-Man das Spiel als Google-Doodle präsentiert. Zum ersten Mal setzte Google ein in JavaScript geschriebenes interaktives Spiel als Doodle ein. Viele Benutzer wunderten sich, woher die Geräusche aus ihrem Computer kamen. Übrigens, das Doodle lässt sich heute noch spielen.

Key-Value-Store in PostgreSQL

Standard

Vor kurzem habe ich mir die relationale Open-Source-Datenbank PostgreSQL angesehen. Dabei sind mir einige Features aufgefallen, die normalerweise mit NoSQL-Datenbanken in Verbindung gebracht werden. Eines davon ist die hstore-Erweiterung.

Die hstore-Erweiterung

Neuere PostgreSQL-Versionen enthalten die hstore-Erweiterung, die einen neuen Datentyp hstore einführt. Darin können Schlüssel-Wert-Paare gespeichert werden, wobei die Erweiterung nur String als Datentyp für Schlüssel und Wert kennt. Die Daten in dem hstore-Feld müssen keinem felstgelegtem Schema folgen.

Erstellen der Datenbank

Um die Erweiterung zu verwenden, muss sie zunächst in der Datenbank installiert werden.

    CREATE EXTENSION hstore;

Danach steht der neue Datentyp hstore für Tabellen zur Verfügung:

    CREATE TABLE account
    (
      id serial PRIMARY KEY,
      login varchar(40) NOT NULL UNIQUE,
      password varchar(40) NOT NULL,
      info hstore
    );

Das SQL-Kommando INSERT kann neue Daten für die hstore-Spalte einfügen. Die Schlüssel-Wert-Paare werden als String durch Kommas getrennt. Der String kann mit ::hstore in den richtigen Datentyp konvertiert werden:

    INSERT INTO account (login, password, info)
        VALUES ('andreas', 'securepassword', 'location => "Wien", age => 38'::hstore);
    INSERT INTO account (login, password, info)
        VALUES ('hugo', 'othersecurepassword', 'location => "Wien", age => 42, married_with => "daniela"'::hstore);
    INSERT INTO account (login, password, info)
        VALUES ('daniela', 'pqwaösdkjf', 'location => "Linz", married_with => "hugo"'::hstore);
    INSERT INTO account (login, password, info)
        VALUES ('agnes', 'krzlzaf', 'age => 39'::hstore);

Operatoren für den Key-Value-Store

Die hstore-Erweiterung implementiert neue Operatoren, um die Schlüssel-Wert-Paare in SQL-Abfragen zu verwenden. So kann man z.B. mit dem Operator ‘->’ auf die Werte in der hstore-Spalte mit dem Schlüssel zugreifen. Den Operator kann man z.B. in Abfragen verwenden:

    SELECT login, info->'age' as age, info->'married_with' as married_with
        FROM account WHERE info->'location' = 'Wien';

Der Operator kann auch in Joins verwendet werden:

    SELECT a1.login, a1.info->'location' as location,
           a2.login as partner, a2.info->'location' as partnerlocation
      FROM account a1 INNER JOIN account a2
        ON a1.info->'married_with' = a2.login;

Der Operator ‘?’ überprüft, ob ein Schlüssel in der hstore-Spalte verfügbar ist.

    SELECT login FROM account WHERE info ? 'married_with';

Der Operator ‘-’ entfernt ein Schlüssel-Wert-Paar aus einem hstore:

    UPDATE account SET info = info::hstore - 'married_with'::text
        WHERE info->'married_with'='daniela';
    UPDATE account SET info = info::hstore - 'married_with'::text
        WHERE info->'married_with'='hugo';

Der Operator ‘||’ verkettet zwei hstores:

    UPDATE account SET info = info || 'married_with=>"agnes"' WHERE login='hugo';
    UPDATE account SET info = info || 'married_with=>"hugo", location=>"Wien"'
        WHERE login='agnes';

Bestehende Werte werden dabei geändert:

    UPDATE account SET info = info || 'location=>"Graz"' WHERE login in ('hugo', 'agnes');

Mehr Informationen über die hstore-Erweiterung gibt es in der PostgreSQL-Dokumentation.

Zugriff auf die Datenbank aus C#

Von C# aus kann man mit Npgsql auf eine PostgreSQL-Datenbank zugreifen. Neue Daten kann man dann so in eine Tabelle mit einer hstore-Spalte einfügen:

    var cmdText = "INSERT INTO account (login, password, info) VALUES (:login, :password, :info::hstore)";
    var info = "location => \"Wien\", age => 42, married_with => \"daniela\"";
    using (var command = new NpgsqlCommand(cmdText, connection))
    {
        command.Parameters.Add(new NpgsqlParameter("login", NpgsqlDbType.Varchar, 40));
        command.Parameters.Add(new NpgsqlParameter("password", NpgsqlDbType.Varchar, 40));
        command.Parameters.Add(new NpgsqlParameter("info", NpgsqlDbType.Text));

        command.Parameters[0].Value = "hugo";
        command.Parameters[1].Value = "pqwesdf";
        command.Parameters[2].Value = info;
    
        command.ExecuteNonQuery();
    }

Den Wert für die hstore-Spalte muss man selbst in einen String serialisieren und als Datentyp NpgsqlDbType.Text an das Parameterobjekt übergeben. Im SQL-Kommando muss man den Wert auf den Datentyp hstore casten (“:info::hstore“).

    var cmdText = "SELECT login, password FROM account WHERE data->'location' = :location";
    using (var command = new NpgsqlCommand(cmdText, connection))
    {
        command.Parameters.Add(new NpgsqlParameter("location", NpgsqlDbType.Varchar, 40));
        command.Parameters[0].Value = "Wien";

        using (var reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                var login = reader.GetString(reader.GetOrdinal("login"));
                var password = reader.GetString(reader.GetOrdinal("password"));

                Console.WriteLine("Login: '{0}', Password: '{1}'", login, password);
            }
        }
    }

Natürlich können auch andere Operatoren verwendet werden:

    var cmdText = "SELECT login, password FROM account WHERE NOT data ? 'age'"; 
    using (var command = new NpgsqlCommand(cmdText, connection))
    {
        using (var reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                var login = reader.GetString(reader.GetOrdinal("login"));
                var password = reader.GetString(reader.GetOrdinal("password"));

                Console.WriteLine("Login: '{0}', Password: '{1}'", login, password);
            }
        }
    }

Zugriff mit Python

In Python kann man psycopg2 für den Zugriff auf eine PostgreSQL-Datenbank benutzen:

    import psycopg2

    connection_string = <INSERT CONNECTION STRING>
    conn = psycopg2.connect(connection_string)
    cur = conn.cursor()
    cmdText = 'INSERT INTO account (login, password, info) VALUES (%s, %s, %s::hstore)'
    cur.execute(cmdText, ('andreas', 'qwpoeir', 'location => "Wien", age => 38'))
    cur.execute(cmdText, ('daniela', 'askdjf', 'location => "Wien", married_with => "hugo"'))
    cur.execute(cmdText, ('agnes', 'asdfoi', 'age => 37'))
    cur.execute(cmdText, ('hugo', 'ypxoiu', 'location => "Linz", age => 42, married_with => "daniela"'))
    conn.commit()
    cur.close()
    conn.close()

Zugriff aus Node.js

Für Node.js kann das Modul pg für den Zugriff auf PostgreSQL verwendet werden:

    var pg = require('pg');
    var conString = <INSERT CONNECTION STRING>;

    var client = new pg.Client(conString);
    client.connect();

    var cmdText = "INSERT INTO account (login, password, info) VALUES ($1, $2, $3)";
    client.query(cmdText, ['andreas', 'asdf', 'location=>"Wien", age=>38']);
    client.query(cmdText, ['daniela', 'qwre', 'location=>"Wien", married_with=>"hugo"']);
    client.query(cmdText, ['agnes', 'lkj', 'age=>37']);
    client.query(cmdText, ['hugo', 'ppoiu', 'location=>"Linz", age=>42, married_with=>"daniela"']);

    var query = client.query("SELECT login, password FROM account WHERE info->'location' = $1", ['Wien']);

    query.on('row', function(row) {
      console.log('Login: "%s", Password: "%s"', row.login, row.password);
    });

    query.on('end', function() {
      client.end();
    });

Zusammenfassung

Die hstore-Erweiterung fügt einen neuen Datentyp, neue Operatoren und neue Funktionen zu einer Datenbank hinzu. Damit kann mit der relationalen Open-Source-Datenbank PostgreSQL eine Art Key-Value-Store realisiert werden.

In C# muss man auf die Datentypen achten, wenn man mit Npgsql die Datenbank verwendet. In Python und mit Node.js können die als String serialisierten Informationen direkt als Parameter übergeben werden. Ich würde mir wünschen, dass ich die Daten für die hstore-Spalte als Dictionary übergeben könnte. Leider wird das von den Datenbankbibliotheken (noch) nicht unterstützt.

Dogmatisches TDD

Standard

Immer wieder wird den “Verfechtern” des Test-Driven Developments (TDD) vorgeworfen, dogmatisch zu sein. Vor ein paar Wochen gab es eine Diskussion unter Größen der Software-Entwicklung darüber, wann TDD und Unit-Tests verwendet werden sollen und wann nicht. Hier eine kurze Zusammenfassung.

Die Start-Up-Falle

Alles begann mit einem Blogpost von Robert C. Martin, auch bekannt als Uncle Bob. In diesem Blogpost beschrieb er die Start-Up-Falle. Das ist die Idee, dass die Situtation in einem Start-Up anders wäre. Dass alles, was man über professionelle Software-Entwicklung gelernt hat, in diesem besonderen Job nicht anwendbar ist und man daher die Regeln brechen muss.

Uncle Bob widerspricht dem vehement. Die Start-Up-Phase ist nicht anders. Sie gibt vielmehr die Richtung für die nächsten Phasen vor. Die Praktiken, die zu erfolgreicher Software führen, sind immer gültig.

Natürlich meint er mit einer dieser Praktiken TDD (Test Driven Development). Manche glauben, dass man schneller ist, wenn man keine Tests schreibt.

Uncle Bob setzt dem entgegen, dass der Buchhalter eines Start-Ups unter noch größerem Druck steht als jeder Software-Entwickler. Immerhin verwaltet er oder sie das Geld der Investoren. Dennoch folgt der Buchhalter den Regeln und der Methoden der Buchhaltung. Sind Fehler im Code tolerierbarer als die Berechnungen des Buchhalters? Natürlich nicht. Kann ein Fehler im Code das Unternehmen nicht ruinieren? Natürlich kann er das. Wieso sollte man dann auf TDD verzichten, wenn der Buchhalter auch nicht auf die doppelte Buchhaltung verzichtet?

TDD nicht anzuwenden, macht einen nicht schneller, sondern vielmehr langsamer. Die Gründe dafür sind einfach:

  • Man kann den Code nicht mehr refaktorisieren.
  • Daher wird der Code verrotten.
  • Er wird daher immer schwerer zu handhaben.
  • Man wird langsamer.

Das Gemeine daran, TDD nicht anzuwenden, ist, dass es einem das Gefühl gibt, schneller zu sein. Man arbeitet hart, ist 60, 70 oder 80 Stunden pro Woche in der Arbeit.

Aber Aufwand ist nicht gleich Geschwindigkeit. Mit der Zeit steigen die Schätzungen. Es wird immer schwerer, neue Features einzubauen. Es sammeln sich immer mehr Bugs an. Der Entwicklungsfortschritt wird immer langsamer, die Anzahl der Fehler steigt. Die Katastrophen werden immer mehr.

Daher empfiehlt Uncle Bob: Wenn man schneller vorankommen will,

  • folge den bewährten Praktiken!
  • schreibe deine Tests!
  • refaktorisiere deinen Code!
  • halte den Code einfach und sauber!
  • hetze nicht!

Die Pragmatik des TDD

Der Blogpost über die Start-Up-Falle dürfte einigen Aufruf verursacht haben. Deshalb hat Uncle Bob einen Tag später den nächsten Blogpost verfasst. Darin geht er auf den Vorwurf des Dogmatismus ein.

Im Prinzip wurde Uncle Bob vorgeworfen, dass er zu dogmatisch sei. TDD mag in manchen Fällen großartig sein, aber in anderen Fällen hätte es zu hohe Kosten. Deshalb muss man pragmatisch sein und je nach Situation entscheiden.

Für Uncle Bob ist TDD eine Disziplin für Programmierer wie die doppelte Buchhaltung für Buchhalter oder die sterile Vorgehensweise für Chirurgen. Selbst diese gehen in bestimmten Situationen von diesen Praktiken ab.

Und genauso ist es mit TDD für Programmierer. Uncle Bob listet Fälle auf, in denen nicht einmal er TDD anwendet:

  • Er schreibt keine Tests für Getter und Setter.
  • Er schreibt keine Tests für Member-Variablen.
  • Er schreibt keine Tests für triviale Funktionen oder Einzeiler.
  • Er schreibt keine Tests für GUIs. Er bemüht sich aber, jede signifikante Verarbeitung im GUI-Code in testbare Module auszulagern (MVVM, MVC, MVP, …)
  • Er wendet TDD nicht für Code an, den er noch mit der Methode “Versuch und Irrtum” entwickeln muss. Er schreibt entweder die Tests danach oder löscht den so entstandenen Code und entwickelt ihn mit TDD neu.
  • Er schreibt in manchen Situationen keine Tests für Programme, die klein sind und wirklich nur einmal verwendet werden.
  • Er mockt Software von anderen Herstellern und testet seinen Code. Nur wenn er glaubt, dass die Fremdsoftware nicht funktioniert, oder Mocken zu viel des Guten wäre, testet er diese auch.

Tests für trivialen Code?

Dieser Blogpost wiederum führte zu weiteren Diskussionen über die Frage, ob trivialer Code getestet werden soll. Immerhin hat Uncle Bob geschrieben, dass er Getter, Setter, triviale Funktionen und Einzeiler nicht testet. Mark Seemann vertritt die Position, dass auch trivialer Code getestet werden muss. Seine Argumente:

  • Bei TDD lenken die Tests das Design. Wenn man den Test schreibt, kann man noch gar nicht wissen, ob der Code trivial wird. Er untermauert dieses Argument mit Uncle Bobs Idee der Transformation Priority Premise.
  • Trivialer Code muss nicht trivial bleiben. Wenn ein Getter oder Setter bzw. eine .NET-Property trivial ist, wofür hat man sie dann überhaupt? Getter und Setter ermöglichen es, in Zukunft die Implementierung zu ändern. Um diese Implementierung refaktorisieren zu können, braucht man Unit-Tests.
  • Nicht-trivialen Code nicht zu testen, ist ein schrecklicher Rat für TDD-Anfänger. TDD-Anfänger werden diese Hintertüre jedesmal verwenden, wenn der Code einmal schwieriger zu testen ist. Die Bedingung ist ja auch nicht besonders genau definiert. Wann kann man schon vorhersehen, dass der Code trivial sein wird?

Weiters demonstriert Mark Seemann an einem Beispiel, dass man den Aufwand für das Testen von Properties reduzieren kann. Seine These ist, dass Uncle Bob die Kosten für das Testen von trivialen Code als fix annimmt und den Nutzen dafür als gering. Wenn man aber die Kosten senkt, ergibt sich ein besseres Kosten-/Nutzenverhältnis.

Dem widersprach Mark Rendle. Seine Argumente:

  • Tests für .NET-Properties testen den Compiler und nicht den Code.
  • Sie testen Implementierungsdetails und nicht die Anwendungslogik. Es gibt andere Tests, die eine Anwendungsfunktionalität testen, die die .NET-Properties, Getter und Setter brauchen, sonst müssten sie als toter Code entfernt werden.
  • Sinnlose Tests sind schlimmer als überhaupt keine Tests.
  • Tests für Getter, Setter und anderen trivialen Code verbergen überflüssigen Code.
  • Sie präsentieren TDD in schlechtem Licht, weil sie TDD als unnötig zeitfressend, kleinlich und zwecklos erscheinen lassen.

Daraufhin hat Derick Bailey beiden widersprochen und rechtgegeben. Für ihn ist die Frage, ob eine .NET-Autoproperty getestet werden soll, mit der eigentlichen Funktion des Codes verbunden. Wenn die Property einen Mehrwert liefert, was die Funktionalität betrifft, sollte es auch einen Unit-Test dafür geben.

Jimmy Bogard hat wahrscheinlich unabhängig von der ganzen Diskussion ungefähr zur gleichen Zeit einen Blogpost geschrieben. Kurz gesagt, soll man nur dann Tests schreiben, wenn sie einen Wert bieten. Und wann das ist, beantwortet er mit der berühmten Beraterantwort: “It depends.”

In einem weiteren Blogpost präzisierte Mark Seemann seine Position. In Übereinstimmung mit dem Dreyfus-Modell bevorzugt er fixe Regeln für TDD-Anfänger. Weiters wirft er die Frage auf, ob in seinem Beispiel überhaupt eine .NET-Property notwendig ist. Eigentlich würde auch ein normales Feld reichen.

Start-Ups und TDD

In der Zwischenzeit hat Greg Young auf einen anderen Aspekt des ursprünglichen Blogposts von Uncle Bob geantwortet. Anhand eines eigenen persönlichen Beispiels versucht er dabei zu argumentieren, dass es für Start-Ups gefährlich sei, das Produkt nach den üblichen Entwicklungsmethoden zu entwickeln. Ein Start-Up sollte sich vielmehr darauf konzentrieren, “Production Prototypes” zu entwickeln.

Chris McKenzie hat ihm widersprochen und die Frage aufgeworfen, ob es gerechtfertigt ist, mangelhafte Software am laufenden Band zu produzieren, nur weil man für ein Start-Up arbeitet.

Uncle Bob antwortete in einem eigenen Blogpost und wehrt sich vor allem gegen die Unterstellung, dass TDD jemanden langsamer entwickeln lässt.

Meine Sichtweise

Was halte ich eigentlich von dem allen?

Ich stimme der Position von Uncle Bob zu. TDD ist eine bewährte Methode, doch gibt es Situationen, wo sie nicht praktikabel ist. Allerdings handelt es sich dabei um Ausnahmen, die es so lange wie möglich zu vermeiden gilt.

Ich stimme auch Mark Seemann zu, dass es für TDD-Anfänger fixe Regeln braucht. Nur mit viel Erfahrung kann man Regeln brechen. Deshalb empfehle ich jedem TDD-Anfänger, den Regeln zu folgen. Auch dann, wenn jemand mit TDD-Erfahrung diese Praktik nicht anwenden würde.

Ich habe noch nie in einem Start-Up gearbeitet, aber ich verwende seit ca. 10 Jahren TDD. Und ich entwickle deshalb nicht langsamer. Greg Young beschreibt in seinem Blogartikel einen Fall von technischen Schulden. Um kurzfristige Ziele zu erreichen, kann man auf gutes Design und Unit-Tests verzichten. Allerdings darf man nicht vergessen, rechtzeitig den Code auf eine tragfähige Basis mit Tests zu stellen, sonst verrottet der Programmcode, neue Features können nur mehr mit großem Aufwand eingearbeitet werden und die Fehlerquote steigt. Genauso wie es Uncle Bob in seinem ursprünglichen Blogpost beschrieben hat. Kann es sich ein junges Unternehmen, dass sich noch einen Namen erarbeiten muss, überhaupt leisten, laufend mangelhafte Software auszurollen?

Blogredesign

Standard

Vor rund 2 Wochen habe ich mein Blog rundum erneuert. Ich habe ein eigenes moderneres Theme entwickelt und habe das Blog von WordPress auf Pelican migriert.

Die Gründe dafür sind ganz einfach:

  • Markdown
  • Keine Serversoftware mehr
  • Einfachere Versionierung in einem Git-Repository
  • Einfachere Entwicklung von eigenen Themes

Markdown

Bei diesem Blog geht es hauptsächlich um Software-Entwicklung. Daher muss ich oft Code-Beispiele in einen Artikel einfügen. Unter WordPress war das nicht immer einfach. Ich habe mir sämtliche Plugins angesehen, die hier Abhilfe versprachen. WordPress hat trotzdem entweder den Artikel oder die Code-Beispiele zerstört. Nur wenn ich den Text direkt in HTML formatiert habe und nicht in die WYSIWYG-Ansicht gewechselt bin, blieb der Code vielleicht erhalten.

Schon seit längerem plante ich, die Artikel im Markdown-Format zu verfassen. Auch andere Entwickler schwören auf Markdown. Mit meinem englisch-sprachigen Blog habe ich ein Experiment gewagt und es auf Octopress umgestellt. Octopress basiert auf dem Site-Generator Jekyll, der in Ruby geschrieben ist. Mittlerweile gab es schon seit längerem kein Update mehr für Octopress. Ruby und die Ruby-Tools sind mir nicht so geläufig und deshalb habe ich mich umgesehen, was es sonst noch gibt. Pelican ist ein Site-Generator, der in Python geschrieben ist. Und von Python habe ich deutlich mehr Ahnung als von Ruby. Außerdem wird Pelican aktiv weiterentwickelt.

Keine Serversoftware mehr

WordPress ist in PHP geschrieben und läuft auf dem Server. Das heißt, dass ein potentieller Hacker neben dem HTTP-Server noch ein anderes lohnenswertes Ziel hat und Sicherheitslücken in PHP oder WordPress selbst ausnützen kann, um sich Zugriff auf den Server zu verschaffen. Außerdem benötigt WordPress eine eigene Datenbank und Systemresourcen am Server.

Mit einem Site-Generator gehören diese Probleme der Vergangenheit an.

Versionierung in einem Git-Repository

Weil sich die Dateien für das Blog auf meiner Festplatte befinden und es sich ausschließlich um Textdateien handelt, ist es ganz einfach, das Blog in einem Git-Repository zu versionieren. Außerdem kann ich so einen normalen Texteditor verwenden.

Einfachere Entwicklung von eigenen Themes

Die Entwicklung von WordPress-Themes erfordert einiges an Einarbeitungszeit. Ich verwende die Zeit lieber mit dem Schreiben von Blogposts.

Mit Pelican ist die Entwicklung von eigenen Themes wesentlich einfacher.

Kein Kommentarsystem mehr

Eine Konsequenz aus der Umstellung ist, dass ich kein Kommentarsystem mehr habe. Ich habe vor einiger Zeit auf Disqus umgestellt, doch aus diversen Gründen möchte ich davon wegkommen.

Auch andere Entwickler mit Blog kommen ohne Kommentarsystem aus.

Für den Fall, dass mich jemand kontaktieren möchte, habe ich auf jeder Seite Kontaktmöglichkeiten angegeben.

Wie geht’s weiter?

Ein weiterer Schritt wird sein, mein englischsprachiges Blog auf das neue System und Theme umzustellen und wiederzubeleben.

Das neue Theme hat noch hier und da seine Schwächen, die ich mit der Zeit ausbügeln möchte. Vor allem hoffe ich, dass mich das neue System dazu motiviert, mehr zu schreiben. Ich habe jedenfalls schon einige Beiträge in Planung.

The Value of TDD

Standard

Michael Feathers wrote a blog post about the flawed theory behind unit testing some years ago. He concluded that testing does not improve quality just by catching errors. Unit testing and especially Test-Driven Development (TDD) force us to reflect on our code. Thought and reflection are important to improve the quality of our code.

I wrote a component which should generate a higher-level representation of a level in the game engine Unity some weeks ago. Writing the unit tests I noticed that I haven’t thought about an edge case before which required a redesign of the component. Without TDD I most likely wouldn’t have spotted this minor design flaw and the error would have popped up much later when it would have been very hard to find.

I think that there are two main reasons for Unit Testing and TDD:

  1. It forces us to think about our code.
  2. Tests provide a safety net for refactorings.

Of course, there are times when unit testing is not appropriate. I don’t like unit testing when I’m still learning the programming platform or the technology. But I think that generally there is no reason why not to write unit tests and why not to write them first.