Wie gesund ist eigentlich mein Code?

 

software quality metric: A function whose inputs are software data and whose output is a single numerical value that can be interpreted as the degree to which software possesses a given attribute that affects its quality.

 Definition nach IEEE Standard 1061 – [Quelle: Wikipedia]

Jede Software, deren Code länger als ein paar hundert Zeilen ist, wird irgendwann den Punkt erreichen, dass man den Code auf Anhieb nicht mehr verstehen kann. Die meisten von uns schreiben Code, der älter als ein paar Monate ist und noch ganz viele Jahre erhalten bleiben soll. (Alle, die das nicht wollen, können hier aufhören zu lesen).

Das Problem, das man früher oder später bekommt, ist die Komplexität unter Kontrolle zu halten. Jeder neuer Kollege hat das Problem, unbekannten, vorhandenen Code so schnell wie möglich zu verstehen. Für beide Fälle ist es sehr hilfreich, wenn man Tools zur Hand hat, die zum Beispiel die Zusammenhänge und Abhängigkeiten visualisieren können.

Als ich bei dem Open Space Karlsruhe die Frage gestellt habe, was die .NET-Community zu diesem Zweck nutzt,war die einstimmige Antwort : NDepend.  Code Metriken sind wichtig, sie sind aber nicht allmächtig. Wenn man allerdings wissen möchte, wie gesund sein Code ist, was sich verschlechtert hat und welche Baustellen aufgeräumt wurden, dann ist NDepend das de facto Standardtool, welches benutzt wird.

Was macht das Tool eigentlich?

Um all die Features zu beschreiben, die NDepend hat, würde man sehr viel Platz und Zeit benötigen – und zum Glück ist dies gar nicht nötig: auf deren Webseite findet man alles, was man braucht: Bilder, Erklärungen, weiterführende Links.

Ich würde hier nur zwei wichtige Funktionalitäten herausheben:

  • Visualisiert

MVC-Runtime Dependency Graph
Abhängigkeiten im MVC-Runtime

 

Auf diesem Bild sieht man, dass man gar nichts sieht 😀

Stellt euch mal vor, ihr müsstet ab sofort an MVC weiterentwickeln. Wo würdet ihr anfangen? Ich würde hiermit beginnen und immer mehr reinzoomen.

Alle Verwender von DotNetOpenAuth.OpenId

 

  • Erklärt

Das coolste für mich bei NDepend ist eigentlich nicht die Tatsache, dass es mir Statistiken und Grafiken liefert, sondern, dass es sie mir Diese auch  erklärt!

 

Interne Abhängigkeiten von DotNetOpenAuth.OpenId

 

Genau so läuft es auch mit den Metriken. Ich will nicht wissen, wie diese berechnet werden – eventuell später –  aber ich will wissen, was es bedeutet, wenn ein Wert zu hoch oder zu klein ist. Und das Tool erklärt dies alles oder leitet mich gezielt dahin weiter, wo es erklärt wird. Und so, ohne es zu merken, habe ich etwas gelernt, was meine Codequalität höchstwahrscheinlich erhöhen wird. Ich kann dadurch ein besserer Programmierer werden.

Es gibt noch sehr viele Gründe, wofür man NDepend ausprobieren bzw. nutzen sollte. Spätestens, wenn ein Team sich für gemeinsame Regeln einigen möchte, sollte man die Einhaltung durch Tools wie dieses und StyleCop and co. absichern. Dadurch wird irgendwann egal, wie ungesund unserer Code heute ist, morgen wird es ihm auf jedem Fall besser gehen – und uns auch.

Create class from JSON

Ich habe vor einem Jahr bei einer katastrophalen MSFT-Veranstaltung über ein super Feature in VS2012 erfahren, das ich – brownfield-bedingt – noch nie ausprobieren konnte. Heute hat es mir allerdings mindestens eine halbe Stunde Arbeit und eine große Menge Frust erspart!

Das Feature ist sehr schnell erklärt: wenn man ein Objekt, das als JSON oder XML serialisiert vorliegt – also einen string 😉 – in Zwischenspeicher hat, dann kann man das direkt als Klassenmodel abspeichern. Tatata..Fertig 🙂
Edit/Paste Special

Templating mit Razor aber ohne MVC

Ich habe unlängst nach einer Möglichkeit gesucht, Seiten schnell und effektiv zu generieren, sowohl für Web als auch für E-Mails als Background-Jobs. Ein Kollege hat mich auf @razorengine aufmerksam gemacht und diese Templating Engine scheint alles zu bieten, was ich gesucht habe.

A templating engine built upon Microsoft’s Razor parsing technology. The RazorEngine allows you to use Razor syntax to build robust templates. Currently we have integrated the vanilla Html + Code support, but we hope to support other markup languages in future.

Die Installation ist so einfach wie möglich:

//mit NuGet:
Install-Package RazorEngine

Man muss danach nur noch die RazorEngine.dll und System.Web.Razor.dll referenzieren und das war’s.

 

Das Generieren von Seiten kann entweder direkt oder mit vorkompilierten Templates erfolgen:

[Test]
public void PageCanBeParsed()
{
   const string template = "Hello @Model.Name! Welcome to Razor!";
   var result = Razor.Parse(template, new {Name = "World"});

   Assert.That(result, Is.EqualTo("Hello World! Welcome to Razor!"));
}
//unterstützt anonyme Klassen
[Test]
public void PageCanBeParsedWithCompiledTemplate()
{
   const string template = "Hello @Model.Name! Welcome to Razor!";
   Razor.CompileWithAnonymous(template, "world");
   var result = Razor.Run(new {Name = "World"}, "world");

   Assert.That(result, Is.EqualTo("Hello World! Welcome to Razor!"));
}
//oder konkrete Typen
[Test]
public void TemplateIsCompiledWithModelType()
{
   const string template = "Hello @Model.Name! Welcome to Razor!";
   var testModel = new TestModel{ Name = "World" };
   Razor.Compile( template, typeof(TestModel), "world2" );
   var result = Razor.Run(testModel, "world2");
   Assert.That(result, Is.EqualTo("Hello World! Welcome to Razor!"));
}

public class TestModel { public string Name; }

Um ehrlich zu sein, ich habe noch keinen Grund gefunden, warum man nicht die vorkompilierte Variante nutzen soll. In diesem Fall wird das Template EIN MAL kompiliert und in Cache abgelegt. Ein Vergleichs- und Stresstest, in dem ich ein halbwegs komplexes Template 500-mal mit Razor.Parse bzw. mit Razor.Compile + Razor.Run aufgerufen habe, hat folgendes geliefert:

   Parse: 03:35.97 min
   Compile+Run: 00:00.63 min

Ich glaube, damit ist alles gesagt. Es sei denn, eine Zeile weniger gilt auch als Argument 🙄

 

RazorEngine unterstüzt fast alles, was Microsoft’s Razor in Views anbietet, wie zum Beispiel Helper-Methoden, Model-Definitionen direkt im Template oder partielle Views:

[Test]
public void EngineSupportsInlineHelper()
{
   const string template = @"@helper MyMethod(string name) {Hello @name}@MyMethod(Model.Name)! Welcome to Razor!";
   var testModel = new TestModel{ Name = "World" };
   var result = Razor.Parse(template, testModel);
   Assert.That(result, Is.EqualTo("Hello World! Welcome to Razor!"));
}

[Test]
public void EngineSupportsInheritsCommand()
{
   const string template = @"@inherits RazorEngine.Templating.TemplateBase
@helper MyMethod(string name) {Hello @name}@MyMethod(Model.Name)! Welcome to Razor!";
   var testModel = new TestModel{ Name = "World" };
   Razor.Compile(template, typeof(TestModel),"testModel");
   var result = Razor.Run(testModel, "testModel");

   Assert.That(result, Is.EqualTo("Hello World! Welcome to Razor!"));
}

[Test]
public void EngineSupportsSubtemplating()
{
    const string masterTemplate = "You are on www.yellow-brick-code.org!";
    Razor.Compile(masterTemplate, typeof(TestModel), "master");

    const string contentView = @"@inherits RazorEngine.Templating.TemplateBase
@helper MyMethod(string name) {Hello @name}@MyMethod(Model.Name)! Welcome to Razor!";
    var testModel = new TestModel{ Name = "World" };

    const string template = contentView + " @Include(\"master\")";

    Razor.Compile(template, typeof(TestModel), "testModelMaster");
    var result = Razor.Run(testModel, "testModelMaster");
    Assert.That(result, Is.EqualTo("Hello World! Welcome to Razor! You are on www.yellow-brick-code.org!"));
}

Und nun sind wir soweit, das Ganze im Web zu testen.

 

Die Engine funktioniert so, dass sie zur Laufzeit aus dem Template eine dll mit einem Zufallsnamen erstellt. Also wenn man die Engine im Web nutzen will, müssen noch ein paar Dinge getan werden:

  1. RazorEngine.Web.dll referenzieren
  2. Ein VirtualPathProvider in Global.asax.cs registrieren
    public class Global : System.Web.HttpApplication
    {
       protected void Application_Start(object sender, EventArgs e)
       {
          HostingEnvironment.RegisterVirtualPathProvider(new RazorVirtualPathProvider());
       }
    ...
    
  3. In Web.Config muss der BuildProvider registriert werden:
    <configuration>
    <configSections>
    <section name="razorEngine" type="RazorEngine.Configuration.RazorEngineConfigurationSection, RazorEngine" requirePermission="false" />
    </configSections>   <razorEngine factory="RazorEngine.Web.WebCompilerServiceFactory, RazorEngine.Web" /><system.web>
    <compilation debug="true" targetFramework="4.0">
    <buildProviders>
    <add extension=".csrzr" type="RazorEngine.Web.CSharp.CSharpRazorBuildProvider, RazorEngine.Web" />
    </buildProviders>
    </compilation>

So aufgerüstet kann man mit den Models und Templates beginnen. Man kann die Templates als html-Dateien speichern. Allerdings wenn man IntelliSense haben möchte, dann muss MVC3 auf den Rechner installiert und die Datei als cshtml gespeichert werden.

 

Das Beispiel hier ist über ein MasterHeader mit dem Anfang der Seite, ein MasterFooter mit dem Ende, beide eingebettet mit " @Include(\"master...\")" in Content. Die ganze Seite bekommt ein Objekt vom Typ Model zum Parsen. Alle Templates werden mit einem ITemplateFinder geladen.

...
ITemplateFinder templateFinder = new TemplateFinder(path);
Razor.Compile(templateFinder.GetTemplate("masterHeader.cshtml"), typeof(Model), "masterHeader");
Razor.Compile(templateFinder.GetTemplate("masterFooter.cshtml"), typeof(Model), "masterFooter");

var model = new Model
{
    PurchaseNo = "011313074142",
    Amount = "270.63",
    Date = "20110121"
};

var template = templateFinder.GetTemplate("Content.cshtml");
Razor.Compile(template, typeof(Model), "content");
var parsedTemplate = Razor.Run(model, "content");
context.Response.ContentType = "text/HTML";
context.Response.Write(parsedTemplate);

Bevor ihr was über Namen oder Verantwortlichkeiten was sagt: das Projekt wurde als Spike erstellt, und als solche hat seine Rolle  perfekt erfüllt 🙂 In Produktion würde ich das Kompilieren von statischen Templates in Application_Start verschieben und das ITemplateFinder sollte auf  jeden Fall Injected werden.

 

Ich muss mich bei den 2 Jungs, die das Projekt entwickeln, sehr bedanken, es war eine super Idee! Schaut es einfach an, die dll kann noch viel mehr.

Tausende Codezeilen verstehen – aber wie?

Nach 7 Wochen Zwangsurlaub (siehe unten) bin ich wieder back to life: mit einem halbwegs neuen Bein (mit Titaneinlagen und Schlitzschrauben 😉 ), in einer neuen Stadt mit 100%-er Snowcoverage, in einem neuen Job – nach einem Monat Verspätung.

Das letzte Mal, als ich bei einer Firma den Code verstehen musste, ging es um ASP-Classic. Die Abhängigkeiten waren überschaubar, das Debugen ging mit Response.Write-Zeilen ;). Aber wie macht man das heute, wie versteht man den bestehende Code, der in vielen Jahren aus den fleißigen Fingern der Programmierern herausgeflossen ist? Und das im Web, in einer unglaublich flexiblen E-Commerce-Anwendung…

Die Lösung war einfach: mit Unit-Tests ! Ich musste nicht erklärt bekommen haben, WAS der Code tut, nur welche Aufrufe zu welchen Ergebnissen führen. Immer, wenn ich ein Szenario verstanden habe, wurden dafür Tests geschrieben und das Ergebnis besprochen. Der Begriff unit wurde teilweise natürlich ausgedehnt, aber das hat nichts an der Tatsachen geändert, dass am Ende

  1. ich den Code verstanden habe
  2. die meisten analysierten Zeilen durch einen Test abgedeckt wurden
  3. die Diskussionen über den Testnamen dazugeführt haben, dass manch unnötige Zeilen (lese “Szenarien”) entfernt wurden, also der Code besser geworden ist

Ich wurde getestet … und ganz gut gefunden!

Ich habe zufällig auf dem Blog von Gordon Breuer eine Webseite entdeckt, wo man seine Kenntnisse teilweise kostenlos testen kann. Ich habe mir den kostenlosen Test “C# 3.0 Fundamentals” ausgewählt und jetzt kann ich hier ganz stolz das Ergebnis melden:

C# 3.0 Fundamentals

Scored higher than 64% of all previous test takers.

Demonstrates a clear understanding of many advanced concepts within this topic. Appears capable of mentoring others on most projects in this area.

Das ist aber ein super Gefühl 🙂
Hier die Bewertung der Punkte:

How do I interpret my Brainbench test score?

Scores range from 1-5 with 5 being the highest in the Brainbench
scoring system. Within that range, Brainbench recognizes two levels
of achievement for standard assessments. A score between 2.75
– 3.99 earns the test taker the standard certification while
a score of 4.0 or higher certifies a master level of achievement.

Scores range from 1 to 5 and are divided into the
following proficiency level categories:

Test Overall Score
Range
Proficiency
Level
1.00 – 1.50 Novice
1.51 – 2.50 Basic
2.51 – 3.50 Proficient
3.51 – 4.50 Advanced Master
4.51 – 5.00 Expert

Während ich den Test gemacht habe, musste ich allerdings ziemlich oft wegen Resharper fluchen, weil der ja die ganze Arbeit macht, wenn es um sowas geht a = b ?? 0; aber bei Multiple Choice hat man ja kein Resharper 😆  (hallo René …)