tag:blogger.com,1999:blog-64255302024-03-13T09:31:46.280+01:00Martin R-L's Programming Oriented AspectsRandom thoughts about every aspect of software development.MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.comBlogger35125tag:blogger.com,1999:blog-6425530.post-39330270226732136812012-03-19T23:21:00.001+01:002014-11-30T16:08:42.736+01:00QCon London 2012 Retrospective/Post-mortem<b>Intro</b>
<br/>
This was my second visit to QCon London. My impression of the conference, based on these two visits, is that it tries to focus more on the bigger issues such as architecture and management, and less on coding details (although present). An example would be that a dedicated .net track wasn't present, despite the platform's popularity. øredev, which I've visited basically every year, does the opposite and focuses more on hands-on coding, but let this be about QCon.
<br/>
<br/>
I didn't really learn anything new at the conference, but was rather strengthened in what I've come to hold valuable regarding sustainable, high quality software development over the years (due to experience, theory, or both). I had the opportunity to chat with some of the speakers, since QCon is organized with social events to facilitate that, and was even further convinced by that.
<br/>
<br/>
<br/>
<b>Architecture</b>
<br/>
One big trend was splitting up larger applications and web sites in "systems of systems" and software architecture on both micro and macro levels. The latter being between smaller systems, hence more stable and important, while the inner workings of the smaller systems is of much less importance.
<br/>
<br/>
"What's simpler than one thing? Two!" meaning that small systems are much easier to modify than large.
<br/>
<br/>
<a href="https://twitter.com/#!/bruntonspall">Michael Brunton-Spall</a> gave a talk named <a href="https://docs.google.com/viewer?url=http%3A%2F%2Fqconlondon.com%2Fdl%2Fqcon-london-2012%2Fslides%2FMichaelBrunton-Spall_ArchitectingForFailureAtTheGuardianCoUk.pdf">Architecting for Failure</a>, where he described how The Guardian had re-architectured their whole web site from a huge monolith, into a lot of smaller systems. The only thing they had standardized on, was the JVM. Not only are they more robust and flexible nowadays, but innovation has increased a lot due to the change. Now they code, release, and test small bits in isolation.
<br/>
<br/>
So why is it then, that we're stuck with large monolithic applications, that are hard to modify? The main objectives change over time. First, we value things like ease of development, homogeneity, cohesion (one big sln-file to rule them all) and simplicity. Over time we come to value things like heterogeneity, decoupling, modularity and autonomy.
<br/>
<br/>
In one talk it was said that there's always a lot of management reasons to not change the architecture from the former to the latter, despite it being a good idea. I guess it's easier to identify the risks, than the opportunities that comes thereof.
<br/>
<br/>
<br/>
<b>Software Engineering</b>
<br/>
Since I'm a big fan of <a href="http://www.growing-object-oriented-software.com/">GOOS</a>, and working in a <a href="http://xunitpatterns.com/need-driven%20development.html">need-driven</a> fashion, it was great to listen to <a href="https://twitter.com/#!/sf105">Steve Freeman</a> et.al., describing a team delivering high quality software every second week for seven consequitive years. How? Using most practices from XP and making sure every feature has executable acceptance tests (or specification by example, as it's commonly called these days due to <a href="http://manning.com/adzic/">Gojko Adzic's book</a>) and time isn't more important than getting the requirements right. Last, but not the least, the team made sure that new hires not only had high technical qualifications, but also shared the same mindset in order to fit in the culture.
<br/>
<br/>
Since they pair-program 100% of the code, turnover hasn't affected the speed of the team. Conclusion of XP and NDD according to them: cheap and reliable.
<br/>
<br/>
I think <a href="https://twitter.com/#!/robbowley">Rob Bowley</a> summarized it great, with the quote "It’s the code, stupid". He went on with details on the subject, but this one stands out: "clean, maintainable, well tested code [is] more important than anything else you do". One can read <a href="https://docs.google.com/document/d/1IN8e3UNvR1oRlvT9b_1gKHNkBE2E6fEx_dVSD3p--6g/edit">his notes from the talk</a> for more info.
<br/>
<br/>
<br/>
<b>Testing</b>
<br/>
Context is king. Great results seem to come from teams doing SBE and TDD. However, if you're a part of the tiny fraction of the software industry creating services like social networks and games, and the like, where the service is free or very cheap, and the impact of bugs isn't that big; you can let the end users test your software for you (logs, feedback links, forums, etc.) in order to produce the software faster and cheaper.
<br/>
<br/>
This also addresses the biggest problem these services have; what's the right product? When working with SBE in a traditional setting, the pre-requisit is, that you via specification workshops, domain experts, or something else can capture pretty much exactly what you're supposed to develop. If this simply isn't possible, you have to let your user-base guide you.
<br/>
<br/>
<br/>
<b>Management</b>
<br/>
In his talk Agile Adoption is Fool's Gold (and other stories from the coal face), <a href="https://twitter.com/#!/robbowley">Rob Bowley</a> said what we've known for years: SCRUM == training wheels, while you're better off with Kanban, when you're mature enough (you can't go there directly).
<br/>
<br/>
DevOps? Yes! <a href="https://twitter.com/#!/tastapod">Dan North</a> told a great story about how they had managed developers and operations personal to work together, and how beneficial that had been. So much low hanging fruit!
<br/>
<br/>
<a href="https://twitter.com/#!/jodi_p_moran">Jodi Moran</a> took it one step further, and said <a href="http://www.slideshare.net/plumbee/sustainable-speed-is-king-qconlondon-2012">"operations is dead"</a> which was clarified with "When all engineers operate the system, the constraints and requirements of the operational environment will always be taken into account", and two other statements.
<br/>
<br/>
If you want to change anything, telling people what to do will most likely fail. Make them want to go where you want them to. How? Prove that it makes sense. Pair program. Action! "A little less conversation a little more action"
<br/>
<br/>
<a href="https://twitter.com/#!/holman">Zach Holman</a>'s talk <a href="http://zachholman.com/posts/how-github-works/">How GitHub Works</a> was very inspirational, despite that I've read all about it before. Zach is a very professional coder and speaker, who backs his story with real success. He basically tells the story of a programmer's heaven. No meetings. At. All. No managers (they distract). No estimates and deadlines. No time keeping. At. All. ("Hours are BS!", "Creating code is a crative endeavor!", "Embrace flexibility!", "Working long hours <i>isn't</i> a badge of honor!" and "Marathons drains you mentally == shit code!") Family-friendly. Everything is optimized for happiness :-) We want the best work, and get it when people are happy. (BTW, check out <a href="http://www.youtube.com/watch?v=u6XAPnuFjJc&sns=fb">the RSAnimate</a> about why that's a great idea.) 8000 tests in 250 seconds (slow test considered regression). Pair program scary code, and with new hires. Only hire really good people. The list goes on... and it feels good to be an enterprise customer of GitHub.
<br/>
<br/>
<i>Everybody</i> talking on the subject of management emphasized the importance of hiring only the best people. I wholeheartedly agree, but I've never hired someone with money from my own pocket, so I haven't really put my money where my mouth is on this one... If the theory and evidence on this subject shows great results, why isn't it done more often? I've even had a boss, who said "we don't need good developers here"... If you're interested in the subject, <a href="http://blogs.hbr.org/schrage/2011/06/why-zuckerberg-is-almost-right.html">Why Zuckerberg Is (Almost) Right About Great Talent</a> is a good article IMHO.
<br/>
<br/>
In another talk, <a href="https://twitter.com/#!/tastapod">Dan North</a> talked about how every decision is a trade-off, and concluded that "there are no best-practices". Context <i>is</i> king.
<br/>
<br/>
<br/>
<b>London by Night</b>
<br/>
A big <3 to my colleagues, with whom I spent some really fun London nights with!MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-38893945371378963012011-08-22T16:37:00.005+02:002011-08-24T08:31:01.389+02:00Goodbye _, Hello R# Color IdentifiersI've been prefixing my C# class members with _ (underscore) since the first version of C#. I really do like to be able to distinguish between instance and function scope. <br />
<br />
I've also obeyed the larger part of the classic <a href="http://www.idesign.net/idesign/download/IDesign%20CSharp%20Coding%20Standard.zip">IDesign Coding Standard</a> for who knows how long.<br />
<br />
Therein, rule #67 states: "Do not use the this reference unless invoking another constructor from within a constructor." Usually, you'll use the same name for your fields and constructor arguments, why a this reference is needed if the fields aren't prefixed.<br />
<br />
While re-reading the great book <a href="http://books.google.com/books/about/Clean_code.html?id=dwSfGQAACAAJ">Clean Code authored by Uncle Bob</a> today, I came across the following statement on page 24: "You […] don't need to prefix member variables […] anymore. […] you should be using an editing environment that highlights or colorizes members to make them distinct.".<br />
<br />
The argument appealed to me, but how should I follow that advise? As a C# programmer, you're more or less forced to use Visual Studio. I knew, despite using ReSharper (R#) since its first version, that I've never come across field/member highlightning in the Fonts and Colors section.<br />
<br />
After some googling, I finally found what I was looking for in R#; you need to <a href="http://www.jetbrains.com/resharper/features/coding_assistance.html">enable Color Identifiers</a>. Once I'd done that, the "ReSharper Field Identifier" (and more) show up among the display items in the aforementioned Fonts and Colors section.<br />
<br />
Goodbye _, hello color identifiers!<br />
<br />
Now I need to write a refactoring regex that refactors old, prefixed code…<br />
<br />
<br />
UPDATE 2011-08-24: I've now refactored two C# solutions where I got rid of the prefixes by using VS' find and replace with regex and capture groups like so:<br />
find what: private {.*} _{.*}<br />
replace with: private \1 \2<br />
and similar expressions.MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com1tag:blogger.com,1999:blog-6425530.post-3682593664019109712011-06-28T22:57:00.002+02:002011-06-28T22:59:22.698+02:00My Take on Recruitment in Three Tweets<div class="separator" style="clear: both; text-align: center;"><a href="#" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"><img border="0" height="74" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAeGZvhr-n6Ps4DS0LIdJMtJVmjwaw7xVqGZTpXh3996Ql9BjYcDyHsYq_oYBuDe0i5Fmd6IrQtOx4GuvirVeAilS-33VRv9v0OPRVMYwNdTsb3pZJmob2WHDjt1n4Yiuo_JI74w/s400/frans.PNG" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://blogs.hbr.org/schrage/2011/06/why-zuckerberg-is-almost-right.html" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"><img border="0" height="73" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3mSPYAXjnpHFMKGOnkIQ_1pJ2HLw5cN6cu5hjHofP_5PfQF_ZDWfWsyFcZipVz45fa-v1ncKwGlueoWYnxXwjQfACaw6cPMYGsWjAmvKt5V4_2FKwkJp6ULeFvnPgzcBwB-i2Aw/s400/harvard.PNG" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://blogs.hbr.org/taylor/2011/06/great_people_are_overrated.html" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"><img border="0" height="59" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZ8sH-AkNEYHxg-uPN_M67X7QvrZJg5YOQQQ2yW47-0H6A6ND2XrnLOeZLDSDjReb5bXKfHbvfibtfivAYMeZg71UWdX-VLQiWnbb1Mi1TE8TPu14c6-DMmgq4LB5b4UpJIcfQcw/s400/me.PNG" /></a></div>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-4621687680938442012011-05-02T23:35:00.004+02:002011-05-03T21:05:15.814+02:00Project Euler: 1 Down. 335 To Go!I signed up at <a href="http://projecteuler.net/">Project Euler</a> today in order to give myself some brain teasers.<br />
<br />
I set up a <a href="http://code.google.com/p/project-euler-csharp/source/browse/">Mercurial account at Google Code</a>, and solved the first, very basic, problem tonight.<br />
<br />
It was good fun, and now that I have the Visual Studio solution set up, I hope to solve more of these in my spare time.<br />
<br />
<span class="Apple-style-span" style="color: #cc0000;">Spoiler alert!</span><br />
<br />
The C# implementation can be found here: http://code.google.com/p/project-euler-csharp/source/browse/ProjectEuler/Problem001_Find_the_sum_of_all_the_multiples_of_3_or_5_below_1000.cs<br />
<br />
And the tests can be found here: http://code.google.com/p/project-euler-csharp/source/browse/ProjectEulerTests/Problem001_Find_the_sum_of_all_the_multiples_of_3_or_5_below_1000_Tests.csMartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-6090710367102309252011-03-16T22:20:00.003+01:002011-03-19T18:49:21.486+01:00Er tidens digitale teknologier en af årsagene til den øgede fascisme?Baggrunden til denne min første danske blogpost, er at jeg studerer dansk hos Bente Hahne ved Sprogcentrum. Hver gang vi mødes får jeg lektier, og herunder en skriveopgave. Denne gang er min opgave at skrive om <a href="http://www.e-pages.dk/politiken/5023/33">Kresten Schultz-Jørgensen's debatartikel i Politiken som fik rubrikken "Den sproglige fascisme breder sig" i Politiken 19. februar 2011</a>. Da "tidens digitale teknologier" spiller en stor rolle i denne, synes jeg at det giver god mening at benytte lige den slags til at lave denne lektie. <br />
<br />
Kresten ser med rædsel at det sproglige niveau er på nedtur bland de studerende ved CBS og andre universiteter, hvor han underviser og har været censor de seneste ti år. Dette på trods af at han mener at de samtidig er mere flittige og fokuserede end tidligere generationer.<br />
<br />
Personligt mangler jeg selvfølgelig Kresten's empiriske grundlag, men jeg har samme erfaringer (dog hovedsagelig i en svensk kontekst). Disse kommer fra at lytte til P3, unge i fjernsynet og i byen, snakke med min elleve år yngre søster og hendes venner, etcetera.<br />
<br />
Lige præcis som Kresten, frygter jeg at denne nedtur leder til flere fordomme og at der bliver flere synes, og færre af logisk underbyggede argumenter. "Færre ord, mindre råderum, flere fordomme. Dét er, som bekendt, fascisme." Det er med andre ord en utrolig alvorlig sag, som samfundet må bekæmpe! Hvis man er i tvivl om sprogets indflydelse på tanken, kan man bare læse <a href="http://www.modkraft.dk/sektion/kontradoxa/article/det-tredje-riges-sprog">Morten Things anmeldelse af Victor Klemperers Det Tredje Riges sprog</a>.<br />
<br />
Kresten anerkender dannelsekrisen i familien og skolen, men ønsker derefter at udpege "tidens digitale teknologier" som en stor grund til den gældende nedtur mht. sprogbrug og logisk argumentation. Af alle ydelser man kan tilgå i mobilnettet/på internettet, fokuserer Kresten kun på SMS, Facebook og Twitter. Disse er jo alle lige designede for hurtige, korte beskeder. Facebook er jo desuden et stort socialt netværk hvor idéen er at man skal hygge sig, dele feriefotos, musikvideoer, og tilsvarende. Dette er selvfølgelig ikke arenaen for dyb, sokratisk argumentation eller diskussion! Kresten, hvad mener du <b>egentlig</b>? (Man kan jo for søren også finde <a href="http://www.facebook.com/sprogpolitiet">Sprogpolitiet på Facebook</a>!)<br />
<br />
Hvad med blogrevolutionen hvor jeg selv læser mange blogs med meget god logisk opbygning og argumentation (især tekniske, men også <a href="http://johannorberg.net/">politiske</a>)? ... og hvad med alle <a href="http://atheism.stackexchange.com/">fora på nettet</a> hvor du ikke kommer ret langt ved kun at synes ting uden argumenter? Jeg mener at den digitale revolution vi er midt i, giver os mange flere muligheder for god, klassisk debat og udveksling af ideer, end nogensinde før. Lige denne post er jo et eksempel på det.<br />
<br />
I stedet for at gå i denne faldgrube, synes jeg at Kresten skal adlyde <a href="http://politiken.dk/debat/ECE1205012/ja-vi-skal-stramme-op-paa-sproget/">opfordringen fra Lars Friis Farsøe</a>, en af Krestens tidligere studenter, når han siger at Kresten burde "vende blikket indad" og fokusere på sin egen rolle i egenskab af, blandt andet, lektor på et universitet med den slags store problemer.<br />
<br />
Jeg vil slutte med at citere <a href="http://videnskab.dk/blog/det-er-digitaliseringens-skyld-forenklinger-overdrivelser-og-syndebukke">Tanja Juul Christiansen</a> som både blogger med klogskab og undrer sig over hvem der egentlig har aben: "At skyde skylden på digitale teknologier som sådan er en uholdbar generalisering, der fordrejer sagen, og forringer debatten om, hvordan vi kan gøre noget ved problemet."MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-77354151433699769222011-01-28T18:54:00.000+01:002011-01-28T18:54:10.086+01:00Conway's Game of Life Code Kata #2<div>After <a href="http://www.agileskane.se/wiki/Code_Retreat">yesterday's code retreat</a>, I've refined, refactored, and added some IEnumerable plumbing to <a href="http://martinsaspects.blogspot.com/2011/01/conways-game-of-life-code-kata.html">the solution I implemented in preparation for the retreat</a>.<br />
<br />
It would be a really fun exercise to visualize the generations with WPF, Silverlight or something else. I'd also like to finish the attempts made at the retreat to implement this solution with JavaScript. As one of my pairing partners pointed out, visualization could be done by manipulating the DOM of an ordinary web page.<br />
</div><pre class="brush: c#">using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using SharpTestsEx;
namespace ConwaysGameOfLifeKatas.Generations.UnitTests {
[Description("en.wikipedia.org/wiki/Conway's_Game_of_Life")]
public class GenerationsUnitTests {
[Test]
public void new_generation_should_kill_alive_cell_with_fewer_than_two_live_neighbours() {
var generations = new Generations(new Cell(1, 1), new Cell(0, 1));
generations.First().Contains(new Cell(1, 1)).Should().Be.False();
}
[Test]
public void new_generation_should_keep_alive_cell_with_two_live_neighbours_alive() {
var generations = new Generations(new Cell(1, 1), new Cell(0, 1), new Cell(2, 1));
generations.First().Contains(new Cell(1, 1)).Should().Be.True();
}
[Test]
public void new_generation_should_keep_alive_cell_with_three_alive_neighbours_alive() {
var generations = new Generations(new Cell(1, 1), new Cell(0, 1), new Cell(2, 1), new Cell(0, 0));
generations.First().Contains(new Cell(1, 1)).Should().Be.True();
}
[Test]
public void new_generation_should_kill_alive_cell_with_more_than_three_alive_neighbours() {
var generations = new Generations(new Cell(0, 0), new Cell(1, 0), new Cell(2, 0),
new Cell(0, 1), new Cell(1, 1));
generations.First().Contains(new Cell(1, 1)).Should().Be.False();
}
[Test]
public void new_generation_should_revive_dead_cell_with_three_alive_neighbours() {
var generation = new Generations(new Cell(0, 0), new Cell(1, 0), new Cell(2, 0));
generation.First().Contains(new Cell(1, 1)).Should().Be.True();
}
[TestCase(0,0,0,false)]
[TestCase(1,0,0,true)]
[TestCase(2,0,0,false)]
[TestCase(0,1,0,false)]
[TestCase(1,1,0,true)]
[TestCase(2,1,0,false)]
[TestCase(0,2,0,false)]
[TestCase(1,2,0,true)]
[TestCase(2,2,0,false)]
[TestCase(0,0,1,false)]
[TestCase(1,0,1,false)]
[TestCase(2,0,1,false)]
[TestCase(0,1,1,true)]
[TestCase(1,1,1,true)]
[TestCase(2,1,1,true)]
[TestCase(0,2,1,false)]
[TestCase(1,2,1,false)]
[TestCase(2,2,1,false)]
public void blinker_oscillator_should_oscillate_according_to_wikipedia(int x, int y, int generationIndex, bool isAlive) {
var generations = new Generations(new Cell(0, 1), new Cell(1, 1), new Cell(2, 1));
generations[generationIndex].Contains(new Cell(x, y)).Should().Be.EqualTo(isAlive);
}
}
public class Generations : IEnumerable<Generation> {
private readonly Generation _seedGeneration;
public Generations(params Cell[] seed) {
_seedGeneration= new Generation(seed);
}
public IEnumerator<Generation> GetEnumerator() {
return new GenerationEnumerator(_seedGeneration);
}
public Generation this[int index] {
get {
return this.ElementAt(index);
}
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
public class GenerationEnumerator : IEnumerator<Generation> {
public GenerationEnumerator(Generation generation) {
Current = generation;
}
public void Dispose() {}
public bool MoveNext() {
Current = Current.Tick();
return true;
}
public void Reset() {}
public Generation Current { get; private set; }
object IEnumerator.Current {
get { return Current; }
}
}
}
public class Generation {
private readonly ISet<Cell> _aliveCells;
public Generation(params Cell[] aliveCellsSeed) {
_aliveCells = new HashSet<Cell>(aliveCellsSeed);
}
private IEnumerable<Cell> KeepAlives {
get {
return _aliveCells
.Where(c => GetNumberOfAliveNeighboursOf(c) == 2
|| GetNumberOfAliveNeighboursOf(c) == 3);
}
}
private IEnumerable<Cell> Revives {
get {
return _aliveCells
.SelectMany(GetDeadNeighboursOf)
.Where(c => GetNumberOfAliveNeighboursOf(c) == 3);
}
}
public Generation Tick() {
return new Generation(KeepAlives.Union(Revives).ToArray());
}
private IEnumerable<Cell> GetDeadNeighboursOf(Cell cell) {
return GetNeighboursOf(cell).Where(c => !Contains(c));
}
private static IEnumerable<Cell> GetNeighboursOf(Cell cell) {
return Enumerable.Range(-1, 3)
.SelectMany(x => Enumerable.Range(-1, 3)
.Select(y => new Cell(cell.X + x, cell.Y + y)))
.Except(cell);
}
private int GetNumberOfAliveNeighboursOf(Cell cell) {
return GetNeighboursOf(cell).Count(Contains);
}
public bool Contains(Cell cell) {
return _aliveCells.Contains(cell);
}
}
public struct Cell : IEquatable<Cell> {
private readonly int _x;
private readonly int _y;
public Cell(int x, int y) {
_x = x;
_y = y;
}
public int Y {
get { return _y; }
}
public int X {
get { return _x; }
}
public bool Equals(Cell other) {
return other._x == _x && other._y == _y;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != typeof (Cell)) return false;
return Equals((Cell) obj);
}
public override int GetHashCode() {
unchecked {
return (_x*397) ^ _y;
}
}
}
public static class Extensions {
public static IEnumerable<T> Except<T>(this IEnumerable<T> @this, T element) {
return @this.Except(new[] {element});
}
}
}
</pre>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-66056111431028284552011-01-27T11:34:00.000+01:002011-01-27T11:34:55.790+01:00Conway's Game of Life Code Kata<div>In preparation for my first <a href="http://www.agileskane.se/wiki/Code_Retreat">code retreat</a> later today, we were asked to study the problem at hand - Conway's Game of Life. Wikipedia has an <a href="http://en.wikipedia.org/wiki/Conway's_game_of_life">excellent article</a> describing the rules.<br />
<br />
At my first attempt; I focused on the cell and tried to use a state machine. It didn't take too long before it felt cumbersome, so I stopped (although the state machine tests passed ;-)).<br />
<br />
Then I tried to mimic "the real world" (oh, what a fallacy...) again, but this time I implemented a Grid<Coordinate> with an internal List<List<Coordinate>>. This was also a mistake... Imagine keeping a reference for all empty cells as well as making the grid infinite.<br />
<br />
Third time's the charm, right? Why not just let a logical Grid instance keep a set of live cells? And why not remove all state changes, hence making both the Coordinate and the Grid types immutable? That way the Grid is asked to create a new immutable Grid for each generation.<br />
<br />
By using these ideas, I quickly arrived at the solution below which I really like. It'll be very interesting to see what other solutions we'll come up with at the retreat.<br />
</div><br />
<pre class="brush: c#">using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using SharpTestsEx;
namespace ConwaysGameOfLifeKatas.GridAsListOfAliveCells.UnitTests
{
public class GridAsListOfAliveCellsTests {
[Test]
public void new_generation_should_kill_alive_cell_with_fewer_than_two_live_neighbours() {
var grid = new Grid(new Coordinate(1, 1), new Coordinate(0, 1));
grid = grid.CreateNextGeneration();
grid.IsAlive(new Coordinate(1, 1)).Should().Be.False();
}
[Test]
public void new_generation_should_keep_alive_cell_with_two_live_neighbours_alive() {
var grid = new Grid(new Coordinate(1, 1), new Coordinate(0, 1), new Coordinate(2, 1));
grid = grid.CreateNextGeneration();
grid.IsAlive(new Coordinate(1, 1)).Should().Be.True();
}
[Test]
public void new_generation_should_keep_alive_cell_with_three_alive_neighbours_alive() {
var grid = new Grid(new Coordinate(1, 1), new Coordinate(0, 1), new Coordinate(2, 1), new Coordinate(0, 0));
grid = grid.CreateNextGeneration();
grid.IsAlive(new Coordinate(1, 1)).Should().Be.True();
}
[Test]
public void new_generation_should_kill_alive_cell_with_more_than_three_alive_neighbours() {
var grid = new Grid(new Coordinate(0, 0), new Coordinate(1, 0), new Coordinate(2, 0),
new Coordinate(0, 1), new Coordinate(1, 1));
grid = grid.CreateNextGeneration();
grid.IsAlive(new Coordinate(1, 1)).Should().Be.False();
}
[Test]
public void new_generation_should_revive_dead_cell_with_three_alive_neighbours() {
var grid = new Grid(new Coordinate(0, 0), new Coordinate(1, 0), new Coordinate(2, 0));
grid = grid.CreateNextGeneration();
grid.IsAlive(new Coordinate(1, 1)).Should().Be.True();
}
[Test]
public void blinker_oscillator_should_oscillate_according_to_wikipedia() {
var grid = new Grid(new Coordinate(0, 1), new Coordinate(1, 1), new Coordinate(2, 1));
grid = grid.CreateNextGeneration();
grid.IsAlive(new Coordinate(0, 0)).Should().Be.False();
grid.IsAlive(new Coordinate(1, 0)).Should().Be.True();
grid.IsAlive(new Coordinate(2, 0)).Should().Be.False();
grid.IsAlive(new Coordinate(0, 1)).Should().Be.False();
grid.IsAlive(new Coordinate(1, 1)).Should().Be.True();
grid.IsAlive(new Coordinate(2, 1)).Should().Be.False();
grid.IsAlive(new Coordinate(0, 2)).Should().Be.False();
grid.IsAlive(new Coordinate(1, 2)).Should().Be.True();
grid.IsAlive(new Coordinate(2, 2)).Should().Be.False();
}
}
public class Grid {
private readonly ISet<Coordinate> _aliveCoordinates;
public Grid(params Coordinate[] aliveCoordinatesSeed) {
_aliveCoordinates = new HashSet<Coordinate>(aliveCoordinatesSeed);
}
public Grid CreateNextGeneration() {
var keepAliveCoordinates = _aliveCoordinates
.Where(c => GetNumberOfAliveNeighboursOf(c) == 2 || GetNumberOfAliveNeighboursOf(c) == 3);
var reviveCoordinates = _aliveCoordinates
.SelectMany(GetDeadNeighboursOf)
.Where(c => GetNumberOfAliveNeighboursOf(c) == 3);
return new Grid(keepAliveCoordinates.Union(reviveCoordinates).ToArray());
}
private IEnumerable<Coordinate> GetDeadNeighboursOf(Coordinate coordinate) {
return GetNeighboursOf(coordinate).Where(c => !IsAlive(c));
}
private static IEnumerable<Coordinate> GetNeighboursOf(Coordinate coordinate) {
return Enumerable.Range(-1, 3).SelectMany(
x => Enumerable.Range(-1, 3).Select(y => new Coordinate(coordinate.X + x, coordinate.Y + y)))
.Except(new []{coordinate});
}
private int GetNumberOfAliveNeighboursOf(Coordinate coordinate) {
return GetNeighboursOf(coordinate).Count(IsAlive);
}
public bool IsAlive(Coordinate coordinate) {
return _aliveCoordinates.Contains(coordinate);
}
}
public struct Coordinate : IEquatable<Coordinate> {
private readonly int _x;
private readonly int _y;
public Coordinate(int x, int y) {
_x = x;
_y = y;
}
public int Y { get { return _y; } }
public int X { get { return _x; } }
public bool Equals(Coordinate other) {
return other._x == _x && other._y == _y;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != typeof (Coordinate)) return false;
return Equals((Coordinate) obj);
}
public override int GetHashCode() {
unchecked {
return (_x*397) ^ _y;
}
}
}
}
</pre>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com3tag:blogger.com,1999:blog-6425530.post-11543067811436006302010-12-11T14:58:00.000+01:002010-12-11T14:58:55.422+01:00Insourcing and responding to change<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">Just found a Danish job ad with an interesting, as well as promising, wording:<br />
</span><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
"</span><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">Udviklingen er for nylig blevet insourcet igen efter nogle års outsourcing </span><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">[...] </span><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">Formålet med insourcingen af udviklingen er at <span class="Apple-style-span" style="background-color: #f1c232;">sikre hurtigst mulig reaktion</span> i forhold til både vores danske kædekunder og vores internationale ekspansion."<br />
</span><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
The ad can be viewed here: <a href="http://goo.gl/vFHpm">http://goo.gl/vFHpm</a></span>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-1701808559528601312010-11-24T20:46:00.001+01:002010-11-24T20:52:00.521+01:00The only people who like to hear that their code is bad, is people who's trying to get better<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">"If you price yourself high, you're gonna get better clients. The same is true for information in a supply and demand situation when you're trying to help someone that doesn't wanna be helped is no good.<br />
</span><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
- I can show you how to be better.<br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">- I don't want to be better. I want to keep sucking.<br />
</span><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
Let them keep sucking! The only people who like to hear that their code is bad, is people who's trying to get better. Those are the only people you wan't to associate with in the first place anyway.</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
[...] If you don't set yourself apart, you're going to work on mediocre projects. That'll screw you over. It kinda makes you stupid."</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">- <a href="http://feedproxy.google.com/~r/ThisDevelopersLife/~3/0surVz5qNpI/1197337846">Giles Bowkett in this.life() 1.0.4: Being Mean</a></span><br />
<br />
<object height="325" width="325"><param name="allowScriptAccess" value="always"/><param name="allowFullScreen" value="true"/><param name="movie" value="http://player.wizzard.tv/player/o/i/x/129062765797/config/k-27721e9f4e6ff19c/uuid/null/episode/k-cab9e8261c2ee84f"/><embed src="http://player.wizzard.tv/player/o/i/x/129062765797/config/k-27721e9f4e6ff19c/uuid/null/episode/k-cab9e8261c2ee84f" name="movie" menu="false" type="application/x-shockwave-flash" AllowScriptAccess="always" AllowFullScreen="true" width="325" height="325"/></object>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-41861316151285665942010-05-04T23:19:00.001+02:002010-05-04T23:20:34.897+02:00Coders at Work Speaking Words of WisdomI've finished reading <a href="http://www.codersatwork.com/">Coders at Work</a> where Peter Seibel interviews "15 of the all-time greats of programming and computer science" about the craft of programming and related topics.<br />
<br />
It's a very interesting book, which I wholeheartedly recommend to anyone who cares about their profession as a software developer.<br />
<br />
Below some words of wisdom from three of the interviewees:<br />
<br />
Thompson: "My definition of fragile code is, suppose you want to add a feature — good code, there's one place where you add that feature and it fits; fragile code, you've got to touch ten places."<br />
<br />
Siebel: "[…] people […] work long hours because we have this idea that we've got to get this product out the door and the way to do it is for everyone to work 80, 100 hours a week."<br />
<br />
Thompson: "That generates burnout. […] external deadlines — generate stress."<br />
<br />
Siebel: "[…] in terms of getting things done in the short term, does it work?"<br />
<br />
Thompson: "Usually you're in a position where such a thing is continual."<br />
<br />
Siebel: "Can you estimate how long it's going to take to write a given piece of code?"<br />
<br />
Thompson: "[…] if you're doing it for production then usually there are other people involved and coordination — I can't estimate that."<br />
– <a href="http://books.google.com/books?id=nneBa6-mWfgC&printsec=frontcover&dq=Coders+at+Work&ei=YILgS7-EAYa-ywTr7O2gDA&cd=1#v=onepage&q=fragile&f=false">Ken Thompson, p. 467, 478 & 479</a><br />
<br />
<br />
"[…] a design review double checks that the parts that he [the programmer] thought he had right he did have right and potentially give him some insight on the parts that he didn't. […] such an obvious good use of the senior talent doing the review."<br />
– <a href="http://books.google.com/books?id=nneBa6-mWfgC&printsec=frontcover&dq=Coders+at+Work&ei=YILgS7-EAYa-ywTr7O2gDA&cd=1#v=snippet&q=senior%20talent&f=false">Bernie Cosell, p. 539</a><br />
<br />
<br />
"[…] software required so much attention to detail. It filled that much of my brain to the exclusion of other stuff."<br />
<br />
"I think it is always going to be true that a person who manages programmers should not expect it to be predictable."<br />
– <a href="http://books.google.com/books?id=nneBa6-mWfgC&printsec=frontcover&dq=Coders+at+Work&ei=YILgS7-EAYa-ywTr7O2gDA&cd=1#v=snippet&q=attention%20to%20detail&f=false">Donald Knuth, p. 572</a>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-23240182179420032692010-03-25T22:10:00.010+01:002010-03-26T11:38:22.084+01:00Merge (fka Upsert) Extension Method for IDictionary<pre class="brush: c#">[TestFixture]
public class IDictionaryExtensionsTests
{
[Test]
public void ShouldAddIfKeyDoesNotExists()
{
var dictionary = new Dictionary<string, int> { { "nøgle", 100 }, { "key", 200 } };
dictionary.Merge("nyckel", 300).Count.Should().Be.EqualTo(3);
}
[Test]
public void ShouldUpdateIfKeyExists()
{
var dictionary = new Dictionary<string, int> { { "nøgle", 100 }, { "key", 200 } };
dictionary.Merge("nøgle", 400).Count.Should().Be.EqualTo(2);
dictionary["nøgle"].Should().Be.EqualTo(400);
}
}
/// <summary>
/// Adds the key/value pair if the key doesn't exist, or updates the key with
/// the supplied value if the key exists.
/// <remarks>
/// The name "Merge" is taken from SQL:2003 (f.k.a. "Upsert")
/// <see cref="en.wikipedia.org/wiki/Merge_(SQL)"/>
/// </remarks>
/// </summary>
public static class IDictionaryExtensions
{
public static IDictionary<TKey, TValue> Merge<TKey, TValue>(this IDictionary<TKey, TValue> thiz, TKey key, TValue value)
{
if (thiz.ContainsKey(key))
{
thiz.Remove(key);
}
thiz.Add(key, value);
return thiz;
}
}
</pre>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-53094496448037401932010-02-12T19:31:00.000+01:002010-02-12T19:31:54.319+01:00YAK - Yet Another Keyboard Navigator for Chrome<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;">I wanted to learn some basic jQuery, and I wanted a simple ergonomic keyboard navigator for Chrome: meet YAK! </span><br />
<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;">I implemented it as a user/Greasemonkey script, which will pass as an extension in Chrome.</span><br />
<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;">YAK web site: </span><a href="http://yak.nfshost.com/"><span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;">http://yak.nfshost.com/</span></a><br />
<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;">YAK @ userscripts.org: </span><a href="http://userscripts.org/scripts/show/68609"><span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;">http://userscripts.org/scripts/show/68609</span></a><br />
<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-34727558327501179822010-02-10T15:08:00.002+01:002010-02-10T15:13:22.054+01:00Poor Man's Custom Types with C# Using Aliases<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;">For the sake of simplicity, I never implement custom types just for the sake of readability. I.e., I don't encapsulate int, string, decimal, and so on. This is because I find the cost being too high when writing, maintaining, and (OR) mapping all these types.</span><br />
<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;">What I've just started doing in order to achieve the same level of readability, is using aliases like so:</span><br />
<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"></span><br />
<span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">using Birthday = DateTime;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">using PersonId = Int32;</span><br />
before the class declaration, but after the namespace declaration (so that one doesn't have to fully qualify the type names).<br />
<br />
For complex generic types, the readability increases even more IMHO:<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">using StrangeDictionary = IDictionary<int, KeyValuePair<string, decimal>>;</span><br />
<br />
/Martin</span>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com1tag:blogger.com,1999:blog-6425530.post-25900804803028704572009-12-01T09:45:00.002+01:002009-12-01T09:46:45.123+01:00Oracle Horror Morning<span style="font-family: 'Trebuchet MS', sans-serif;">First, I need to say that the database I've primarily used for the last decade is MS SQL Server.</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;">I needed to write a simple function like so:</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">is_holiday(in_code in varchar2, in_year in integer, in_month in integer, in_day in integer)</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;">that returns a boolean.</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;">First, I wrote a few tests like these:</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"></span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><span style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">select is_holiday('xyz', 2006, 8, 1) from dual; -- false </span><br />
<span style="font-family: 'Courier New', Courier, monospace;">select is_holiday('xyz', 2009, 11, 1) from dual; -- true </span><br />
<br />
When I ran the tests after implementing the function I got the following messages:<br />
<br />
<span style="font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif;"></span><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"></span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><pre style="font-family: Courier, monospace; white-space: pre;">SQL Error: ORA-06552: PL/SQL: Statement ignored
ORA-06553: PLS-382: expression is of wrong type</pre><pre style="font-family: Courier, monospace; white-space: pre;"></pre><pre style="white-space: pre;"><span style="font-family: 'Trebuchet MS', sans-serif;">I later learned that this is because booleans aren't allowed inside SQL like that. A sort of misguiding and not very helpful error message IMHO.</span></pre><pre style="white-space: pre;"><span style="font-family: 'Trebuchet MS', sans-serif;">
</span></pre><pre style="white-space: pre;"><span style="font-family: 'Trebuchet MS', sans-serif;">On my quest of finding a solution, I also tried named params like so:</span></pre><pre style="white-space: pre;"><span style="font-family: 'Trebuchet MS', sans-serif;">
</span></pre><pre style="white-space: pre;"><span style="font-family: 'Courier New', Courier, monospace;">select is_holiday(in_sm_center_code => 'xyz', in_year => 2006, in_month => 8, in_day => 1) from dual;</span></pre><pre style="white-space: pre;"><span style="font-family: 'Trebuchet MS', sans-serif;">
</span></pre><pre style="white-space: pre;"><span style="font-family: 'Trebuchet MS', sans-serif;">That gave me the horror:</span></pre><pre style="white-space: pre;"><span style="font-family: 'Trebuchet MS', sans-serif;"><span style="font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; white-space: normal;">
<pre style="font-family: Courier, monospace; white-space: normal;">ORA-00907: missing right parenthesis</pre><pre style="font-family: Courier, monospace; white-space: normal;"></pre><pre style="white-space: normal;"><span style="font-family: 'Trebuchet MS', sans-serif;">What?!?</span></pre></span></span>
</pre></span><span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;">Once again, PL/SQL programs differ from the SQL statements like that, where the params can't be named, but must be in positional form.</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;">Why not simply state that in the error dialog box?</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;">Thanks to this blog: <a href="http://oraclequirks.blogspot.com/"><span style="color: black;">oraclequirks.blogspot.com</span></a>, I got my head around these issues and solved the task at hand.</span>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-8383548885209330462009-11-24T08:37:00.000+01:002009-11-24T08:37:05.321+01:00More on TDD and BDD, but this time I link to interesting resources from ThoughtWorks<span style="font-family: 'Trebuchet MS', sans-serif;">Here are two interesting slide shows from ThoughtWorks that closely relates to what <a href="http://martinsaspects.blogspot.com/search/label/bdd">I wrote regarding executable acceptance tests (stories)</a> some time ago:</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><a href="http://xpday3.xpday.org/slides/ATDD.ppt">Acceptance Test Driven Development (ATDD)</a></span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><a href="http://dynamicorange.com/uploads/slides/xpday/ATvUT(XPDay3).ppt">Acceptance Testing vs. Unit Testing: A Developer’s Perspective</a></span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;">I think these two resources make a good theoretical starting point for the "Why ATDD?" question.</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br />
</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;">Don't just look at the slides as slide shows, since there's a lot of great stuff in the comments.</span>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-58258641595878285162009-11-21T17:48:00.001+01:002009-11-21T17:50:00.513+01:00Ubuntu Karmic Koala - I Like!<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhjXCwyzP9XGZE8D6cAE-hv5sbnEKJRaBJyvVR5zoIFS-r_bUUQMaqu93mznC58KDyMIQNzKO7N71yUcz2UCGa1wVRk1dhLV7s5yAwAVdouR5-hZRtrdtwdlmGEDQrXgXyev2Ymg/s1600/ubuntu_karmic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhjXCwyzP9XGZE8D6cAE-hv5sbnEKJRaBJyvVR5zoIFS-r_bUUQMaqu93mznC58KDyMIQNzKO7N71yUcz2UCGa1wVRk1dhLV7s5yAwAVdouR5-hZRtrdtwdlmGEDQrXgXyev2Ymg/s640/ubuntu_karmic.png" /></a><br />
</div>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-52131685256035028632009-11-02T21:22:00.003+01:002010-02-11T08:43:00.429+01:00“Programming as you know it just died”The above was stated by Juval Löwy during today’s workshop at øredev entitled “Service-Orientation, WCF, and You”.<br />
<br />
Just like every .Net class conceptually is a COM-object, Juval argued that with the challenges we as biz app architects/developers face today, every (.Net) class should be a (WCF) service.<br />
The main reason is that basically all complex plumbing such as security, concurrency, logging, fault tolerance, and so forth is given to you for free.<br />
The learning curve is however mammoth, and is best compared with going from procedural to object-oriented programming. <br />
<br />
When interpreting all the signs from MS, Intel, and others, Juval means that it is clear that service-orientation is the next paradigm shift that will replace .Net.<br />
The analogy is ATL, which made it it easy to follow the good practice of making your C++ class a COM one. Then .Net came along were this wasn’t framework-based. Now we have WCF making it possible to make every .Net class a service by utilizing a framework.<br />
The financial figures also indicates this being the main focus of MS. Juval claims that the cost of WCF is some 150% of what went into the CLR.<br />
<br />
All in all, this was an extremely mind-exercising day that left me with the feeling of seeing the world of programming with a new pair of eyes from now on.<br />
I guess I’ll have to read Juval’s WCF book when the 3rd edition is out… ;-)<br />
<br />
<br />
UPDATE Feb 11 2010:<br />
1. There's a recent <a href="http://www.dotnetrocks.com/default.aspx?showNum=520">DotNetRocks show with Juval</a>, where he explains all of this.<br />
<br />
2. There's now a <a href="http://oreilly.com/catalog/9780596805494">"rough cut" edition of the book</a> available.MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-41204733567771358252009-10-21T12:22:00.001+02:002009-10-21T12:24:30.870+02:00"I often find that a nice design can come from just being really anal about getting rid of duplicated code"- Martin Fowler<br /><br />Hear, hear!MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-13196883765307028012009-10-15T22:21:00.001+02:002014-03-06T13:28:36.344+01:00Same old tale (about code quality and technical dept)<p>Yesterday, we had yet another great Agile Skåne meeting at Green Lion Inn in Malmö.</p> <p>One of the members is right now experiencing how much bad code can hurt you. </p> <p>His new employer went to the lowest bidder without any quality control for many years, and have ended up with a total mess, prohibiting them to add features their competitors offer, and hence can’t improve their market share.</p> <p>How come this serious mistake is made over and over again? After all it’s common knowledge that quality makes you fast and able to respond to change in the long run. (I know, we’re irrational human beings…)</p> <p>I don’t know if it’s true, but I want it to be:</p> <p>“Quality is the best business plan.” – Pixar’s John Lasseter</p> <p>At least, you can’t blame Pixar for not being successful…</p> <p>More on the subject:</p> <p>Fowler recently wrote an interesting piece on technical dept: <a href="http://martinfowler.com/bliki/TechnicalDebtQuadrant.html">http://martinfowler.com/bliki/TechnicalDebtQuadrant.html</a></p> <p>There’s a book on the subject of not acting according to what’s known to work: <br />”The Knowing-Doing Gap” (ISBN 1422163520), which I haven’t read.</p> MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-27236786662816637362009-10-07T21:28:00.001+02:002009-10-07T21:28:53.157+02:00Decreased changeability when TDD:ing, and a possible solution<p>Yesterday, we had a great <a href="http://oresund.altdotnet.org/Blog/Meeting-in-Copenhagen-on-Oct-1st" target="_blank">alt.net øresund meeting at BestBrains in København</a>. Sune held a great presentation about TDD, its pros, and cons. Actually, despite being a TDD fan and user, Sune is very insightful and realizes that TDD isn’t a silver bullet. It could actually decrease your changeability if done wrong for example.</p> <p>I’ve personally experienced some of the pains Sune described and that we discussed during the open space session that followed. Actually, previously the very same day <a href="http://groups.google.com/groups/profile?hl=en&enc_user=uYNa_iAAAAAVTPJPG5pa_j8gqgbvHcTtGdt7NIPHa_j7CyrS7PSMHg" target="_blank">Martin N Jensen</a> and I tried to start attacking some of them by complementing our TDD efforts with a BDD way of defining higher level requirements in the form of stories and scenarios.</p> <p>During the bicycle/train ride home, i thought some more about these issues. Only two days before I had to fix a lot of tests that utilized mocking/stubbing when an API changed that didn’t affect the system behavior in any way. That got me thinking – what the frak am I doing?!? No business value in that activity…</p> <p>My not-IRL-tested idea goes something like this: </p> <p>You have two sets of unit tests.</p> <ul> <li>Stable BDD tests that drives the SUT (System Under Test) by only looking at the highest level of state (i.e. the database), performs an action, and then verifies the resulting state/output on the highest possible level. These tests corresponds one-to-one with the requirements if using stories as requirements</li> <li>Instable “classic” TDD tests that drives the design of the inner workings (classes and their interactions, individual methods, etc.) of the SUT</li> </ul> <p>My idea definitely requires UFD (UpFront Design) more according to the lean way of developing software as opposed to some agile start-immediately approaches.</p> <p>If you start with a set of fairly stable stories, and implement them as BDD tests in one way or another (we’re using <a href="http://www.codeplex.com/storyq" target="_blank">StoryQ</a> at the moment), you consider these tests as stable meaning it should require some thinking before changing them.</p> <p>The much more fragile TDD tests are considered something that could be thrown away (likely in parallel with writing new ones), or rewritten if still needed, when the inner workings are refactored (e.g. when replacing Castle Active Record with <a href="http://code.google.com/p/frogdotnet/" target="_blank">Frog.Net</a> Sune, or in my case when removing specialized repositories in favor of one generic the other day).</p> <p>I feel that if I have my stories implemented as executable acceptance tests, then I would really get</p> <ul> <li>true confidence when undertaking big refactorings</li> <li>tests that could actually be read and understood by peers, and possibly some other stakeholders</li> <li>regression test suite that verifies the SUT’s behavior, i.e. that the stakeholders get what’s agreed upon</li> </ul> <p>As a potential benefit I also get extremely readable status reports of every story on e.g. the build server every time an ok commit is made. This is a huge benefit IMHO since it allows you to remove the possible need for a complex task tracking system but still gives your PM (and all others) complete real-time status, allowing you to work with a whiteboard and post-its as a kanban board.</p> MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-4339384212139380692009-10-02T15:12:00.008+02:002010-03-25T21:53:08.274+01:00Programmatically configure Log4net with two rolling file appenders<pre class="brush: c#">public static class LogBootstrapper
{
public static void Bootstrap()
{
RootLogger.AddAppender(CreateRollingFileAppender(Level.All));
RootLogger.AddAppender(CreateRollingFileAppender(Level.Info));
RootLogger.Repository.Configured = true;
}
private static Logger RootLogger
{
get { return ((Hierarchy)LogManager.GetRepository()).Root; }
}
private static RollingFileAppender CreateRollingFileAppender(Level level)
{
var usingFileName = string.Format("logs\\MyProject_{0}-{1}-{2}_{3}.log",
DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, level.Name);
var layout = new PatternLayout("[%level] %message%newline");
var rollingFileAppender = new RollingFileAppender
{
Layout = layout,
AppendToFile = true,
RollingStyle = RollingFileAppender.RollingMode.Date,
File = usingFileName,
ImmediateFlush = true,
Threshold = level
};
rollingFileAppender.ActivateOptions();
return rollingFileAppender;
}
}</pre><br />
<div style="font-family: trebuchet ms;">Beats XML config IMHO.</div>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-41933091713379388172009-10-01T20:39:00.000+02:002009-10-01T20:42:12.388+02:00Pragmatic Thinking and Learning: Refactor Your Wetware<span class="Apple-style-span" style="font-family:'trebuchet ms';">I just finished reading <a href="http://www.pragprog.com/titles/ahptl/pragmatic-thinking-and-learning">Pragmatic Thinking and Learning: Refactor Your Wetware by Andy Hunt</a>.</span><div><span class="Apple-style-span" style="font-family:'trebuchet ms';"><br /></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms';">In short: read it ;-)</span></div>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-74135374321089245652009-09-24T15:24:00.002+02:002009-09-24T15:26:31.568+02:00Great Lean/Kanban Post<span style="font-size:100%;"><span style="font-family:trebuchet ms;">... with a big extra plus for letting me know about "Pirate Metrics" for the first time:</span><br /><a href="http://raibledesigns.com/rd/entry/lean_teams_doing_more_with"><span style="font-family:trebuchet ms;">http://raibledesigns.com/rd/entry/lean_teams_doing_more_with</span></a><br /></span>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0tag:blogger.com,1999:blog-6425530.post-71884075810038373452009-09-24T12:39:00.006+02:002009-09-24T12:44:24.149+02:00Trying out Sharp Tests Ex with NUnit 2.5<div style="background: rgb(44, 44, 44) none repeat scroll 0% 0%; font-family: Consolas; font-size: 10pt; color: rgb(220, 220, 204); -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;"><p style="margin: 0px;"> </p><p style="margin: 0px;"><span style="color: rgb(0, 153, 0);">/*before*/</span> <span style="color: rgb(228, 192, 82);">Assert</span>.<span style="color: rgb(223, 223, 191);">That</span>(<span style="color: rgb(223, 223, 191);">logHandlersCount</span>, <span style="color: rgb(228, 192, 82);">Is</span>.<span style="color: rgb(223, 223, 191);">EqualTo</span>(<span style="color: rgb(138, 204, 207);">1</span>));</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;"><br /></p><p style="margin: 0px;"> </p><p style="margin: 0px;"><span style="color: rgb(0, 153, 0);">/*after*/</span> <span style="color: rgb(223, 223, 191);">logHandlersCount</span>.<span style="color: rgb(223, 223, 191);">Should</span>().<span style="color: rgb(223, 223, 191);">Be</span>.<span style="color: rgb(223, 223, 191);">EqualTo</span>(<span style="color: rgb(138, 204, 207);">1</span>);</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;"><br /></p><p style="margin: 0px;"> </p></div><br /><span style=";font-family:trebuchet ms;font-size:100%;" >Even better readability :-)</span><span style="font-size:100%;"><br /><br /></span><span style=";font-family:trebuchet ms;font-size:100%;" >More on Sharp Tests Ex here: <a href="http://sharptestex.codeplex.com/Wiki/View.aspx?title=SyntaxMainPage">http://sharptestex.codeplex.com/Wiki/View.aspx?title=SyntaxMainPage</a></span>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com2tag:blogger.com,1999:blog-6425530.post-22718983538919019072009-09-23T22:05:00.003+02:002009-09-23T22:13:14.605+02:00It's tough to make predictions, especially about the future<span style="font-family: trebuchet ms;">- Yogi Berra, Philosopher</span>MartinRLhttp://www.blogger.com/profile/17794301976274938698noreply@blogger.com0