adamralph.com 2017-02-19T18:30:34+00:00 http://www.adamralph.com/ Adam Ralph Copyright (c) Adam Ralph No Pull Request Is Too Small 2015-10-12T00:00:00+00:00 http://blog.adamralph.com/2015/10/12/no-pull-request-is-too-small// <p>This is a message I try and convey as often as possible to people starting out in open source contribution. To show how easy it is, I sometimes <a href="https://www.youtube.com/watch?v=1v091LSnThE&amp;t=36m4s">send a tiny pull request live on stage</a>. I don’t even have to leave the GitHub web UI.</p> <p>And this is not something I do only for show. If you were to follow my activity on GitHub you’d see that <a href="https://github.com/damianh/LibLog/pull/86/files">I do this for real</a>. If I’m browsing some source code and I see a typo, why not fix it there and then? Project maintainers (myself included) appreciate these little fixes and polishes. Every little bit helps.</p> <p>It was heart warming for me to learn that I inspired <a href="https://twitter.com/Cyberlane">Justin Nel</a> and <a href="https://twitter.com/BleedingNEdge">Paweł Grudzień</a> when they watched me do this in my talks. They went on to send their own small improvements to <a href="https://github.com/NancyFx/Nancy.Demo.Samples/pull/3">NancyFX</a> and <a href="https://github.com/aspnet/dnx/pull/2936">DNX</a>.</p> <p>Indeed, this is something <em>all</em> OSS contributors should bear in mind. Beginners, journeymen and masters alike.</p> <p>The next time you’re browsing some code on GitHub, see if you can spot an opportunity to make a tiny improvement. Click the edit button, and <em>just do it</em>. You’ll improve the project, even if only a little. All the baby steps add up. Your name will enter the commit history and you’ll be a recognised contributor. If it’s your first ever pull request, you’ll have taken your first step in an exciting journey.</p> Blog = Weblog 2015-09-28T00:00:00+00:00 http://blog.adamralph.com/2015/09/28/blog-equals-weblog// <p>The word ‘blog’ is a truncation of the expression ‘weblog’. Google defines the word as follows:</p> <blockquote> <p>a regularly updated website or web page, typically one run by an individual or small group, that is written in an informal or conversational style.</p> </blockquote> <p>The key expression here is <em>regularly updated</em>. The key facilitator is <em>written in an informal or conversational style</em>.</p> <p>At some point, I believe myself and at least a few other blog authors forgot this. Instead of blogging, i.e. regularly logging our activities, observations, general thoughts, etc. on the web in an informal style, we started producing polished articles, written with a beginning, a middle and an end. We started to plan blog posts way in advance, to craft them over days, weeks or more, to keep a backlog of blog post ideas “for the future”. Whilst these approaches may work for some, evidence has shown that has not worked for me. Whatever the reasons are, be they a lack of ability, time or motivation, the end result is that my blog is <em>not</em> regularly updated.</p> <p>More than once, I’ve found myself asking:</p> <blockquote> <p>When did my blog stop being a weblog?</p> </blockquote> <!--excerpt--> <p>Indeed, was it ever a weblog? Why can it not just <em>be</em> a weblog? There’s no good answer. I’ve made it as easy as possible to write posts. My blog uses GitHub pages and Jekyll and I can write posts directly in the GitHub UI using Markdown. It is my personal blog. I am not using it to sell services or convey a corporate message. It <em>should</em> be personal, even if it does have a general theme, such as software development. There’s no reason I can’t write posts in an informal or conversational style. There is no reason a post can’t be short. A few mistakes here and there? Who cares? Let the spelling and grammar police do they’re worst! (See what I did there?) :wink:</p> <p>So this evening, as I stepped past my office door, I decided to pop in and try and reclaim my <strong>weblog</strong>. I went to GitHub, clicked on the create file icon and started writing some Markdown. This post is the result.</p> <p>Perhaps this is just the stepping stone to the next era of tumbleweeds in my blog, but at least I’ll sleep with a sense of satisfaction tonight.</p> Announcing xBehave.net 2.0 2015-08-06T00:00:00+00:00 http://blog.adamralph.com/2015/08/06/announcing-xbehave-net-2-0// <p><img src="https://raw.github.com/xbehave/xbehave.net/master/assets/xbehave_128x128.png" alt="" /></p> <p>I’m enormously happy to announce that <a href="http://xbehave.github.io/">xBehave.net 2.0</a> (based on <a href="http://xunit.github.io/">xUnit.net 2.0</a>) is now live and available on <a href="https://www.nuget.org/packages/Xbehave/2.0.0">NuGet</a>. Work on xBehave.net 2.0 began in April 2014, shortly after the first beta release of xUnit.net 2.0, and continued on and off for the next 16 months with the first xBehave.net 2.0 beta release in December.</p> <p>Just like xUnit.net 2.0, xBehave.net 2.0 is a complete re-write from scratch, with only the public API type names and method signatures remaining relatively unchanged. However, only ‘half’ of the code base was in fact re-written. xBehave.net has dogfooded itself for it’s own acceptance tests since pre-1.0, and those acceptance tests have remained largely as is and only added to, throughout the whole exercise. This meant that xBehave.net 2.0 was developed almost entirely in true TDD style. The suite of 1.x acceptance tests already existed. All that needed to be done was to create a project linked against xUnit.net 2.0 which made those tests green!</p> <p>Linking against xUnit.net 2.0 is not the only change in xBehave.net 2.0. Several new features have been added (and, of course accompanying acceptance tests), some changed and some taken away. For full details, see the <a href="https://github.com/xbehave/xbehave.net/wiki/Changes-in-version-2.0">Wiki</a>.</p> <!--excerpt--> <h2 id="thanks">Thanks</h2> <p><a href="http://adamralph.com/2013/10/09/announcing-xbehave-net-1-0/">As I’ve said before</a>, by far the most important aspect of xBehave.net is <strong>you</strong>, the community. In particular, I’d like to call out the following people for their invaluable help in getting xBehave.net 2.0 out of the door:</p> <h3 id="brad-wilson"><a href="https://github.com/bradwilson">Brad Wilson</a></h3> <p>Brad gave me a horde of advice and assistance over at the <a href="https://xunit.slack.com/messages/general/details/">xUnit.net chat room</a> and made a slew of changes to xUnit.net 2.0 during the beta phase which allowed xBehave.net 2.0 to come into existence. Not least, the split of the component packages into meta packages and *.extensibility.* packages. Thank you Brad for embracing the community during the development of xUnit.net 2.0 and for one of the most elegantly designed extensibility API’s this developer has ever had the pleasure to use!</p> <h3 id="matt-ellis"><a href="https://github.com/citizenmatt">Matt Ellis</a></h3> <p>During smoke testing with the <a href="https://www.jetbrains.com/resharper/">ReSharper</a> xUnit.net plug-in, I discovered some problems in the beta versions of both xBehave.net and the ReSharper plug-in. I was lucky enough to able to collaborate closely with Matt in the xUnit.net chat room, resulting in a cross-pollination of improvement and fixes to both products.</p> <h3 id="remco-mulder"><a href="https://github.com/remcomulder">Remco Mulder</a></h3> <p>During smoke testing with a pre-release version of <a href="http://www.ncrunch.net/">NCrunch</a> linked against the xUnit.net 2.0 betas, I discovered some problems in both NCrunch and xBehave.net. Remco spent a large amount of time with me in the xUnit.net chat room reproducing and identifying these problems and, together, we fixed the problems on either side of the integration until we got to a point where xBehave.net was crunching along nicely with NCrunch.</p> <h3 id="urs-enzler"><a href="https://github.com/ursenzler">Urs Enzler</a></h3> <p>Urs gave me some enormously valuable feedback as a user of xBehave.net during the release candidate phase. Not least, he drove the split of the package into a meta package plus a core implementation package, to allow developers who do not want to take a dependency on xunit.assert to install the Xbehave.Core package instead of Xbehave, similarly to the xunit and xunit.core packages. Urs also presented the ideas which inspired the creation of the <a href="https://github.com/xbehave/xbehave.net/wiki/Continuing-on-failure">continuing on failure</a> and <a href="https://github.com/xbehave/xbehave.net/wiki/Step-filters">step filters</a> features. BTW, if you’re interested in generating Markdown output from your xBehave.net tests, check out his <a href="https://github.com/ursenzler/xBehaveMarkdownReport">xBehaveMarkdownReport </a> tool.</p> <h2 id="future">Future</h2> <p>The next planned version of xBehave.net is 2.1, which will be linked against xUnit.net 2.1 (currently in beta), and will add support for the ‘dotnet’ (vNext, DNX, .NET Core, etc.) NuGet TFN.</p> <p>If you’d like to help out, just head over to the <a href="https://github.com/xbehave/xbehave.net/">GitHub repository</a> where you can get familiar with the code, send pull requests or raise issues for new features, bugs, questions, discussions etc.</p> <p>You can also ping me on <a href="https://twitter.com/adamralph">Twitter</a> or in the xBehave.net <a href="https://jabbr.net/#/rooms/xbehavenet">chat room</a> on JabbR.</p> <p><strong>Thanks again!</strong></p> How To Kill Your NuGet Package Through Casing 2015-03-04T00:00:00+00:00 http://blog.adamralph.com/2015/03/04/nuget-gallery-casing-woes// <p>When the initial placeholder for the <a href="https://www.nuget.org/packages/Bau.Xunit/">Bau.Xunit</a> package was uploaded, the ID was cased <code class="highlighter-rouge">Bau.XUnit</code>. Later, when the first real package was uploaded, the ID was cased <code class="highlighter-rouge">Bau.Xunit</code>. Since the NuGet gallery compares IDs without case sensitivity, these ID were matched and hence the later package became a newer version of the older, but the original ID <code class="highlighter-rouge">Bau.XUnit</code> remained.</p> <p>This caused problems in Linux, since the folder on disk was created using the gallery ID <code class="highlighter-rouge">Bau.XUnit</code>, but the .csproj reference was added using the ID in the package version that has been downloaded, <code class="highlighter-rouge">Bau.Xunit</code>. Now, because Linux has a case sensitive file system, the reference did not point to the package on disk, and everything blew up when trying to compile the project. This is a bug in itself, but getting a NuGet client patch pushed out wasn’t something I fancied pursuing at the time. This lead to some <a href="https://github.com/bau-build/bau/blob/780bb643bd8ec16aab1daa7cc3f1add31084ef3f/bau.sh#L22-L26">horrible build hacks</a>. So, I decided it was time to fix things once and for all, in the NuGet gallery…</p> <!--excerpt--> <p>The natural thing to do was to fix the ID in the gallery, since <code class="highlighter-rouge">Bau.Xunit</code> is the desired casing. The NuGet support staff where very helpful and, on my request, removed all the versions of the package from the gallery. I then re-uploaded all the correctly ID’d versions of the package and, hey presto, the casing was fixed and the ID of the package was <code class="highlighter-rouge">Bau.Xunit</code>. This is proven since the URL for the package, in search results, etc. was now “https://www.nuget.org/packages/Bau.Xunit/” instead of “https://www.nuget.org/packages/Bau.XUnit/”. All good.</p> <p>That is, until I tried to consume the package using the NuGet client. I received an error telling me</p> <blockquote> <p>Unable to find version ‘0.1.0-beta07’ of package ‘Bau.Xunit’.</p> </blockquote> <p>So I dug further and spun up Fiddler to see what was going on behind the scenes. I found the following:</p> <div class="highlighter-rouge"><pre class="highlight"><code>REQUEST: -------------------------------- GET https://www.nuget.org/api/v2/Packages(Id='Bau.Xunit',Version='0.1.0.0-beta07') HTTP/1.1 DataServiceVersion: 1.0;NetFx MaxDataServiceVersion: 2.0;NetFx User-Agent: NuGet/2.8.50506.491 (Microsoft Windows NT 6.2.9200.0) Accept: application/atom+xml,application/xml Accept-Charset: UTF-8 Host: www.nuget.org Accept-Encoding: gzip, deflate -------------------------------- RESPONSE: -------------------------------- HTTP/1.1 200 OK Cache-Control: no-cache Content-Length: 4153 Content-Type: application/atom+xml;type=entry;charset=utf-8 Server: Microsoft-IIS/8.0 X-Content-Type-Options: nosniff DataServiceVersion: 2.0; X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET X-Frame-Options: deny X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff Strict-Transport-Security: maxage=31536000; includeSubDomains Date: Wed, 04 Mar 2015 21:52:17 GMT &lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;entry xml:base="https://www.nuget.org/api/v2/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"&gt;&lt;id&gt;https://www.nuget.org/api/v2/Packages(Id='Bau.XUnit',Version='0.1.0-beta07')&lt;/id&gt;&lt;category term="NuGetGallery.V2FeedPackage" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /&gt;&lt;link rel="edit" title="V2FeedPackage" href="Packages(Id='Bau.XUnit',Version='0.1.0-beta07')" /&gt;&lt;title type="text"&gt;Bau.XUnit&lt;/title&gt;&lt;summary type="text"&gt;&lt;/summary&gt;&lt;updated&gt;2014-12-01T21:51:40Z&lt;/updated&gt;&lt;author&gt;&lt;name&gt;Adam Ralph, Bau contributors&lt;/name&gt;&lt;/author&gt;&lt;link rel="edit-media" title="V2FeedPackage" href="Packages(Id='Bau.XUnit',Version='0.1.0-beta07')/$value" /&gt;&lt;content type="application/zip" src="https://www.nuget.org/api/v2/package/Bau.XUnit/0.1.0-beta07" /&gt;&lt;m:properties&gt;&lt;d:Version&gt;0.1.0-beta07&lt;/d:Version&gt;&lt;d:NormalizedVersion&gt;0.1.0-beta07&lt;/d:NormalizedVersion&gt;&lt;d:Copyright&gt;Copyright (c) Bau contributors. (baubuildch@gmail.com)&lt;/d:Copyright&gt;&lt;d:Created m:type="Edm.DateTime"&gt;2014-12-01T21:51:40.407&lt;/d:Created&gt;&lt;d:Dependencies&gt;ScriptCs.Contracts:0.9.0:|Bau:0.1.0-beta07:&lt;/d:Dependencies&gt;&lt;d:Description&gt;A Bau task plugin for running xUnit.net tests. Require&amp;lt;Bau&amp;gt;().Xunit().Do(xunit =&amp;gt; xunit.UseExe("xunit.console.clr4.exe").RunAssemblies("Tests.dll")).Run();&lt;/d:Description&gt;&lt;d:DownloadCount m:type="Edm.Int32"&gt;339&lt;/d:DownloadCount&gt;&lt;d:GalleryDetailsUrl&gt;https://www.nuget.org/packages/Bau.XUnit/0.1.0-beta07&lt;/d:GalleryDetailsUrl&gt;&lt;d:IconUrl&gt;https://raw.githubusercontent.com/bau-build/bau/dev/assets/bau.128.png&lt;/d:IconUrl&gt;&lt;d:IsLatestVersion m:type="Edm.Boolean"&gt;false&lt;/d:IsLatestVersion&gt;&lt;d:IsAbsoluteLatestVersion m:type="Edm.Boolean"&gt;true&lt;/d:IsAbsoluteLatestVersion&gt;&lt;d:IsPrerelease m:type="Edm.Boolean"&gt;false&lt;/d:IsPrerelease&gt;&lt;d:Language&gt;&lt;/d:Language&gt;&lt;d:Published m:type="Edm.DateTime"&gt;2014-12-01T21:51:40.407&lt;/d:Published&gt;&lt;d:PackageHash&gt;8xLVSlIHbAgJ/BxwAhELOKYZZCKEnwP3IHRWHnvQyuen8gB0vONMdEuuuUoxfwhir6mK5dSPtst3baua3oARqA==&lt;/d:PackageHash&gt;&lt;d:PackageHashAlgorithm&gt;SHA512&lt;/d:PackageHashAlgorithm&gt;&lt;d:PackageSize m:type="Edm.Int64"&gt;41098&lt;/d:PackageSize&gt;&lt;d:ProjectUrl&gt;https://github.com/bau-build/bau/tree/dev/src/Bau.Xunit&lt;/d:ProjectUrl&gt;&lt;d:ReportAbuseUrl&gt;https://www.nuget.org/package/ReportAbuse/Bau.XUnit/0.1.0-beta07&lt;/d:ReportAbuseUrl&gt;&lt;d:ReleaseNotes&gt;&lt;/d:ReleaseNotes&gt;&lt;d:RequireLicenseAcceptance m:type="Edm.Boolean"&gt;false&lt;/d:RequireLicenseAcceptance&gt;&lt;d:Tags&gt;xunit xbehave test tests tdd bdd C# build script task target runner rake grunt gulp fake nake jake psake sake&lt;/d:Tags&gt;&lt;d:Title&gt;Bau.Xunit&lt;/d:Title&gt;&lt;d:VersionDownloadCount m:type="Edm.Int32"&gt;0&lt;/d:VersionDownloadCount&gt;&lt;d:MinClientVersion&gt;&lt;/d:MinClientVersion&gt;&lt;d:LastEdited m:type="Edm.DateTime" m:null="true" /&gt;&lt;d:LicenseUrl&gt;https://github.com/bau-build/bau/blob/dev/license.txt&lt;/d:LicenseUrl&gt;&lt;d:LicenseNames&gt;&lt;/d:LicenseNames&gt;&lt;d:LicenseReportUrl&gt;&lt;/d:LicenseReportUrl&gt;&lt;/m:properties&gt;&lt;/entry&gt;&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"&gt;&lt;m:code&gt;500&lt;/m:code&gt;&lt;m:message xml:lang="en-US"&gt;An error occurred while trying to write an error payload.&lt;/m:message&gt;&lt;m:innererror&gt;&lt;m:message&gt;Cannot transition from state 'Completed' to state 'Error'. Nothing further can be written once the writer has completed.&lt;/m:message&gt;&lt;m:type&gt;Microsoft.Data.OData.ODataException&lt;/m:type&gt;&lt;m:stacktrace&gt; at Microsoft.Data.OData.ODataWriterCore.Microsoft.Data.OData.IODataOutputInStreamErrorListener.OnInStreamError()&amp;#xD; at Microsoft.Data.OData.Atom.ODataAtomOutputContext.WriteInStreamErrorImplementation(ODataError error, Boolean includeDebugInformation)&amp;#xD; at Microsoft.Data.OData.Atom.ODataAtomOutputContext.WriteInStreamError(ODataError error, Boolean includeDebugInformation)&amp;#xD; at System.Data.Services.ErrorHandler.WriteErrorWithFallbackForXml(ODataMessageWriter messageWriter, Encoding encoding, Stream responseStream, HandleExceptionArgs args, ODataError error, MessageWriterBuilder messageWriterBuilder)&lt;/m:stacktrace&gt;&lt;/m:innererror&gt;&lt;/m:error&gt; -------------------------------- </code></pre> </div> <p>Clearly something was going horribly wrong when trying to request this package from the API.</p> <p>Note that in the response, the package is referred to as <code class="highlighter-rouge">Bau.XUnit</code> which means that there are still traces of the old, incorrectly, cased package ID somewhere in the API.</p> <p>So it seems like some parts of the system are referring to the package as <code class="highlighter-rouge">Bau.Xunit</code> (correct) and others are still referring to it as <code class="highlighter-rouge">Bau.XUnit</code> (incorrect). I suspect this is the root of the problem.</p> <p>I’ve asked NuGet support to look into the problem since, as it stands, the Bau.Xunit package is completely out of action :scream:.</p> <hr /> <h3 id="update">Update</h3> <p><em>05 Mar 2015</em></p> <p>I’ve <a href="https://github.com/NuGet/NuGetGallery/issues/2379">raised a bug</a> on the NuGet Gallery GitHub repo to try and get some more eyes on this. Ultimately this ought to be fixed, but in the meantime I’m hoping some keen eyed developers can spot a workaround that could be done by the support team.</p> <hr /> <h3 id="update-1">Update</h3> <p><em>05 Mar 2015</em></p> <p>Until the issue has been resolved, I’ve uploaded a temporary stand in package named <a href="https://www.nuget.org/packages/Bau.Xunit.Temp">Bau.Xunit.Temp</a>. You can switch your project to using this package whilst the saga continues.</p> Guard Clause Abuse 2014-12-21T00:00:00+00:00 http://blog.adamralph.com/2014/12/21/guard-clause-abuse// <p>Today sees the release of version 0.10 of the mighty <a href="https://www.nuget.org/packages?q=liteguard">LiteGuard</a> library.</p> <p>After announcing the release on <a href="https://jabbr.net/#/rooms/general-chat">JabbR</a>, I found myself considering and commenting on the common abuse of guard clauses that I see so very often:</p> <!--excerpt--> <div class="highlighter-rouge"><pre class="highlight"><code>public class Car { private readonly string numberPlate: public Car(string numberPlate) { Guard.AgainstNull("numberPlate", numberPlate); this.numberPlate = numberPlate; } public string NumberPlate { get { return this.numberPlate; } } } </code></pre> </div> <p>(I’m using the British English term “number plate”, known as “license plate” in the US and some other countries.)</p> <p>The above code should not be using a guard clause and should not raise an <code class="highlighter-rouge">ArgumentNullException</code>. It should throw some kind of model* exception, since it is encapsulating a rule which has been identified in the domain of the application. The code is not de-referencing <code class="highlighter-rouge">numberPlate</code> so it doesn’t need a guard clause:</p> <div class="highlighter-rouge"><pre class="highlight"><code>public class Car { private readonly string numberPlate: public Car(string numberPlate) { if (numberPlate == null) { throw new UsedCarDealershipException("The number plate is missing."); } this.numberPlate = numberPlate; } public string NumberPlate { get { return this.numberPlate; } } } </code></pre> </div> <p>* <em>Feel free to substitute the word ‘model’ for ‘business’, according to taste</em> :wink:.</p> No Tech Lead 2014-03-15T00:00:00+00:00 http://blog.adamralph.com/2014/03/15/no-tech-lead// <p>I’ve had many discussions about the notion of a ‘tech lead’ in a team of developers. It’s something I feel passionate about as it affects both me personally and the entire software development industry.</p> <p>Yesterday, I read the blog post <a href="https://medium.com/p/948b2b806d86">Good Tech Lead, Bad Tech Lead</a> by <a href="https://medium.com/@jliszka">Jason Liszka</a>. Jason’s post offers me an ideal reference to argue my views and has prompted me to write this post, for which I’m grateful. This post is at least partially formed as a reaction to Jason’s post, which I think is very good and highlights some very important points for a tech lead, <em>assuming a tech lead exists</em>.</p> <p>I challenge the ‘tech lead’ model and propose that a ‘no tech lead’ model with a complete focus on teams opposed to individuals is way more productive and a lot more rewarding not just for team members who would otherwise be followers, but for the team member who would otherwise be leading.</p> <!--excerpt--> <p>I was a tech lead for a number of years. During that time I believed that the model of a more qualified individual leading with less qualified developers following was a natural method of organisation and I felt qualified to be the tech lead. It was an informal position since I had no official ‘lead’ title nor subordinates in the org chart but it felt productive and I certainly used it when it came to compensation negotiation.</p> <p>For the last few years, I’ve been involved in a far more productive and rewarding model of team organisation. In my current team, we have no tech lead. We have team members with substantially different levels of experience but we have not used this as a means to ‘grade’ team members and identify a tech lead. Instead, we’ve used the differences to improve the team as a whole by spreading knowledge by use of various practices, gradually smoothing out differences in skills and experience whilst recognising that individuals will always differ in certain ways and using those lasting differences in positive ways.</p> <p>This has been far more rewarding for me since I now work in a team which has better levels of experience as a whole. I have far more trust in my team mates. When I am on leave, I have absolute confidence that the team can functionally equally well in my absence (with an according reduction in capacity due to a missing team member). I learn way more since two way communication and learning is continuous throughout the entire team.</p> <p>One of the motivations for the ‘tech lead’ model is career progression. Career progression may be important for an individual but it has nothing to do with maximising the productivity of a development team. Skewing the organisation of a development team to meet career progression aspirations of an individual can only be negative for a team.</p> <p>Compensation justification can often be a major factor and indeed I used my tech lead position to drive my compensation upward in the past. In my view compensation should be something considered outside the <em>organisation</em> of a team. Individuals should be compensated according to the value they provide to the team and can also be affected by their relative level of experience and their individual desirability to other companies. Indeed a ‘no tech lead’ team will evolve to smooth out differences in individuals and should therefore smooth out differences in compensation.</p> <p>One of the major obstacles is the traditional company hierarchy. Most companies are still formed as vertically oriented hierarchies and people have to be seen to be progressing up org charts in order to justify better compensation. Some companies, such as GitHub, have demonstrated that there is no need for this. However, in most companies, there is still a need for line managers but that is a separate discussion and the reasons are highly removed from anything technical. When this notion of hierarchy leaks down to the technical level, the practices of a team are skewed inappropriately and the effect is counter-productive.</p> <p><a id="criticism"></a>I will now criticise each section of Jason’s post from my ‘no tech lead’ view.</p> <h3 id="teamwork">Teamwork</h3> <blockquote> <p>Good tech leads act as a member of the team, and consider themselves successful when the team is successful. They take their share of unsexy grungy work and clear roadblocks so their team can operate at 100%. They work to broaden the technical capabilities of their team, making sure knowledge of critical systems is not concentrated in one or two minds.</p> </blockquote> <blockquote> <p>Bad tech leads take the high-profile tasks for themselves and are motivated by being able to take credit for doing the work. They optimize locally, keeping team members working on projects that benefit the team at the expense of the engineering organization at large.</p> </blockquote> <p>Everything listed in ‘good’ is what <em>every</em> member of the team should be doing.</p> <p>Everything listed in ‘bad’ outlines some typical problems of <em>having</em> a tech lead.</p> <h3 id="technical-vision">Technical vision</h3> <blockquote> <p>Good tech leads have an overall vision for the technical direction of the product and make sure the team understands it. They delegate feature areas to other team members and let them own their decisions. They recognize that their team members are smart, trust them, and rely on them to handle significant pieces of the project.</p> </blockquote> <blockquote> <p>Bad tech leads resist explaining or clarifying the technical direction and dictate decisions instead. They keep critical institutional knowledge in their heads, failing to multiply their effectiveness by creating and disseminating helpful documentation.</p> </blockquote> <p>If the team understand the technical vision then they share it. Why should one person be responsible for it rather than the whole team?</p> <p>Why delegate? Why does one person know best? The team can self organise. Command and control does not work for software development.</p> <p>The recognition that team members are smart, trusting them and relying on them is a relationship <em>all</em> team members should have with each other.</p> <p>Everything listed in ‘bad’ outlines some typical problems of <em>having</em> a tech lead.</p> <p>Technical direction and institutional knowledge are things that the team needs to share through appropriate activities, e.g. whiteboard sessions, backlog grooming. If these things are shared assets of the team then there is rarely a need to produce documentation describing them. Documentation which is regularly produced for this purpose is usually never read.</p> <h3 id="discussion-and-debate">Discussion and debate</h3> <blockquote> <p>Good tech leads listen and encourage debate. When the team is unable to resolve a debate, they describe a process or framework of thinking that would help them resolve it. They don’t enter discussions with foregone conclusions, and always allow themselves to be persuaded by great ideas.</p> </blockquote> <blockquote> <p>Bad tech leads allow debates to go on for too long without resolution, hampering the productivity of the team. Others cut off debate prematurely, dismissing new discussions by saying the matter is “already settled.” Bad tech leads believe it is more important that they win the argument than that the team reaches the right decision.</p> </blockquote> <p>All members of the team need to be doing the things in ‘good’.</p> <p>In my team we have heated debates and we always manage to settle them somehow. Sometimes, a team member takes the position of mediator and suggests a resolution to a particularly difficult conflict of views. This role emerges organically during the debate and is usually the best person to have taken that role for that given debate. This is better than appointing one person to always mediate regardless and avoids the problems listed in ‘bad’.</p> <h3 id="project-management">Project management</h3> <blockquote> <p>Good tech leads are proactive. They make sure technical progress is on track. They work with team members to come up with estimates and to establish intermediate milestones. They anticipate areas of concern and make sure they are addressed before they become a problem. They identify technical roadblocks and help the team get around them. They identify areas of overlap where work can be shared, and conversely, find areas that are not getting enough attention and direct resources toward it.</p> </blockquote> <blockquote> <p>Bad tech leads are reactive. They may delegate, but do not follow up to make sure progress is being made. They don’t set intermediate goals and hope that everything just comes together in the end. They wait until just before launch to do end-to-end tests of complex systems. They allow team members to waste time on interesting but unimportant work.</p> </blockquote> <p>The team should be addressing the things listed in ‘good’ together.</p> <p>There should be a shared vision of where the project is heading and this should be reinforced with activities such as whiteboard sessions with domain experts and product owners, and story mapping.</p> <p>The team should overcome technical roadblocks together.</p> <p>The team should have continuous knowledge of what everyone is working on making it easy to avoid duplicated effort.</p> <p>Backlog prioritisation and grooming will highlight areas which are not getting enough attention and the team can self organise to address them.</p> <h3 id="pragmatism">Pragmatism</h3> <blockquote> <p>Good tech leads are pragmatic and find a balance between doing it right and getting it done. They cut corners when it’s expedient but never out of laziness. They encourage their team to find temporary shortcuts or workarounds to problems that are blocking overall progress, and to build minimum viable infrastructure for launch. To good tech leads, details matter. Code quality, code reviews, and testing are just as important as shipping on time.</p> </blockquote> <blockquote> <p>Bad tech leads take shortcuts that save time in the short term but cost more in the long term, and let technical debt pile up. They cannot distinguish between situations that call for expediency and those that call for perfection.</p> </blockquote> <p>The team should be collectively pragmatic.</p> <p>The team should identify and agree pragmatic paths to progress during backlog grooming and whilst work is being done through continuous communication, reinforced with code reviews.</p> <p>Code quality, code reviews and testing are baked into the ‘definition of done’ agreed by the team.</p> <p>Management of technical debt and the balance between expediency and perfection are team exercises.</p> <h3 id="communication">Communication</h3> <blockquote> <p>Good tech leads know that their role is much more than writing code, that effective communication is a vital part of their job, and that time spent making their team more efficient is time well spent. They acknowledge that some communication overhead is necessary when working on a team, and they sacrifice some personal productivity for overall team productivity.</p> </blockquote> <blockquote> <p>Bad tech leads believe that they are most productive when they are writing code, and think communication is a distraction. They do not optimize for overall team productivity, but rather for what works best for themselves. They get frustrated when they have to take time to lead.</p> </blockquote> <p>Communication is not an overhead. It is the lifeblood of a team.</p> <p>A team which does not have enough communication is dysfunctional. The things listed in ‘good’ and ‘bad’ apply to a team, not an individual.</p> <h3 id="relationship-with-product">Relationship with Product</h3> <blockquote> <p>Good tech leads are in a conversation with product managers and designers about how the product should work. They are not afraid to push back on decisions they disagree with, but keep the product goals in mind and know when to accommodate them. They find creative workarounds to technical constraints by suggesting alternative product formulations that are less technically demanding, and help PMs and designers understand technical challenges so that they make informed trade-offs themselves.</p> </blockquote> <blockquote> <p>Bad tech leads throw product decisions “over the wall” and do not take ownership of the product. They push back due to technical constraints but do not offer alternatives or explanations.</p> </blockquote> <p>I would simply replace the term ‘tech lead’ with ‘team’ in this entire section.</p> <p>The team, and not an individual, should have a good relationship with the product manager and domain experts.</p> <p>During backlog grooming, when a story is identified as requiring more in depth conversation with the product owner or a domain expert, the team can self organise and assign the task to the most effective subset of the team for the given story.</p> <h3 id="resiliency">Resiliency</h3> <blockquote> <p>Good tech leads are resilient to changes to the product specification and react calmly to surprises. They anticipate where changes might take place and design their code to handle them.</p> </blockquote> <blockquote> <p>Bad tech leads are upset when the specification changes, or prematurely generalize their design in areas where changes are unlikely to occur.</p> </blockquote> <p>The team should be displaying all the qualities listed.</p> <p>Software architecture is not an up-front exercise but is an evolution. The team should own the architecture collectively and evolve it together during backlog grooming, whiteboards sessions, spikes and continuous communication.</p> <h2 id="personality">Personality</h2> <blockquote> <p>Good tech leads are easy-going but assertive. Bad tech leads are confrontational and aggressive. Good tech leads emerge naturally and earn respect through technical competence and experience. Bad tech leads think their title confers respect and authority. Good tech leads are always looking for ways to improve.</p> </blockquote> <blockquote> <p>Bad tech leads get defensive when given feedback. Good tech leads are humble and boost the confidence of everyone else on the team. Bad tech leads are arrogant and take pleasure in making their teammates feel inferior.</p> </blockquote> <p>The ‘good’ and ‘bad’ personality traits apply to all team members.</p> <p>Every team has members with (sometimes wildly) varying skills and levels of experience and these should be embraced as the team self organises rather than elevating an individual to a special status.</p> <p>Team members earn respect <em>from one another</em> through technical competence and experience.</p> <p>The team should always be searching for ways to share the knowledge of more experienced team members with those less experienced. Practices such as pair programming and gated commits spread knowledge through the team, avoid single person dependencies and accelerate the development of less experienced team members. This avoids inappropriate expectations of respect and authority, and feelings of arrogance.</p> Continuously Losing Internet Connectivity in Windows 8.1 2014-01-24T00:00:00+00:00 http://blog.adamralph.com/2014/01/24/continuously-losing-internet-connectivity-in-windows-8-1// <h3 id="tldr">TL;DR</h3> <ol> <li>Execute <code class="highlighter-rouge">netsh winsock reset</code></li> <li>Restart machine</li> </ol> <h3 id="the-excruciating-story-behind-this">The excruciating story behind this</h3> <p>This has been driving me nuts recently. On my two Windows 8.1 machines, I’ll be happily using the internet. You know browsing, pushing to GitHub, Tweeting, that kind of thing. Then all of a sudden everything would blow up. Browser tabs would hang trying to load pages. JabbR would go red. GitHub fetches would fail. On my one remaining Windows 7 machine all was good.</p> <!--excerpt--> <p>I found I could fix the problem by disconnecting and reconnecting my WiFi connection. After a while it would happen again. Worst of all, it’s been happening at just that frequency which is not quite enough for me to stop what I’m doing and sort out the problem once and for all, but just enough to very slowly accumulate a snowball of anger and hate which has been welling inside me for the past couple of weeks.</p> <p>Today it was finally too much so after a disconnect/reconnect I decided to sort out the problem once and for all. First of all, I found a thread telling me I should I should switch off ‘Allow the computer to turn off this device to save power’ for the network adapter. I tried that and everything looked fine for around 15 minutes until I lost my connectivity yet again. A deep breath and back to the drawing board.</p> <p>I then took my woes to <a href="https://jabbr.net/#/rooms/general-chat">#general-chat</a> over on JabbR where both <a href="https://twitter.com/bryan_wood">@bryan_wood</a> and <a href="https://twitter.com/davepermen">@davepermen</a> suggested it may be a DNS problem. This sounded likely since I do remember the browser status spending a while on ‘Resolving host’. So I pinged google.com, grabbed the IP and waited patiently for my internet stack to fall over again which of course it politely obliged. I found I could still ping the IP which again hinted at a DNS problem. I then ran <code class="highlighter-rouge">ipconfig /flushdns</code> and hey presto! I could browse sites again.</p> <p>Having narrowed it down to a local problem (I had suspected my WiFi router which is way past its prime) I continued my search for problems specifically regarding Windows 8.1 and DNS. Lo and behold the internet is full of people complaining about this. There was even a question raised on <a href="http://answers.microsoft.com/en-us/windows/forum/windows8_1-performance/dns-issues-since-upgrading-to-81/8e5b5a68-e8e6-462a-b53e-cde8915e82df">answers.microsoft.com</a> in October last year with still no resolution from Microsoft.</p> <p>More Googling, and I found this little gem which told me all I had to do was run <code class="highlighter-rouge">netsh winsock reset</code> and restart my machine. <strong>Any by god it seems to be working</strong>. Around 2 hours continuous, flawless connectivity which is an order of magnitude longer than I’ve experienced in the last fortnight. I’m not out of the woods yet, but I’m optimistic.</p> <h3 id="what-does-the-command-do">What does the command do?</h3> <p>A good question and I had absolutely no idea until I started digging around (I’m a netsh noob). <code class="highlighter-rouge">netsh winsock reset ?</code> reveals that the command</p> <div class="highlighter-rouge"><pre class="highlight"><code>Resets Winsock Catalog to a clean state. All Winsock Layered Service Providers which were previously installed must be reinstalled. This command does not affect Winsock Name Space Provider entries. </code></pre> </div> <p>According to <a href="http://en.wikipedia.org/wiki/Winsock#Microsoft_implementations">Wikipedia</a></p> <blockquote> <p>Winsock 2 is extensible by a mechanism known as a Layered Service Provider (LSP). Winsock LSPs are available for a wide range of useful purposes, including Internet parental controls, web content filtering, QoS etc. The layering order of all providers is kept in the Winsock Catalog. In previous versions of Windows, removing a buggy LSP could result in corruption of the Winsock catalog in the registry, potentially resulting in a loss of all network connectivity. Winsock in Windows XP Service Pack 2, Windows Server 2003 Service Pack 1 and all later Windows operating systems has the ability to self-heal after a user uninstalls such an LSP.</p> </blockquote> <p>So it seems there is probably a buggy LSP sitting in there which is probably added by Windows 8.1 or at least one of the apps it comes bundled with. I guess it could also be a third party app but given so many people have had the problem and that it only manifests in Windows 8.1 and not Windows 8, suggests it’s something fairly close to the metal in the Windows 8.1 distribution. Let’s hope MS fix this soon so this blog post becomes redundant.</p> Decoupling Apps from Config with ConfigR 2013-12-10T00:00:00+00:00 http://blog.adamralph.com/2013/12/10/decoupling-apps-from-config-with-configr// <p>ConfigR is a <a href="https://www.nuget.org/packages/ConfigR/">NuGet</a> package based on <a href="http://scriptcs.net/">scriptcs</a> and <a href="http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx">Roslyn</a> which allows writing configuration files in C# instead of XML.</p> <p>For example, the stringy XML soup:</p> <div class="language-xml highlighter-rouge"><pre class="highlight"><code> <span class="cp">&lt;?xml version="1.0"?&gt;</span> <span class="c">&lt;!-- app.config or web.config --&gt;</span> <span class="nt">&lt;configuration&gt;</span> <span class="nt">&lt;appSettings&gt;</span> <span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Uri"</span> <span class="na">value=</span><span class="s">"http://tempuri.org/myservice"</span><span class="nt">/&gt;</span> <span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Timeout"</span> <span class="na">value =</span><span class="s">"10000"</span><span class="nt">/&gt;</span> <span class="nt">&lt;appSettings&gt;</span> <span class="nt">&lt;/configuration&gt;</span> </code></pre> </div> <p>can become</p> <pre><code class="language-C#">// MyApp.exe.csx or Web.csx Add("Uri", new Uri("http://tempuri.org/myservice")); Add("Timeout", 10000); </code></pre> <p>In the real world configuration is often more complex than this, e.g. multiple dev, test and production environments, per machine configuration, etc.</p> <!--excerpt--> <p>In their great book, <em><a href="http://www.amazon.co.uk/gp/product/0321601912/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;camp=1634&amp;creative=6738&amp;creativeASIN=0321601912&amp;linkCode=as2&amp;tag=adamralphcom-21">Continuous Delivery</a></em>*, Jez Humble and David Farley describe how such configuration can be injected into your application at <strong>build time</strong>, <strong>packaging time</strong>, <strong>deployment time</strong> and <strong>run time</strong>.</p> <h3 id="run-time-configuration">Run Time Configuration</h3> <p>The most flexible of these strategies is <strong>run time</strong> configuration which allows identical building, packaging and deployment of your application in all environments by deferring all configuration decisions until run time. This is easy to achieve using ConfigR since the configuration script is executed at runtime and allows any C# you like, as long as it can be executed by scriptcs**.</p> <p>First, let’s see what the application has to do to retrieve one of the configuration values:</p> <pre><code class="language-C#">var uri = Configurator.Get&lt;Uri&gt;("Uri"); </code></pre> <p>In the simple case above, we chose to hardcode our URI into the configuration script. Later, we may choose to look up the URI from a database based on the current machine name:</p> <pre><code class="language-C#">// MyApp.exe.csx or Web.csx using System.Data.SqlClient; using(var connection = new SqlConnection("...")) { connection.Open(); using(var command = connection.CreateCommand()) { command.CommandText = "select Uri from Uris where Machine = '{@Machine}'"; command.Parameters.AddWithValue( "Machine", Environment.MachineName); Add("Uri", new Uri(command.ExecuteScalar().ToString())); } } </code></pre> <p>Let’s see how the application code has to change in order to support this:</p> <pre><code class="language-C#">var uri = Configurator.Get&lt;Uri&gt;("Uri"); </code></pre> <p>The application code hasn’t changed at all. This because the application is completely <em>decoupled</em> from the choice of configuration strategy.</p> <p>Later, we may wish to fetch our configuration from the file system or from an HTTP endpoint, perhaps based not only on the machine name but the current application version, or the current user. We may invent other weird and wonderful configuration strategies.</p> <p>By implementing your configuration strategy in a ConfigR configuration script, <em>the application code never needs to change.</em></p> <p>Among other features, ConfigR allows loading of config scripts from custom file locations or over HTTP and nested loading of scripts from within scripts. The project is under active development and plenty more features are planned. For more information see the <a href="https://github.com/config-r/config-r/wiki/Quickstart">quickstart</a>, <a href="https://github.com/config-r/config-r-samples">samples</a>, <a href="https://github.com/config-r/config-r/wiki">wiki</a> and <a href="https://github.com/config-r/config-r/issues?direction=desc&amp;sort=updated&amp;state=open">issues</a>. Keep an eye out for further ConfigR blog posts by subscribing to the <a href="/feed/">feed</a>. ConfigR is available at <a href="https://www.nuget.org/packages/ConfigR/">NuGet</a> and can be forked at <a href="https://github.com/config-r/config-r">GitHub</a>.</p> <p>* Humble, J. &amp; Farley D. (2011). <em>Continuous Delivery.</em> Boston: Pearson Education, Inc.</p> <p>** At the time of writing, scriptcs will run any valid C# with the exception of <code class="highlighter-rouge">async/await</code> and <code class="highlighter-rouge">dynamic</code> due to limitations in the current release of Roslyn.</p> NDC Diary - Day 3 2013-12-06T00:00:00+00:00 http://blog.adamralph.com/2013/12/06/ndc-diary-day-3// <p>The incrementation of hangover severity continued this morning after <a href="https://twitter.com/stack72">@stack72</a> dragged everyone to Shoreditch last night and <a href="https://twitter.com/JakeGinnivan">@JakeGinnivan</a> introduced me to 35% beer. A great night all round.</p> <p><img src="http://adamralph.com/img/nancy-stickers-everywhere.jpg" alt="Nancy stickers... everywhere" /></p> <p>The prize for sticker bombing goes to <a href="https://twitter.com/randompunter">@randompunter</a> (<a href="https://twitter.com/NancyFx">@NancyFx</a>) with <a href="https://twitter.com/roysvork">@roysvork</a> (<a href="http://superscribe.org/">Superscribe</a>) the runner up.</p> <h3 id="recording-a-net-rocks-show-with-glenn-block-and-justin-rusbatch">Recording a .NET Rocks! show with <a href="https://twitter.com/gblock">Glenn Block</a> and <a href="https://twitter.com/jrusbatch">Justin Rusbatch</a></h3> <p>Glenn and Justin were kind enough to invite me to join them this morning in recording a show about <a href="http://scriptcs.net/">scriptcs</a> for <a href="http://www.dotnetrocks.com/">.NET Rocks!</a> with <a href="https://twitter.com/richcampbell">Richard Campbell</a> and <a href="https://twitter.com/carlfranklin">Carl Franklin</a>. We had a lot of fun recording the show and I hope I managed to add something useful. Look out for the show at <a href="http://www.dotnetrocks.com/">.NET Rocks!</a> in early January.</p> <!--excerpt--> <h3 id="the-future-of-c-with-mads-torgersen">The Future of C# with Mads Torgersen</h3> <p>The NDC Wi-Fi was put to the test during this talk with the C# Twitter stream seeing an explosion in traffic as Mads revealed, for the first time publicly, a range of features either set to be delivered in the next version of C# or at least under strong consideration.</p> <p>Unfortunately I was too caught up in the excitement to note everything down but after mining my memory and the Twitter feed, here is the list of implemented/potential language features (I’m sure I’m missing at least one of them):</p> <ul> <li>primary constructors - <code class="highlighter-rouge">public class Point(int x, int y) { }</code></li> <li>read only auto-properties - <code class="highlighter-rouge">public int X { get; } = x;</code></li> <li>static type using statements - <code class="highlighter-rouge">using System.Math;</code></li> <li>property expressions - <code class="highlighter-rouge">public double Distance =&gt; Sqrt(X * X + Y * Y);</code></li> <li>method expressions - <code class="highlighter-rouge">public Point Move(int dx, int dy) =&gt; new Point(X + dx, Y + dy);</code></li> <li>params for enumerable types - <code class="highlighter-rouge">public Point Average(params IEnumerable&lt;Point&gt; points) { }</code></li> <li>monadic null checking - <code class="highlighter-rouge">if (points?.FirstOrDefault()?.X ?? -1) { }</code></li> <li>constructor type parameter inference - <code class="highlighter-rouge">var t = new Tuple(1,2); // infers Tuple&lt;T1, T2&gt;</code></li> <li>inline declarations for out params - <code class="highlighter-rouge">public void Foo(out var x, out var y) { }</code></li> </ul> <p>I love all these features perhaps with the exception of the last one. Whist it doesn’t negatively effect the language (although some of the audience were concerned about encouraging the use of <code class="highlighter-rouge">out</code> parameters) it doesn’t really do much to achieve the stated goal of improving syntax for multiple return values. I was hoping for something like:</p> <pre><code class="language-C#">public (int x, int y) Foo() { } </code></pre> <p>But still, the rest of the features look awesome and I can’t wait to get my hands on C# vNext!</p> <h3 id="nodejs-vs-c-cage-match-with-rob-ashton-and-jeremy-miller">NodeJS vs C# Cage Match with <a href="https://twitter.com/RobAshton">Rob Ashton</a> and <a href="https://twitter.com/jeremydmiller">Jeremy Miller</a></h3> <p>Rob spun my head as usual. Jeremy quietly impressed me. I didn’t understand much of what Rob did, but I’m sure he doesn’t care. He seemed to be enjoying himself.</p> <p>Jeremy’s work in FubuMVC to give it a Rails-like management system using Rake is impressive and shows just what Rake can do when put to work. This was particularly interesting for me since I’ve recently started work on a C# Rake equivalent named <a href="https://github.com/bau-build/bau">Bau</a>, based on Roslyn and scriptcs. If I can get Bau to the level where it could potentially be used for FubuMVC instead of Rake that would be quite an achievement and has definitely given me something to aim for.</p> <h3 id="signalr-with-damian-edwards-and-david-fowler">SignalR with <a href="https://twitter.com/DamianEdwards">Damian Edwards</a> and <a href="https://twitter.com/davidfowl">David Fowler</a></h3> <p>The dynamic duo took centre stage again and didn’t fail to impress with another whirlwind live coding tour of the huge capabilities of SignalR, including a nice little off-script foray by David into Nancy hosted with nowin. Equally impressive was the way Damian and David seemed to have bravely shrugged off the embarrassment suffered at the pool table at the hands of <a href="https://twitter.com/jchannon">@jchannon</a> and myself just two days earlier.</p> <h3 id="scriptcs-with-justin-rusbatch">scriptcs with <a href="https://twitter.com/jrusbatch">Justin Rusbatch</a></h3> <p>And the holy trinity is finally complete. I’m now one of the lucky few that has seen scriptcs presented by all three of the owners independently.</p> <p>One thing that really stood out in Justin’s talk was how <em>blazingly fast</em> he had scriptcs running in his demos. I couldn’t help asking about this during the talk and it turns out Justin completely threw the rule book out of the window and compiled a custom version of scriptcs right before his talk with some new compiler switches introduced in .NET 4.5. Wow, this is the definition of courage! I can’t wait to see those compiler switches make it into the published package.</p> <h3 id="ndc-london-the-conclusion">NDC London: The Conclusion</h3> <p>What a week. Remember those times you went on holiday as a child and you never wanted it to end? That’s me right now. NDC London was my first conference and the decision to attend was one of the best I’ve made in a while.</p> <p>Would I recommend it to others? Definitely. Will I be back? Yes. Where and when? Next year in Oslo. My biggest takeaway from the conference? All the awesome people I met, many of whom I’ve chatted and coded with virtually but never before met in person and others I’ve admired and followed over the years and was honoured to finally meet in person. And with that I leave you with my complete ‘names to faces checklist’, collected each day to show just how many great people you can meet at an event like the NDC. Bye bye NDC London. Hello NDC Oslo…</p> <p>Until next time (apologies to anyone I’ve missed): <a href="https://twitter.com/mat_mcloughlin">@mat_mcloughlin</a> <a href="https://twitter.com/venkat_s">@venkat_s</a> <a href="https://twitter.com/jezhumble">@jezhumble</a> <a href="https://twitter.com/randompunter">@randompunter</a> <a href="https://twitter.com/jrusbatch">@jrusbatch</a> <a href="https://twitter.com/roysvork">@roysvork</a> <a href="https://twitter.com/stevensanderson">@stevensanderson</a> <a href="https://twitter.com/serialseb">@serialseb</a> <a href="https://twitter.com/darrel_miller">@darrel_miller</a> <a href="https://twitter.com/shiftkey">@shiftkey</a> <a href="https://twitter.com/JakeGinnivan">@JakeGinnivan</a> <a href="https://twitter.com/Ben_Hall">@Ben_Hall</a> <a href="https://twitter.com/chrissie1">@chrissie1</a> <a href="https://twitter.com/jchannon">@jchannon</a> <a href="https://twitter.com/Cranialstrain">@Cranialstrain</a> <a href="https://twitter.com/gblock">@gblock</a> <a href="https://twitter.com/jonskeet">@jonskeet</a> <a href="https://twitter.com/dhelper">@dhelper</a> <a href="https://twitter.com/stack72">@stack72</a> <a href="https://twitter.com/paulcbetts">@paulcbetts</a> <a href="https://twitter.com/davidfowl">@davidfowl</a> <a href="https://twitter.com/DamianEdwards">@DamianEdwards</a> <a href="https://www.google.co.uk/search?q=mads+torgensen">Mads Torgensen</a> <a href="https://twitter.com/scottgu">@scottgu</a> <a href="https://twitter.com/unclebobmartin">@unclebobmartin</a> <a href="https://twitter.com/BrockLAllen">@BrockLAllen</a> <a href="https://twitter.com/citizenmatt">@citizenmatt</a> <a href="https://twitter.com/RemoGloor">@RemoGloor</a> <a href="https://twitter.com/richcampbell">@richcampbell</a> <a href="https://twitter.com/carlfranklin">@carlfranklin</a></p> NDC Diary - Day 2 2013-12-05T00:00:00+00:00 http://blog.adamralph.com/2013/12/05/ndc-diary-day-2// <p>I woke up this morning with a slightly larger hangover than yesterday and a nasty lack of sleep but it was offset with the fond memory of pwning <a href="https://twitter.com/DamianEdwards">@DamianEdwards</a> and <a href="https://twitter.com/davidfowl">@davidfowl</a> on the pool table.</p> <h3 id="cloud-apps-on-azure-with-scott-guthrie">Cloud Apps on Azure with <a href="https://twitter.com/scottgu">Scott Guthrie</a></h3> <p>Azure is an interesting one for me. Whilst I can see that cool stuff is being done on Azure, I’m still reluctant to treat Azure as anything other than ‘yet another service provider’. The reason for this is the maintenance of portability and, ultimately, choice. I use an Azure VM as a build server. I might use one to host a blog. I might use one to host some windows services. I might use Azure websites to host an IIS website. All of these things I could potentially pick up and move elsewhere should I become dissatisfied with Azure for some reason. If I wrote my code against Azure specific SDK’s then I would lose this portability. I’d be locking myself into Azure. Somehow this just doesn’t sit right with me and I can’t bring myself to do it. This is a shame, because there’s so much cool stuff in Azure and I’d like to be able to use it. But the platform and vendor lock in just scares me off. The awesome content which the red-shirted maestro presented today makes the two opposing considerations pull at me even harder.</p> <!--excerpt--> <h3 id="battle-of-the-mocking-frameworks-with-dror-helper">Battle of the Mocking Frameworks with <a href="https://twitter.com/dhelper">Dror Helper</a></h3> <p>As an owner of <a href="http://fakeiteasy.github.io/">FakeItEasy</a>, how could I not attend this talk? Dror prepared a thorough examination of most of the popular mocking frameworks including comparisons on aspects which I never would have thought of myself, such as deployment. I didn’t realise deployment has such an impact with the commercial offerings. The API and test fragility comparisons were mostly familiar to me, with the exception of a brief shock at seeing the MS Fakes syntax. The conclusion of the battle was, as I expected, ‘it depends’ but I’m glad to report that FakeItEasy did very well indeed and scored highly in all aspects. There was one clear loser however, and I’m afraid to say it was MS Fakes.</p> <h3 id="functional-programming-with-uncle-bob">Functional programming with <a href="https://twitter.com/unclebobmartin">Uncle Bob</a></h3> <p>The chance to see Uncle Bob in the flesh was too much to resist. I’d previously watched Bob’s ‘Expectations from your new CTO’ talk over a live web cast and found it hugely entertaining. Uncle Bob has a habit of spending the first few minutes speaking about something not directly related to the topic. In the previous talk it was the speed of light, in this talk it was quantum interference patterns. I’ve no idea why he does this but it’s a lot fun. As a seasoned master, Uncle Bob typically uses computing history to lay the foundations of his message. Today he gave us a fascinating tour of the astounding level of hardware innovation over the last 60 years and starkly contrasted that with the lack of innovation in programming languages which, ultimately, haven’t changed that much. Did he get his message across? Yes. Functional programming was the first of three language revolutions, the others being OO and structural. Bob convinced me how important functional programming languages are becoming right now and will be in the future and I left the talk wanting to learn a functional language!</p> <h3 id="clojure-coding-dojo-with-rob-ashton">Clojure Coding Dojo with <a href="https://twitter.com/RobAshton">Rob Ashton</a></h3> <p>So, I left Uncle Bob’s talk wanting to learn a functional language, and as I walked into the main hall I found Rob Ashton sitting at a table with his laptop, wanting to teach me a functional language, Clojure. So a few of us gathered round Rob’s laptop for an spontaneous coding dojo and the VIM maestro went to work. The challenge: produce an infinite series of simulated stock price ticks, with up/down market price movement. This was my first coding dojo and the unexpected part was when we had to rotate the typist, one function each. What this means is that you don’t just learn the language, but you also learn the tools. Rob’s tool is VIM. I was like a fish out of water but I struggled through my turn on the keyboard and was happy to have a working function when I was done. As time ran out and the next talk was due, Rob took over and finished things off and our target was achieved!</p> <h3 id="f-and-ddd-with-scott-wlaschin">F# and DDD with <a href="https://twitter.com/ScottWlaschin">Scott Wlaschin</a></h3> <p>Continuing my new found determinism to learn a functional language I headed over to learn how to use F# in DDD. After a few DDD basics which I was largely familiar with, my illusions about the F# type system were completely blown away. I had naively assumed that F# types were largely equivalent to C# classes but that couldn’t be further from the truth. My criticism of the talk is that the examples shown were somewhat anaemic in terms of DDD. They were more about data structure than behaviour. However, I’m now intrigued as to how I might implement a domain in one of my real projects in F#, in combination with things like CQRS and event sourcing. To be continued…</p> <p><strong>Tomorrow:</strong> Recording a show about scriptcs for DotNetRocks with <a href="https://twitter.com/gblock">@gblock</a> and <a href="https://twitter.com/jrusbatch">@jrusbatch</a></p> <h2 id="names-to-faces-checklist">Names to faces checklist</h2> <p><a href="https://twitter.com/scottgu">@scottgu</a> <a href="https://twitter.com/unclebobmartin">@unclebobmartin</a> <a href="https://twitter.com/BrockLAllen">@BrockLAllen</a> <a href="https://twitter.com/citizenmatt">@citizenmatt</a> <a href="https://twitter.com/RemoGloor">@RemoGloor</a></p> NDC Diary - Day 1 2013-12-04T00:00:00+00:00 http://blog.adamralph.com/2013/12/04/ndc-diary-day-1// <p>After nursing a slight hangover inflicted by <a href="https://twitter.com/serialseb">@serialseb</a>’s drinks night in Soho, I somehow managed to drag myself in well before 09:00 to finish off and push yesterday’s diary entry before proceedings began.</p> <h3 id="keynote---jackstones-the-journey-to-mastery-with-dan-north">Keynote - Jackstones: the Journey to Mastery with <a href="https://twitter.com/tastapod">Dan North</a></h3> <p>I really enjoyed Dan’s talk. Although I’d commonly heard the term ‘journeyman’ before I’d never really known what it meant and today I found out. I also found out that the vast majority of people I deal with in the software development world (including myself), are themselves journeymen with, thankfully, a few apprentices thrown in here and there. I’d like to think I work with some masters too but my suspicion is that they are very few and far between. A really well delivered talk from Dan.</p> <!--excerpt--> <h3 id="geteventstore-with-liam-westley">GetEventStore with <a href="https://twitter.com/westleyl">Liam Westley</a></h3> <p>In may day job we use event sourcing heavily. Our typical data flow takes the form:</p> <div class="highlighter-rouge"><pre class="highlight"><code>external client -&gt; Nancy web API -&gt; RabbitMQ -&gt; command handler -&gt; domain mutation -&gt; domain event -&gt; NEventStore -&gt; SQL Server -&gt; RabbitMQ -&gt; event handler -&gt; read model view -&gt; MongoDB -&gt; Nancy web API -&gt; external client </code></pre> </div> <p>After seeing <a href="http://geteventstore.com/">GetEventStore</a> in action I can see how this could easily be reduced to:</p> <div class="highlighter-rouge"><pre class="highlight"><code>external client -&gt; Nancy web API -&gt; RabbitMQ -&gt; command handler -&gt; domain mutation -&gt; domain event -&gt; GetEventStore -&gt; Nancy web API -&gt; external client </code></pre> </div> <p>I.e. the second line of our current flow could be removed completely.</p> <p>A few people seemed to be expecting an explanation of what event sourcing is from this talk but I think Liam did the right thing and kept that out of it. The subject would have drowned the purpose of the talk which was to demonstrate GetEventStore and I think Liam did that well.</p> <h3 id="scriptcs-with-glenn-block">scriptcs with <a href="https://twitter.com/gblock">Glenn Block</a></h3> <p>I’ve been quite involved in <a href="http://scriptcs.net/">scriptcs</a>, having contributed a little code and a few ideas to the project. I also own some OSS projects which are built over scriptcs (<a href="https://github.com/config-r/config-r">ConfigR</a>, <a href="https://github.com/adamralph/scriptcs-nancy">ScriptCs.Nancy</a> and in very early stages - <a href="https://github.com/bau-build/bau">Bau</a>).</p> <p>Glenn is a confident and entertaining speaker which makes his talks really enjoyable. It was great to hear about scriptcs from another of the founding fathers, after seeing <a href="https://twitter.com/filip_woj">@filip_woj</a> in action in Zurich earlier this year. After <a href="https://twitter.com/jrusbatch">@jrusbatch</a>’s talk on Friday that will make a complete set. Unfortunately a nasty bite was taken out of Glen’s talk with an internet connection problem, which Glen’s talk relied on, so a bit of content had to be skipped and the demos were thrown a little off tack as a result but Glen dealt with it well and didn’t let it spoil the talk.</p> <h3 id="hypermedia-with-darrel-miller">Hypermedia with <a href="https://twitter.com/darrel_miller">Darrel Miller</a></h3> <p>The missing link(!) in most web API’s is hypermedia. To paraphrase Roy Fielding, if you’re not doing hypermedia then you’re not doing REST. Hypermedia is not optional. Why is it missing? I think the main reason is that people conflate HTTP with REST and once they’re serving stuff over HTTP without using SOAP, they think they’re doing REST. They don’t know <em>why</em> they should do hypermedia. Another reason is that no one’s really sure <em>how</em> to do it. Darrel has made a really good shot at solving this problem, at least for Microsoft’s WebAPI world, with his <a href="https://github.com/tavis-software/Link">Link</a> library. Later I talked to Darrel about taking things further and lifting the knowledge about links between resources out of controllers to avoid duplication and thin out controllers and this is something he’s also keen to explore.</p> <h3 id="semantics-with-jon-skeet">Semantics with <a href="https://twitter.com/jonskeet">Jon Skeet</a></h3> <p>This is the first time I’d seen Jon Skeet talk and he lived up to his reputation. Jon is an intelligent, articulate and knowledgeable speaker and a joy to watch. I share Jon’s opinion on the phrase “that doesn’t matter, it’s just semantics”. People seem to use this is a sweeping “that’s not important” argument killer but it’s never something I’ve really understood. Jon makes the point that semantics is meaning, and meaning is everything, so when people say this they are saying “that doesn’t matter, it’s just everything’.</p> <h3 id="noda-time-with-jon-skeet">Noda Time with <a href="https://twitter.com/jonskeet">Jon Skeet</a></h3> <p>Because he only had one day to spare, Jon’s 4 talks were compressed into a single day so I found myself following him around for the rest of the day. As the owner of several OSS projects and contributor to many more, I was interested to find out what Jon had learned whilst building <a href="http://nodatime.org/">Noda Time</a>. A couple of things in particular stood out. Jon has a contributor named Malcolm, who does a lot of the non-coding work around the project and his input is invaluable. I think this highlights the importance of never undervaluing contributors who don’t write code. The other was his really interesting approach to CI. Whilst he has a public CI server, he also has a tiny little box running at home on top of a cupboard, completely dedicated to regular benchmark runs to prove that things will run consistently well on low spec hardware.</p> <p>The challenges Jon outlined in the date and time domain are mind bending. Later, during the NDC welcome dinner, I had a hugely enjoyable conversation with Jon about more of the intricacies. If you ever get the chance to chat to Jon, do so. His passion and enthusiasm is thoroughly entertaining.</p> <h3 id="dynamic-vs-static-cage-match-with-jon-skeet-and-gary-bernhardt">Dynamic vs Static Cage Match with <a href="https://twitter.com/jonskeet">Jon Skeet</a> and <a href="https://twitter.com/garybernhardt">Gary Bernhardt</a></h3> <p>What a great way to finish the day. This was fun from start to finish. Arguments from both sides were well balanced and informative and both Jon and Gary flexed their coding muscles impressively. An on the fly C# implementation of Ruby’s <code class="highlighter-rouge">result.Should = 2</code> from Jon really stood out.</p> <p>I was also glad to help out on the static side when Jon managed to crash scriptcs whilst trying to list the methods on a type in an attempt to emulate Ruby’s <code class="highlighter-rouge">methods</code> method. I spotted that the problem was in the JSV serialization going circular on methods and types when trying to render the REPL output and pointed this out to Jon so he could workaround it by picking off just the names of the methods using a LINQ <code class="highlighter-rouge">Select()</code>. This smells like a bug in ServiceStack.Text.</p> <p><strong>Tomorrow:</strong> <a href="https://twitter.com/scottgu">@scottgu</a>, <a href="http://fakeiteasy.github.io/">FakeItEasy</a> on stage and more…</p> <h2 id="names-to-faces-checklist">Names to faces checklist</h2> <p><a href="https://twitter.com/jchannon">@jchannon</a> <a href="https://twitter.com/Cranialstrain">@Cranialstrain</a> <a href="https://twitter.com/gblock">@gblock</a> <a href="https://twitter.com/jonskeet">@jonskeet</a> <a href="https://twitter.com/dhelper">@dhelper</a> <a href="https://twitter.com/stack72">@stack72</a> <a href="https://twitter.com/paulcbetts">@paulcbetts</a> <a href="https://twitter.com/davidfowl">@davidfowl</a> <a href="https://twitter.com/DamianEdwards">@DamianEdwards</a> <a href="https://www.google.co.uk/search?q=mads+torgensen">Mads Torgensen</a></p> NDC Diary - Pre-Conference Workshop Day 2 2013-12-03T00:00:00+00:00 http://blog.adamralph.com/2013/12/03/ndc-diary-pre-conference-workshop-2// <p>Yesterday I attended the <a href="http://ndc-london.com/">NDC London</a> <a href="http://www.ndc-london.com/pre-conference-workshop/continuous-delivery-workshop/1043">‘Continuous Delivery’</a> workshop with <a href="https://twitter.com/jezhumble">Jez Humble</a>. Continuous delivery is a stated goal for my team so this workshop was an obvious choice for me.</p> <p>To kick off, Jez asked us to write what we wanted to get from the day on post-it notes. My three notes were:</p> <ol> <li>How do I convince people that CD is a good idea and worth paying for?</li> <li>How do I introduce CD in an organisation that opposes it?</li> <li>How do I make the transition to CD with a project with a huge cycle time?</li> </ol> <p>The first two questions are particularly relevant to my current job since these are real challenges we face. The last question was also applicable, even though our current cycle time isn’t huge. We want to shrink our 2 week cycle time down to days or even hours and I’m quite sure I’ll face projects with bigger cycle times in the future.</p> <!--excerpt--> <h2 id="the-answers">The Answers</h2> <p>After we all posted our questions on the whiteboard, Jez walked through them and gave a brief initial answer, indicating what he would expand on during they day. The summary answers to my questions were:</p> <ol> <li>Use evidence. There are several case studies and books available which make the benefits very clear. The case of HP’s firmware team is a perfect example and Jez went into great detail on this later in the day.</li> <li>Ultimately, by subversion. Jez quoted “Don’t fight stupid, make more awesome”. What this means is that if you do awesome things, ideally in collaboration with others in the organisation, other people will also want to do those awesome things. Bend the rules and ask for forgiveness, not permission. If you always ask for permission you will likely never get it. I can’t agree more with this answer. The team I work in have made huge strides in our practices in the last 2-3 years and it was almost <em>all</em> achieved by subversion. We didn’t break major rules, just minor ones bit by bit, getting the organisation to buy in gradually. We often talk with other teams who ask things like “How did you manage to get that signed off? We’re not allowed to do that!”. Correction: we <em>weren’t</em> allowed to do it until we <em>tried</em> doing it.</li> <li>Slowly. This can take years. In my experience, this is absolutely true. Whilst we are not yet at the stage of continuous delivery, it has taken us at least 3 years to move from releasing a handful of times in a year to releasing every two weeks. The battle continues…</li> </ol> <p>Throughout the rest of the talk, Jez went into detail on many subjects and demonstrated how CD touches upon <em>every</em> aspect of software delivery. All the way from the golf course (big +1 for this - we talk about the effect of golf on software all the time in my team) all the way through planning, design, architecture, coding, refactoring, testing, deployment, release and more. In fact there is so much to cover that my criticism of the workshop is that it probably should have been expanded to two days. Jez clearly struggled to fit all of the content into one day and some parts seemed a bit rushed. This could also allow the introduction of a little more interactivity although with a subject like CD I’m not sure what form this interactivity might take. In a one day format I think the content needs some cutting and enough to also allow for some more interactivity.</p> <p>I also recommend Jez’s <a href="http://www.amazon.co.uk/Continuous-Delivery-Deployment-Automation-Addison-Wesley/dp/0321601912">Continuous Delivery book</a>, which I am currently about half way through. The workshop was the perfect complement to the book to go into some more detail and it’s great to have the author standing in front of you to bounce questions off!</p> <p><strong>Today:</strong> Keynote with <a href="https://twitter.com/tastapod">Dan North</a> and more…</p> <h2 id="names-to-faces-checklist">Names to faces checklist</h2> <p><a href="https://twitter.com/jezhumble">@jezhumble</a> <a href="https://twitter.com/randompunter">@randompunter</a> <a href="https://twitter.com/jrusbatch">@jrusbatch</a> <a href="https://twitter.com/roysvork">@roysvork</a> <a href="https://twitter.com/stevensanderson">@stevensanderson</a> <a href="https://twitter.com/serialseb">@serialseb</a> <a href="https://twitter.com/darrel_miller">@darrel_miller</a> <a href="https://twitter.com/shiftkey">@shiftkey</a> <a href="https://twitter.com/JakeGinnivan">@JakeGinnivan</a> <a href="https://twitter.com/Ben_Hall">@Ben_Hall</a> <a href="https://twitter.com/chrissie1">@chrissie1</a></p> NDC Diary - Pre-Conference Workshop Day 1 2013-12-02T00:00:00+00:00 http://blog.adamralph.com/2013/12/02/ndc-diary-pre-conference-workshop-1// <p>The <a href="http://ndc-london.com/">NDC London</a> pre-conference workshops kicked off today. After being recognised as <a href="https://twitter.com/NDC_Conferences/status/407540261774831617">NDC’s first ever London delegate</a>, I headed over to <a href="http://ndc-london.com/pre-conference-workshop/javascript-getting-your-feet-wet-venkat-subramaniam-2-dec/1281">‘JavaScript: Getting your feet wet’</a> with <a href="https://twitter.com/venkat_s">Venkat Subramaniam</a>.</p> <p>Although I’ve necessarily tinkered with JavaScript over the years, my focus has been on .NET and C# for a while and I’ve let my JS fundamentals get rusty, to put it mildly. Venkat’s workshop was exactly what I needed to give my JS knowledge a badly needed refresher and teach me a few things I’d never known. We covered everything from syntax basics and quirks through to functions, functional composition, classes and prototypal inheritance. The assumed level of knowledge and pace of learning was just right.</p> <!--excerpt--> <h2 id="the-red-pill">The Red Pill</h2> <p>After a long JS drought, I recently had my first adventure using <a href="http://angularjs.org/">AngularJS</a>. The application was rather simple and I found little need to write any bare metal JS. I was quite amazed how much AngularJS did for me and I knew it was protecting me from a lot of complexity but little did I know just how much.</p> <p>Today the curtain was lifted. At times it certainly felt like Venkat was guiding us down the rabbit hole and after one day I’m sure I still have no idea how deep that rabbit hole goes. JS is in some ways incredibly clever, powerful and elegant, but in other ways it’s horribly ugly, dangerous and mind bending.</p> <p>The first parts of the workshop were largely re-treading old ground for me with few surprises along the way (except a few minor shocks regarding scoping!) but the latter content surrounding classes and prototypes was mostly news to me. I had no idea JS held so much power (and danger!) in it’s prototypal inheritance system. Coming from C# I was quite astonished to discover what can be done at runtime in JS and my high regard for testing was solidly re-enforced!</p> <p>I’m glad I attended the workshop and I now feel far more confident and enthusiastic about using JS in anger. I feel myself itching to implement an application using <a href="http://nodejs.org/">NodeJS</a>, which we used for the workshop exercises.</p> <p><strong>Tommorrow:</strong> <a href="http://www.ndc-london.com/pre-conference-workshop/continuous-delivery-workshop/1043">‘Continuous Delivery’</a> with <a href="https://twitter.com/jezhumble">Jez Humble</a>…</p> <h2 id="names-to-faces-checklist">Names to faces checklist</h2> <p><a href="https://twitter.com/mat_mcloughlin">@mat_mcloughlin</a> <a href="https://twitter.com/venkat_s">@venkat_s</a></p> Announcing xBehave.net 1.0 2013-10-09T00:00:00+00:00 http://blog.adamralph.com/2013/10/09/announcing-xbehave-net-1-0// <p><img src="https://raw.github.com/xbehave/xbehave.net/master/assets/xbehave_128x128.png" alt="" /></p> <p>It gives me great pleasure to announce the release of <a href="http://xbehave.github.io/">xBehave.net</a> <a href="https://www.nuget.org/packages/Xbehave/1.0.0">1.0</a>.</p> <p>This is the first stable release of xBehave.net after more than 18 months of (sporadic) initial development. Extensive documentation is also now available on the xBehave.net <a href="https://github.com/xbehave/xbehave.net/wiki">wiki</a>.</p> <!--excerpt--> <h2 id="thanks">Thanks</h2> <p>By far the most important aspect of xBehave.net is <strong>you</strong>, the community. Thank you for all the contribution, usage, suggestion, bug discovery, criticism and discussion. It has made xBehave.net what it is today. Please continue so xBehave.net can continue to evolve as we do.</p> <p>In particular, I’d like to thank the following developers for their contributions to xBehave.net 1.0:</p> <ul> <li><a href="https://github.com/glennblock">Glenn Block</a></li> <li><a href="https://github.com/jamesfoster">James Foster</a></li> <li><a href="https://github.com/robi-y">robi-y</a></li> <li><a href="https://github.com/manojlds">Manoj</a></li> </ul> <h2 id="history">History</h2> <p>The roots of xBehave.net go back to August 2008 in a spike by <a href="http://haacked.com/">Phil Haack</a> and <a href="http://bradwilson.typepad.com/">Brad Wilson</a> named <a href="http://haacked.com/archive/2008/08/23/introducing-subspec.aspx">SubSpec</a>, aimed at showcasing the extensibility of <a href="https://xunit.codeplex.com/">xUnit.net</a>.</p> <p>The idea was rekindled in August 2010 by <a href="http://jorudolph.wordpress.com/">Johannes Rudolph</a> when he created the <a href="https://bitbucket.org/johannesrudolph/subspec">SubSpec</a> project, later published on <a href="https://www.nuget.org/packages/SubSpec/">NuGet</a>.</p> <p>In January 2012, <a href="http://cameronfletcher.com/">Cameron Fletcher</a> introduced the original SubSpec spike to a project we were working on. Although I’d previously read Phil Haack’s post, I’d never tried the code until then. I immediately liked it and, having already used <a href="http://cukes.info/">Cucumber</a> with Ruby, I soon stumbled upon the idea of introducing a Given, When, Then syntax to the library.</p> <p>Soon afterwards I discovered and forked Johannes Rudolph’s SubSpec project and created <a href="https://bitbucket.org/adamralph/subspecgwt">SubSpecGWT</a> which I pushed to <a href="https://www.nuget.org/packages/SubSpecGWT">NuGet</a> in early February 2012.</p> <p>After a month of development and a few more releases, I re-branded the project as xBehave.net and pushed the <a href="https://www.nuget.org/packages/Xbehave/0.7.0">first release</a> to NuGet at the end of February 2012.</p> <p>Over the next 18 months the library became richer and more polished and gained a few contributors in the march up to 1.0.</p> <p>After a little <a href="https://twitter.com/gblock/status/338899543821262849">tweeting</a> in May 2013 <a href="https://twitter.com/gblock">Glenn Block</a> decided to use xBehave.net for acceptance test examples in his forthcoming book <a href="http://shop.oreilly.com/product/0636920026617.do">‘Designing Evolvable Web APIs with ASP.NET’</a>.</p> <h2 id="future">Future</h2> <p>The next planned version is 2.0 which will see a transition from xUnit.net 1.9 to xUnit.net 2.0 (currently in alpha).</p> <p>1.0 has entered maintenance mode with bug fix releases only. All new features will be scheduled for 2.0 or later.</p> <p>Internally, the changes in 2.0 will be significant. xUnit.net 2.0 is more or less a complete re-write, which means xBehave.net 2.0 is likely to be the same.</p> <p>If you’d like to help out, just head over to the <a href="https://github.com/xbehave/xbehave.net/">GitHub</a> repository where you can get familiar with the code, send pull requests or raise issues for new features, bugs, questions, discussions etc.</p> <p>You can also ping me on <a href="https://twitter.com/adamralph">Twitter</a> or in the xBehave.net <a href="https://jabbr.net/#/rooms/xbehavenet">chat room</a> on JabbR.</p> <p><strong>Thanks again!</strong></p> Extract Pages from a PDF Using Scriptcs 2013-07-06T00:00:00+00:00 http://blog.adamralph.com/2013/07/06/extract-pages-from-a-pdf-with-scriptcs// <p>Today I needed to extract some pages from a large PDF file. One way of doing this would be to pay for a commercial PDF app (free versions don’t have this feature).</p> <p>I discovered a much nicer way.</p> <!--excerpt--> <ol> <li><code class="highlighter-rouge">scriptcs -install iTextSharp</code></li> <li>Save <a href="http://www.jamesewelch.com/2008/11/14/how-to-extract-pages-from-a-pdf-document/">this method</a> in <code class="highlighter-rouge">ExtractPages.csx</code> (shown at the bottom of this post)</li> <li><code class="highlighter-rouge">scriptcs</code></li> <li><code class="highlighter-rouge">#load "ExtractPages.csx"</code></li> <li><code class="highlighter-rouge">ExtractPages(@"C:\big.pdf", @"C:\small.pdf", 1, 3);</code> (pages 1-3)</li> </ol> <p>Job done! <a href="http://scriptcs.net/">scriptcs</a> FTW.</p> <p>Thanks to <a href="https://twitter.com/dchristiansen" title="@dchristiansen">David Christiansen</a> for recommending <a href="https://nuget.org/packages/iTextSharp/">iTextSharp</a> over at <a href="https://jabbr.net/#/rooms/general-chat">JabbR</a>.</p> <div class="highlighter-rouge"><pre class="highlight"><code>// ExtractPages.csx // from http://www.jamesewelch.com/2008/11/14/how-to-extract-pages-from-a-pdf-document/ using iTextSharp.text; using iTextSharp.text.pdf; public static void ExtractPages(string inputFile, string outputFile, int start, int end) { // get input document PdfReader inputPdf = new PdfReader(inputFile); // retrieve the total number of pages int pageCount = inputPdf.NumberOfPages; if (end &lt; start || end &gt; pageCount) { end = pageCount; } // load the input document Document inputDoc = new Document(inputPdf.GetPageSizeWithRotation(1)); // create the filestream using (FileStream fs = new FileStream(outputFile, FileMode.Create)) { // create the output writer PdfWriter outputWriter = PdfWriter.GetInstance(inputDoc, fs); inputDoc.Open(); PdfContentByte cb1 = outputWriter.DirectContent; // copy pages from input to output document for (int i = start; i &lt;= end; i++) { inputDoc.SetPageSize(inputPdf.GetPageSizeWithRotation(i)); inputDoc.NewPage(); PdfImportedPage page = outputWriter.GetImportedPage(inputPdf, i); int rotation = inputPdf.GetPageRotation(i); if (rotation == 90 || rotation == 270) { cb1.AddTemplate(page, 0, -1f, 1f, 0, 0, inputPdf.GetPageSizeWithRotation(i).Height); } else { cb1.AddTemplate(page, 1f, 0, 0, 1f, 0, 0); } } inputDoc.Close(); } } </code></pre> </div> OSS It Already! 2013-05-18T00:00:00+00:00 http://blog.adamralph.com/2013/05/18/oss-it-already// <p>I write bad code.</p> <p>That’s an unusual way to start a blog post, but it’s true.</p> <p>I also write some very good code. Or at least I hope I do. My OSS (open source software) collaborators seem to think I do at least some of the time.</p> <p>The point is, I’m not ashamed to admit it that I write bad code. Every programmer writes bad code sometimes and it doesn’t matter whether they are a complete beginner or an old hand. Any programmer who claims they <em>never</em> write bad code is either deluded or a liar.</p> <p>The attitude of programmers to bad code becomes particularly relevant with regard to OSS.</p> <!--excerpt--> <p><img src="http://adamralph.com/img/what-if-i-oss-it.jpg" alt="WHAT IF OSS IT... AND NO-ONE LIKES IT" /></p> <p>When it comes to making code open source, many people seem to be afraid to do it until they feel their code is at a level where it will be judged by others to be ‘good’. There seems to be a fear of open sourcing initial efforts for reasons of personal pride, fear of ridicule, etc.</p> <p>The reality is this. If you push some code to GitHub, <em>no-one is going to look at it</em>. At least not until you publicise it. GitHub is full of thousands of projects with new ones appearing all the time. No-one is sitting with their trigger finger poised waiting for ‘CoderXyz92’ to push some code so that they can immediately blast all over Twitter how bad the code is.</p> <p><img src="http://adamralph.com/img/checks-new-oss-project.jpg" alt="CHECKS NEW OSS PROJECT... ADDS SOME TESTS" /></p> <p>OSS is all about a community making code better. If you’re trying to get something off the ground there’s no better way to help that happen than to OSS it and get others involved. There are various ways to do that, e.g. word of mouth, user groups and chat rooms (e.g. <a href="https://jabbr.net/#/rooms/general-chat" title="JabbR general-chat">JabbR</a>). If it’s an interesting project, someone will head over and take a look.</p> <p><img src="http://adamralph.com/img/i-dont-often-retreive-state.jpg" alt="I DON'T OFTEN RETRIEVE STATE... BUT WHEN I DO I ABSTRACT OUT THAT DEPENDENCY" /></p> <p>Yes, people may criticise your work. But if it’s positive criticism, what better way to learn? You’ve just opened up your work to a huge community full of enormously talented people, rather than just your friend who you might persuade to pop round your house one night to review your code.</p> <p><img src="http://adamralph.com/img/i-looked-at-your-code.jpg" alt="I LOOKED AT YOUR CODE... IT'S CRAP! HAHAHA LOL" /></p> <p>Of course, trolls exist and you may even encounter one who flames your work as a load of rubbish without any offering any help. I believe that such cases are rare, but if they do happen a degree of humility is necessary. Maintain your dignity, don’t feed the troll and carry on. In the OSS community, the number of good people who want to help far outweighs the number of nasty well-poisoners.</p> <p><img src="http://adamralph.com/img/starts-new-project.jpg" alt="STARTS NEW PROJECT... PUSHES INITIAL COMMIT TO GITHUB" /></p> <p>Which brings me back to my original point. Perhaps your code is bad. In fact, your initial commit of anything probably <em>is</em> quite bad. But that’s natural. It’s a first commit, it’s a new project which is under development and finding its feet. All projects are in a continual state of improvement and even the most popular OSS projects have both good and bad code which needs improvement.</p> <p><strong>The biggest risk is that you will <em>never</em> feel like your project is ‘ready’ to go OSS and it never does.</strong> I hate to think of all the code out there that never went OSS because its author never thought it was ‘good’ enough.</p> <p>So, starting something new or have something already in progress? Join your friends and <strong>OSS it already</strong>! :-D</p> Shadow Stand-ups 2013-04-28T00:00:00+00:00 http://blog.adamralph.com/2013/04/28/the-shadow-standup// <p>If you’re doing agile, you’re probably doing daily stand-ups. A good stand-up is useful. A bad stand-up is a waste of time.</p> <p>My current team needed some time to make our stand-ups good and there was one symptom in particular which took us a while to recognise.</p> <!--excerpt--> <p>Things happened like this:</p> <ol> <li> <p>We stood up (developers, line manager, product owner and scrum master), walked the board, dutifully gave updates and closed the meeting.</p> </li> <li> <p>The developers walked back to their desks and, still standing, talked about technical issues with stories, who was going to pair with who, which stories we would tackle next, etc.</p> </li> </ol> <p>Then the penny dropped - isn’t that what the stand-up is for? Why were we having a <em>shadow</em> stand-up after the official stand-up?</p> <p>The reason was that the meaning and purpose of the stand-up was distorted. We were treating the stand-up as a status update to those people with an interest in the stories but not actually working on them personally (line manager, product owner, scrum master). The developers were afraid to talk about things they really needed to talk about because it didn’t feel suitable for the audience.</p> <p>This kind of stand-up is a waste of time for the team. Jason Yip delves into what makes a good or bad stand-up in his great article <a href="http://www.martinfowler.com/articles/itsNotJustStandingUp.html" title="It's Not Just Standing Up: Patterns for Daily Standup Meetings">“It’s Not Just Standing Up”</a>. As Jason points out, there are far more efficient ways of communicating overall project status to stakeholders, etc. than that of digging into day to day operations.</p> <p>The stand-up is for people working on stories to communicate with each other and synchronise their efforts. Although this should be a continual process and not just once-a-day thing, the stand-up is still a good reminder and habit builder.</p> <p>It’s important in a stand-up to talk to your <em>peers</em> and relay information which <em>you</em> would find useful in their shoes. Make sure that everything which you need to say to your fellow developers at the time is said. Don’t leave it until after the stand-up. These days, we’ve largely manage to eliminate the shadow stand-up by concentrating on these things, but we still slip into it occasionally.</p> <p>If you ever find yourself in a shadow stand-up, stop the conversation and share your revelation. It’s all too easy to do it without realising. It could also be that your shadow stand-up doesn’t happen as a discrete activity but is spread into the rest of the day as you attempt to get the information which you really needed to have in the stand-up. Or perhaps you don’t get the information at all!</p> <p>Once you’ve recognised the problem in whatever manifestation it takes, you can take action to fix your official stand up to make it as useful as it should be.</p> Alternate Code for dotnetConf Test Driving .NET 2013-04-27T00:00:00+00:00 http://blog.adamralph.com/2013/04/27/alternate-code-for-dotnetconf-test-driving-net// <p><a href="http://www.dotnetdevdude.com/">Keith Burnell</a> gave a great talk today titled <a href="http://www.youtube.com/watch?v=_m41mTIPLIE&amp;feature=c4-feed-u">Test Driving .NET</a> at <a href="http://live.dotnetconf.net/">dotnetConf</a>.</p> <p>For anyone who might be interested in using alternate frameworks for TDD in .NET, I thought I’d reproduce the code shown during his talk using a couple of my own OSS projects, <a href="https://github.com/FakeItEasy/FakeItEasy">FakeItEasy</a> and <a href="https://github.com/xbehave/xbehave.net">xBehave.net</a>. I’ve taken the version of the code before the IoC was introduced in the last part of the talk because this is not affected by the changes which I’d like to demonstrate.</p> <!--excerpt--> <p>The original code from Keith’s talk (using <a href="http://www.nunit.org/">NUnit</a>, <a href="http://hibernatingrhinos.com/oss/rhino-mocks">RhinoMocks</a> and <a href="https://fluentassertions.codeplex.com/">FluentAssertions</a>) looked something like this (using <code class="highlighter-rouge">var</code> where possible):</p> <div class="highlighter-rouge"><pre class="highlight"><code>[Test] public void Add_ShouldReturn_12_When_Passed_8_And_4() { //Arrange const decimal input1 = 8; const decimal input2 = 4; var mockRepository = new MockRepository(); var validationServiceMock = mockRepository .StrictMock&lt;IValidationService&gt;(); validationServiceMock .Expect(x =&gt; x.ValidateForAdd(input1, input2)) .Return(True).Repeat.Once; var classUnderTest = new CalculatorService(validationServiceMock); //Act var result = classUnderTest.Add(input1, input2); //Assert mockRepository.VerifyAll(); result.Should().Be(12); } </code></pre> </div> <p>Here is the test re-written using <a href="https://github.com/FakeItEasy/FakeItEasy">FakeItEasy</a> instead of RhinoMocks:</p> <div class="highlighter-rouge"><pre class="highlight"><code>[Test] public void Add_ShouldReturn_12_When_Passed_8_And_4() { //Arrange const decimal input1 = 8; const decimal input2 = 4; var validationService = A.Fake&lt;IValidationService&gt;(); A.CallTo(() =&gt; validationService .ValidateForAdd(input1, input2)) .Returns(true); var classUnderTest = new CalculatorService(validationService); //Act var result = classUnderTest.Add(input1, input2); //Assert A.CallTo(() =&gt; validationService .ValidateForAdd(input1, input2)) .MustHaveHappened(Repeated.Exactly.Once); result.Should().Be(12); } </code></pre> </div> <p>FakeItEasy requires less ceremony than RhinoMocks (no <code class="highlighter-rouge">MockRepository</code>), makes no distinction between stubs/mocks and has a fluent, easy to read DSL (a call to X returns Y).</p> <p>And here is the test (now a scenario) re-written using both FakeItEasy and <a href="https://github.com/xbehave/xbehave.net">xBehave.net</a>:</p> <div class="highlighter-rouge"><pre class="highlight"><code>[Scenario] public void Addition( decimal input1, decimal input2, IValidationService validationService, CalculatorService classUnderTest, decimal result) { "Given an input of 8" .Given(() =&gt; input1 = 8); "And an input of 4" .And(() =&gt; input2 = 4); "And a validation service" .And(() =&gt; { validationService = A.Fake&lt;IValidationService&gt;(); A.CallTo(() =&gt; validationService .ValidateForAdd(input1, input2)) .Returns(true); }); "And a calculation service" .And(() =&gt; classUnderTest = new CalculatorService(validationService); "When I add the inputs" .When(() =&gt; result = classUnderTest.Add(input1, input2); "Then the input must have been validated" .Then(() =&gt; A.CallTo(() =&gt; validationService.ValidateForAdd(input1, input2)) .MustHaveHappened(Repeated.Exactly.Once)); "And the result should be 12" .And(() =&gt; result.Should().Be(12)); } </code></pre> </div> <p>As ever, the benefits of using xBehave.net are primarily readability (read the English to understand what the test is doing and only dive into the implementation if you need to) and a test per step (if one step fails the test output tells you exactly which one failed rather than only which test method).</p> <p>It’s also easy to add further examples when using xBehave.net with the <code class="highlighter-rouge">Example</code> attribute:</p> <div class="highlighter-rouge"><pre class="highlight"><code>[Scenario] [Example(8, 4, 12)] [Example(7, 9, 16)] [Example(100, 200, 300)] public void Addition( decimal input1, decimal input2, decimal expectedResult, IValidationService validationService, CalculatorService classUnderTest, decimal result) { "Given an input of {0}" .Given(() =&gt; { }); "And an input of {1}" .And(() =&gt; { }); "And a validation service" .And(() =&gt; { validationService = A.Fake&lt;IValidationService&gt;(); A.CallTo(() =&gt; validationService .ValidateForAdd(input1, input2)) .Returns(true); }); "And a calculation service" .And(() =&gt; classUnderTest = new CalculatorService(validationService); "When I add the inputs" .When(() =&gt; result = classUnderTest.Add(input1, input2); "Then the input must have been validated" .Then(() =&gt; A.CallTo(() =&gt; validationService.ValidateForAdd(input1, input2)) .MustHaveHappened()); "And the result should be {2}" .And(() =&gt; result.Should().Be(expectedResult)); } </code></pre> </div> The All Solution 2013-04-22T00:00:00+00:00 http://blog.adamralph.com/2013/04/22/the-all-solution// <p>Once upon a time my team had all their code all nice and neat and all in one place. It was all conveniently located in one SVN repo in a single solution. This solution contained many apps which were worked on by some people who worked closely together and others only loosely connected in their day to day activities (a shared line manager but little more). I present to you the ‘All’ solution.</p> <!--excerpt--> <p><img src="https://raw.github.com/adamralph/adamralph.github.com/master/img/all-solution.png" alt="All.sln" /></p> <p>What could be better? Less overhead since you don’t have to ‘maintain’ lots of SVN repos. Easier to locate code since it’s all in one place. Lots of nice reuse between applications. We know how to organise our code!</p> <p>Sadly, nothing could be further from the truth.</p> <p>The solution shown is for a fictional team named ‘HGC’ who develop fictional apps ‘Andromeda’, ‘Gemini’, ‘Jupiter’ and ‘Saturn’. The rest of the projects are so called ‘common’ libraries which are to be reused between applications. This is very similar to how my team had their code organised when I joined the team. In fact, our All solution was <em>way</em> bigger than this. Some of the problems with this approach are:</p> <ul> <li> <p><strong>Technological stagnation.</strong> The apps get so locked together and locked into the same frameworks and approaches that it becomes nearly impossible to try new things and innovate.</p> </li> <li> <p><strong>Lack of defined and recognisable bounded contexts.</strong> Models bleed into and corrupt each other with no discernible integration patterns.</p> </li> <li> <p><strong>Spaghetti like references.</strong> Saturn shouldn’t <em>really</em> reference Andromeda, but it does! Need something from Jupiter? Reference it!</p> </li> <li> <p><strong>Little refactoring.</strong> Developers are worried about changing <em>anything</em> because it’s so difficult to tell what will break. (Good test coverage alleviates this but this was also severely lacking in our case!)</p> </li> <li> <p><strong>Huge build times.</strong> Our All solution took well over an hour to build!</p> </li> <li> <p><strong>Lack of modularisation within apps (look at Hgc.Jupiter).</strong> Developers are afraid to break out apps into defined layers/modules because then there would be ‘too many projects’.</p> </li> <li> <p><strong>Very difficult for each application to evolve independently.</strong> We can’t change the Hgc.Mars0 ‘reusable’ library to do something differently for Hgc.Saturn because it would break Hgc.Gemini!</p> </li> <li> <p><strong>Package updates can be nearly impossible.</strong> You need a new version of package X for Saturn but it might break Jupiter and then you have to run all that manual regression testing again. Argh! You can go with different versions of packages for different apps but then it takes a brave soul to venture into the packages folder and attempt to make head or tail of it!</p> </li> </ul> <p>As we learnt more about DDD and also gained more exposure to OSS projects, we slowly realised that this approach made no sense at all. It was more driven by the organisation of personnel rather than the meaning of the code and the applications. Recognition of bounded contexts and integration patterns where necessary showed us that we were mixing separate concerns all into the same pot for no good reason. Imagine mixing unrelated OSS projects together in a single GitHub repo!</p> <p>These days, each bounded context lives in it’s own repo containing a single solution. Any app which needs to talk to another uses a well defined integration pattern, e.g. Web API or message bus. Build times are down to minutes, if not seconds. Each app is nicely modularised, e.g. Gui, Console, Website, Model, Persistence, Infrastructure. We’re continuously innovating by trying new ways of doing things within the bounded context of a single application. Each application can evolve independently at it’s own pace. Of course there are still bits of mess here and there but it’s a different world now we’ve escaped the clutches of the All solution and the All repo.</p> <p>Splitting our code into single bounded context repos and solutions was one of the best things we ever did for our productivity. If the ‘All’ solution and/or ‘All’ repo look familiar to you, consider breaking things up. I guarantee you won’t regret it.</p> Blog Post Excerpts - A New Solution 2013-01-09T00:00:00+00:00 http://blog.adamralph.com/2013/01/09/blog-post-excerpts-a-new-solution// <p>Something weird happened in the last few days with the Liquid template engine used for GitHub pages.</p> <p>Previously (last push on 06 Jan), I was using the following code to generate excerpts of each post on the home page (<a href="https://github.com/adamralph/adamralph.github.com/blob/142bf461c601dd9bd147d981eb7dd40d45575f70/index.html#L8">source</a>).</p> <div class="highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="err">%</span><span class="w"> </span><span class="err">capture</span><span class="w"> </span><span class="err">excerpt</span><span class="w"> </span><span class="err">%</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="err">%</span><span class="w"> </span><span class="err">for</span><span class="w"> </span><span class="err">paragraph</span><span class="w"> </span><span class="err">in</span><span class="w"> </span><span class="err">post.content</span><span class="w"> </span><span class="err">%</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="err">%</span><span class="w"> </span><span class="err">if</span><span class="w"> </span><span class="err">forloop.index0</span><span class="w"> </span><span class="err">&lt;=</span><span class="w"> </span><span class="err">post.excerpt</span><span class="w"> </span><span class="err">%</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="err">{</span><span class="w"> </span><span class="err">paragraph</span><span class="w"> </span><span class="p">}</span><span class="err">}</span><span class="w"> </span><span class="p">{</span><span class="err">%</span><span class="w"> </span><span class="err">endif</span><span class="w"> </span><span class="err">%</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="err">%</span><span class="w"> </span><span class="err">endfor</span><span class="w"> </span><span class="err">%</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="err">%</span><span class="w"> </span><span class="err">endcapture</span><span class="w"> </span><span class="err">%</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="err">{</span><span class="w"> </span><span class="err">excerpt</span><span class="w"> </span><span class="p">}</span><span class="err">}</span><span class="w"> </span></code></pre> </div> <p>This uses a YAML variable <code class="highlighter-rouge">excerpt</code> in each post which states how many paragraphs to include.</p> <p>This morning, after I pushed an unrelated change, this code no longer worked and the entire content of each post was being shown on the homepage. I <a href="https://gist.github.com/4491164">reported the issue to GitHub support</a> but at the time of writing I have not yet received a reply.</p> <p><a name="more"></a></p> <p><em>(Brief interlude for day job.)</em></p> <!--excerpt--> <p>After coming home from work this evening and investigating further, I noticed that <a href="http://henri.kandersson.com/">Henrik Andersson</a> had <a href="https://github.com/alfhenrik/henri.kandersson.com/blob/c129f9f5fbb4b5923c1e9e9523496664178e470d/index.html#L15">changed his code to use truncation instead</a>. This seemed like a viable alternative but I didn’t like the way each post was potentially truncated mid-word or mid-sentence depending on its content.</p> <p>Then it struck me - why not combine the two approaches, so that each post still has a variable stating how long the excerpt should be but instead of being a paragraph count, make it a character count? So I changed the <code class="highlighter-rouge">excerpt</code> variables in each post accordingly and changed the template code to the following (<a href="https://github.com/adamralph/adamralph.github.com/blob/dcb7d5f9da6e9614c95bef314e5dae3986175972/index.html#L13">source</a>).</p> <div class="highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="err">{</span><span class="w"> </span><span class="err">post.content</span><span class="w"> </span><span class="err">|</span><span class="w"> </span><span class="err">truncate:</span><span class="w"> </span><span class="err">post.excerpt,</span><span class="w"> </span><span class="err">''</span><span class="w"> </span><span class="p">}</span><span class="err">}</span><span class="w"> </span></code></pre> </div> <p>Not only much simpler, but immune to the recent changes made to the GitHub pages Liquid engine! (In case you’re wondering, the second <code class="highlighter-rouge">truncate</code> parameter controls the suffix, which defaults to ‘…’.)</p> <p>And normal service resumes…</p> <p><a name="update1"></a> <strong>Update</strong> 12 Jan 2013</p> <p>Yesterday I received a reply from GitHub support with a link to <a href="http://mikeygee.com/blog/truncate.html">an alternate solution</a> which I think is much nicer (the second solution in the post). With this solution, the <code class="highlighter-rouge">excerpt</code> YAML variable is no longer needed. Instead, the following comment is inserted into each post to signify the end of the excerpt.</p> <div class="highlighter-rouge"><pre class="highlight"><code>&lt;!--excerpt--&gt; </code></pre> </div> <p>And the template code is changed to the following (<a href="https://github.com/adamralph/adamralph.github.com/blob/83bbe606241c4199c8b93e70bf970535d6ce6b3b/index.html#L13">source</a>).</p> <div class="highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="err">{</span><span class="w"> </span><span class="err">post.content</span><span class="w"> </span><span class="err">|</span><span class="w"> </span><span class="err">split:'&lt;!--excerpt--&gt;'</span><span class="w"> </span><span class="err">|</span><span class="w"> </span><span class="err">first</span><span class="w"> </span><span class="p">}</span><span class="err">}</span><span class="w"> </span></code></pre> </div> <p>Thanks very much to <a href="https://github.com/dgraham">David Graham from GitHub</a>.</p>