17 June 2012

Visualizing a code repository with Bubble Charts and Circle Packing


German Version - English Version
Vor einiger Zeit bin ich über die freie Javascript Library d3.js gestolpert, die zur Visualisierung und Animation von  Daten in Webseiten dient. Da Datenvisualisierung eines meiner Hobbies ist und ich mich beruflich auch etwas mit Javascript beschäftige, lag es nahe sich dieses d3 etwas näher anzuschauen. Und es sollte sich lohnen, d3 rockt! Wirklich! Mit sehr wenig Aufwand, etwas Svg und Javascript hatte ich in einigen Stunden eine beeindruckende Visualisierung der Struktur eines sehr großen Source Code Repositories. Das Layouting und Rendering erledigt ein moderner Browser (IE9, Chrome, Safari oder Firefox) in wenigen Millisekunden. Ähnliches mit z.B. Graphviz und WPF als Rendering Engine zu implementieren wäre sehr viel aufwendiger, langsamer und unansehnlicher gewesen. Und da d3 im Browser läuft, ist die Visualisierung gleich plattformübergreifend verfügbar. Begeistert von den ersten Ergebnissen habe ich ausgehend von der ersten Spielerei gleich einige reale Probleme visualisiert. So sieht's heute aus:

Mein Arbeitgeber betreibt eine Website auf Basis von .NET und C#, an deren Entwicklung und Wartung sieben Teams seit einigen Jahren beteiligt sind. Dementsprechend viele Projekte gibt es im Repository. Große Projekte, kleine Projekte, wiederverwendete Projekte, exotische Projekte, tote Projekte, Jobs, Services und Web Anwendungen. Im Laufe der Zeit ging irgendwann die Übersicht verloren, welche Projekte wo benutzt werden, welches Team für welches Projekt zuständig ist, gegen welches Framework ein Projekt kompiliert, was eine gemeinsam genutzte  Komponente ist und was eine eigenständige Applikation sein sollte.
Mein Ziel war es, eine stark komprimierte Übersicht über die Projektstruktur zu geben und dabei die erwähnten Attribute zu visualisieren. Ursprünglich wollte ich dazu Treemaps verwenden, doch bei der Durchsicht der d3 Beispiele stieß ich auf Bubble Charts und Circle Packing die ich visuell ansprechender fand und die nach eigener Beschreibung "can pack hundreds of values into a small space" and "circle packing is not as space-efficient as a treemap, but it better reveals the hierarchy".
In der von mir verwendeten Variante wird jedes Projekt als ein Kreis ("Bubble") dargestellt. Jeder Kreis hat einen Namen, eine Größe (Durchmesser) und eine Farbe. Die Position der Bubbles innerhalb der Grafik ist durch den Packing Algorithmus bestimmt und kann nur in engen Grenzen (Sortierung) beeinflusst werden. Dies ist jedoch nicht schlimm, weil die Projekte innerhalb einer hierarchischen Verzeichnisstruktur liegen. Verzeichnisse werden ebenfalls als Kreise dargestellt, wobei geschachtelte Verzeichnisse und Projekte im übergeordneten Kreis angeordnet werden (siehe Bild). Damit werden in der Hierarchie zusammengehörende Projekte auch räumlich nahe beieinander dargestellt. Im obigen Beispiel sind die Verzeichnisse blau gefärbt, je tiefer die Verschachtelung, desto tiefer der Farbton. Die Bubble Attribute Größe und Farbe können dynamisch zur Visualisierung jeweils zweier Projekteigenschaften verwendet werden. Die Größe des Kreises kann beispielsweise die Größe des Projekts (in Bytes), die Anzahl der Abhängigkeiten oder die Anzahl der Verwender visualisieren. Jedes numerische Attribut kann auf den Durchmesser gemappt werden. Diskrete Werte wie z.B. Team Zugehörigkeit, .NET Version oder Projekttyp können besser durch Farben visualisiert werden.
Nun gibt es noch die Abhängigkeitsbeziehungen zwischen den Projekten. Das Managen dieser Abhängigkeiten ist eine 'viel geliebte' Aufgabe aller Architekten und wird häufig als Dependency Graph oder Dependency Matrix visualisiert. Die Visualisierung der Abhängigkeiten war nicht mein Primärziel, aber oft ist diese Information sehr nützlich. Ich experimentierte daher mit Animationen um diese Beziehungen parallel darzustellen. Ein Klick auf ein Projekt lässt alle Projekte blinken, die von dem angeklickten Projekt verwendet werden. Hält man beim Klicken die Strg Taste gedrückt, blinken alle Projekte die das angeklickte Projekt verwenden. 

Warum eigentlich ...

mache ich mir die Mühe dieser Visualisierung? Nun, ich kann alle Projekte und mehrere Eigenschaften gleichzeitig sehen. Durch geschickte Farbgebung, z.B. rot für ungesunde Eigenschaften, stechen selbst kleine Probleme optisch heraus. So kann ich jeden Tag mit einem Blick Änderungen in der Projektstruktur feststellen. Ein anderer Anwendungsfall war die Umstellung auf .NET4: alte Projekte sind rot, umgestellte sind grün. So lässt sich der Fortschritt leicht beobachten, und wenn alle Projekte umgestellt gibt es nur noch grüne Kreise. Neue hinzugekommene Projekte, die mit einem alten Framework angelegte wurden, lassen sich so sofort erkennen. Insbesondere für die interaktive Erforschung der Projektstruktur und dem Finden von nicht offensichtlichen Abhängigkeiten ist die Verbildlichung für mich als visuell denkenden Menschen sehr hilfreich. Dazu müssen natürlich auch ad hoc neue Eigenschaften visualisiert werden können, und dafür ist Flexibilität von d3 im Zusammenspiel mit Web Standards außerordentlich hilfreich.

Die Technik

Ein kleines C# Programm analysiert die Projektdateien (*.csproj files) und erstellt ein json Datenobjekt, dass die Verzeichnisstruktur, den Abhängigkeitsgraph sowie die Projekteigenschaften enthält (Größe, Typ, Team, .NET Version). Einige redundante Informationen wie leere Verzeichnisse oder Namens Prefixe werden in diesem Schritt entfernt. Das Datenobjekt misst unkomprimiert ca 120KB, und wird in ca 3s aus den Sourcen extrahiert. Dieses Objekt wird einmal pro Tag erzeugt und in eine statische WebSite kopiert. Das Javascript bubble.js übernimmt die Visualisierung im Browser. Dazu werden mit d3.js die Daten gelayoutet (d3.layout.pack()) und in SVG Elemente transformiert. Dies geschieht in wenigen Milisekunden. SVG bietet den Vorteil des flüssigen Zoomens und die grafischen Objekte bleiben als DOM Elemente erhalten und könnem mit Standard Events, wie MouseOver oder Clicks gescriptet werden. Daher ist ein moderner Browser mit SVG Unterstützung notwendig, z.B. Chrome, Safari, Firefox oder IE9. Erstaunlicherweise hat aktuell der IE9 die beste SVG Unterstützung, Chrome und Safari haben gröbere Probleme mit dem Textzooming und dem Hittesting. Firefox funktioniert gut, hat aber die schlechteste Performance. Das Javascript ist nach dem AMD Standard modularisiert, als AMD Loader wird mmd.js verwendet. Da ich zu Beginn die Funktionsweise des Circle Packing noch nicht genau kannte und auch noch nicht exakt wusste, welche Eigenschaften sinnvoll  visualisiert werden konnten, existiert ein kleines User Interface (Standard Html) mit dem verschiedene Parameter geändert werden können. D3 animiert solche Änderungen automatisch mit eingebauten Transitionen. Z.B. könnte man damit die Veränderung des Repositories im Zeitverlauf animieren. Dies könnte ein weiteres Hobby Projekt von mir werden ... stay tuned ...

29 March 2012

HTML5 without warnings in Visual Studio

Today I was annoyed about warnings that Visual Studio shows when editing an html5 file. Example: VS expects a type attribute inside the script tag but html5 doesn't require it anymore (because it defaults to javascript).

When opening the context menu I noticed the "Formatting and Validation" item and opened it:

html5_validate

Choosing "HTML5" as a target removes all those annoying wrong warnings :-)

19 January 2012

The best fellow employee ...

... is someone that you can criticize without pissing him off. The same should be true the other way round. I'm glad that I'm working for an exceptional company where nearly everyone has this attitude.

27 November 2011

Speed up your build with UseHardlinksIfPossible

MsBuild 4.0 added the new attribute "UseHardlinksIfPossible" to the Copy task. Using Hardlinks makes your build faster because less IO operations and disk space is needed (= better usage of file system cache). What's best is that this new option is already be used by the standard .net build system! But Microsoft decided to turn them off by default.

After searching a little bit in the c# target files I found out how to turn this feature globally on and my build was 20% faster than before. And if you have big builds with more than a hundred projects this counts!

So here comes the way to turn on hard linking in your build. First, this works only with NTFS. Second you have to explicitly set the ToolsVersion to 4.0. You can do this with a command line argument (msbuild /tv:4.0) or inside the project file (<Project DefaultTargets="Build" ToolsVersion="4.0" ...).

Then you have to override the following properties with a value of "True":

  • CreateHardLinksForCopyFilesToOutputDirectoryIfPossible
  • CreateHardLinksForCopyAdditionalFilesIfPossible
  • CreateHardLinksForCopyLocalIfPossible
  • CreateHardLinksForPublishFilesIfPossible

Use command line properties (msbuild /p:CreateHardLinksForCopyLocalIfPossible=true) to override them for all projects in one build. Or you can create a little startup build file that collects all projects and set the properties in one place. Here is mine:

Code Snippet
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >

  <PropertyGroup>    
    <Include>**\*.csproj</Include>
    <Exclude>_build\**\*.csproj</Exclude>
    <CreateHardLinksIfPossible>true</CreateHardLinksIfPossible>
  </PropertyGroup>

  <ItemGroup>
    <ProjectFiles Include="$(Include)" Exclude="$(Exclude)"/>
  </ItemGroup>

  <Target Name="Build" >
    <MSBuild Projects="@(ProjectFiles)" Targets="Build" BuildInParallel="True" ToolsVersion="4.0"
             Properties="Configuration=$(Configuration);
                         BuildInParallel=True;
                         CreateHardLinksForCopyFilesToOutputDirectoryIfPossible=$(CreateHardLinksIfPossible);
                         CreateHardLinksForCopyAdditionalFilesIfPossible=$(CreateHardLinksIfPossible);
                         CreateHardLinksForCopyLocalIfPossible=$(CreateHardLinksIfPossible);
                         CreateHardLinksForPublishFilesIfPossible=$(CreateHardLinksIfPossible);
                         " />    
  </Target>
  
</Project>

06 August 2011

Feature Toggle Status Page

If a website uses Feature Toggles it should have some diagnostic page that displays the the current configuration and state of all toggles. On our Feature Toggle status page it is even possible to explicitly switch a toggle ON or OFF with one click. Another feature that I like is, that you can quickly see toggles that are still in use but nowhere configured. This is an indication that it was forgotten to remove some code.

image

More cool Feature Toggle Features

We have extended our Feature Toggle implementation with two more features:

  1. an activation interval that determines in advance when a toggle is on or off
  2. a toggle that is only enabled for a certain rate of visitors

We release new versions every two weeks and sometimes the activation of a feature is coupled to a fixed date by marketing or legal reasons. You could argue that the feature itself should implement the date checking but it was attractive to use the existing Feature Toggle infrastructure. Our existing implementation could be easily extended to support this, but I admit that it added complexity and that the implementation is now a little bit impure in the sense that it is now no longer exclusively used as a development tool.

The Feature Toggle configuration is still simple and readable. Everything except the name attribute is optional so you have to specify only the borders you need:

<toggle name="myFeature" enabled="true" /> 
<toggle name="myFeature" enabled="true" from="2012-01-01" /> 
<toggle name="myFeature" enabled="true" to="2012-06-01" /> 
<toggle name="myFeature" enabled="true" from="2012-01-01" to="2012-06-01" />

The second feature was inspired by the allowHttpOverride property original developed for testing purposes. If a user visits our page the toggle decides randomly with the given rate if the toggle is on or off. The result is stored inside a browser cookie and never changed. So it works for features that span several pages or workflows. The rate feature is extremely useful if you plan to do canary releases for only a few percent of your users. If everything works fine you can slowly increase the rate until it reaches 100%.

Every toggle attribute can be combined with every other attribute, e.g. a toggle that is on only for a specific interval with a specific rate and overriding it per cookie or query parameter is not allowed:

<toggle name="myFeature" enabled="true"  
        from="2012-01-01" to="2012-06-01" rate="42" allowHttpOverride="false" />

26 June 2011

A Cool 'Feature Toggle' Feature

Recently we gave our Feature Toggles the possibility to be overridden at runtime. For a web application I found this extension quite useful and its implementation was easy and straightforward. This is the configuration for toggles that are always on or off:

<featureToggles>
    <toggle name="aFeature" enabled="true" />
    <toggle name="anotherFeature" enabled="false" />
</featureToggles>

If I want that the toggle can be overridden at runtime I just add one additional attribute:

<featureToggles>
    <toggle name="aFeature" enabled="true" allowHttpOverride="true"/>
    <toggle name="anotherFeature" enabled="false" allowHttpOverride="true"/>
</featureToggles>

Now if a visit a page, say www.atombrenner.de/test,  I can modify the url to be www.atombrenner.de/test?aFeature=off and now for the complete request the toggle is off. For workflows that cover more than one page request we use cookies. Just create a new cookie with your favorite cookie manager (I like the 'Edit this cookie' plugin for Google Chrome very much).

image

The name of the cookie must be the name of the toggle you want to override. Allowed values are: 0, 1, off, on, false, true. If both a query string value and a cookie value is given, the query string parameter wins.

If find this feature extremely useful because it allows you to toggle very quickly the feature on or off, without the need for recompiling or reconfiguring. This enables you

  • to always test that your app is still stable when the new feature is turned off
  • to give testers or managers a quick preview of the feature during development
  • to provide external partners a preview of the feature in the live environment, so they can test and accept features before it goes public.