<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <channel>
    <title>Simon Harriyott's Blog</title>
    <link>https://harriyott.com/blog</link>
    <description />
    <language>en-us</language>
    <pubDate>Sat, 20 Apr 2013 23:41:14 GMT</pubDate>
    <lastBuildDate>Thu, 09 Nov 2023 12:15:06 GMT</lastBuildDate>
    <generator>N2 CMS</generator>
    <managingEditor>Simon Harriyott</managingEditor>
    <item>
      <title>Evergreen skills for developers</title>
      <link>https://harriyott.com/2018/evergreen-skills-for-developers</link>
      <description>&lt;p&gt;I've been chatting to &lt;a href="http://secretgeek.net"&gt;Leon&lt;/a&gt; about his upcoming book, &lt;a href="https://evergreenskills.com/"&gt;Evergreen Skills For Software Developers&lt;/a&gt;. These are skills that are useful throughout a career, rather than a particular technology. Being a developer for over 20 years, I have thoughts on how to develop them.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Learn about frameworks' underlying technology. Learn vanilla JavaScript, not just React or Angular. Learn SQL, not just ORM.&lt;/li&gt;
&lt;li&gt;Notice what you're enjoying learning, and what seems like a chore. Have fun with the enjoyable stuff, even if you can't yet see how it's useful.&lt;/li&gt;
&lt;li&gt;Understand the dark side. If you're a front-end developer, learn the back-end too, and versa vice.&lt;/li&gt;
&lt;li&gt;Have a go at some &lt;a href="https://projecteuler.net"&gt;Project Euler&lt;/a&gt; questions&lt;/li&gt;
&lt;li&gt;Don't worry about (or game) your StackOverflow score. That ship has sailed. Don't look like a moron on there though.&lt;/li&gt;
&lt;li&gt;Learn to touch-type. You'll slow down for six months, but you'll then be faster for the rest of your career, and you can type without looking at your fingers.&lt;/li&gt;
&lt;li&gt;If you're trying to learn a new technology, start making something that'll help you in some way; maybe something to do with a non-coding hobby. Then there'll be an incentive to push through the hard bit and learn how to get something to work.&lt;/li&gt;
&lt;li&gt;Notice if you regularly over- or under-estimate how long work will take. Find out why you do, and work on improving. Notice what happens when your estimate is off, e.g. unpaid overtime or missing out on freelance gigs for high pricing.&lt;/li&gt;
&lt;li&gt;Find a mentor. Find someone to mentor.&lt;/li&gt;
&lt;li&gt;The beginning of wisdom is knowing that you know nothing. Don't be proud, arrogant, dogmatic or inflexible.&lt;/li&gt;
&lt;li&gt;Go to meet-ups, community days, evening seminars etc. and talk to new people.&lt;/li&gt;
&lt;li&gt;Keep your social media nice. Don't be having rude, pedantic arguments with people, especially experts.&lt;/li&gt;
&lt;li&gt;Practise being helpful and nice to colleagues. It'll help with promotions, not being at the top of the next redundancies list, getting new jobs at companies where ex-colleagues have ended up. Spend the occasional lunchtime helping a struggling colleague.&lt;/li&gt;
&lt;li&gt;Understand imposter syndrome and the Dunning-Kruger effect.&lt;/li&gt;
&lt;li&gt;Don't gossip. Don't bully anyone. Support people who are being bullied.&lt;/li&gt;
&lt;li&gt;Don't flirt with your colleagues. Definitely don't do anything that could be even slightly misconstrued as sexual harassment. Don't comment on people's bodies or underwear. If you must compliment someone, compliment their work. Don't drink/dope excessively at work or industry socials.&lt;/li&gt;
&lt;li&gt;Understand how diversity and discrimination work in the industry. Notice it in action. Work out how you can be part of the solution.&lt;/li&gt;
&lt;li&gt;Don't be afraid to leave a toxic workplace.&lt;/li&gt;
&lt;li&gt;If you're having the Sunday evening dreads, listen to them carefully, and act accordingly. Fix the source of the dread, or change your working environment or job.&lt;/li&gt;
&lt;li&gt;You might not always want to be a coder. Think about &lt;a href="https://harriyott.com/2006/02/12-career-moves-for-developers-without"&gt;what else you could be&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;n.b. I'm still working at some of these. I may never make it. I also have a &lt;a href="https://harriyott.com/2006/04/whose-responsibility-is-your-career"&gt;couple of other posts&lt;/a&gt; with &lt;a href="https://harriyott.com/2005/01/things-ive-learnt-so-far"&gt;some other ideas&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Thu, 01 Nov 2018 12:23:39 GMT</pubDate>
    </item>
    <item>
      <title>Beeping Angular</title>
      <link>https://harriyott.com/2018/beeping-angular</link>
      <description>&lt;p&gt;Waiting for Angular to build is boring. I'm using &lt;code&gt;ng build --watch&lt;/code&gt; to trigger a build whenever I save a file, but it's hard to know when it's done. I don't like staring at the timestamp waiting for it to change:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://c1.staticflickr.com/1/807/26240442277_cc73b54894_o.png" alt="Screen shot of Angular build --watch" /&gt;&lt;/p&gt;
&lt;p&gt;I compare the time in the window with the actual time in the bottom corner of my screen, but if I'm making small changes, the minute is the same. If I'm distracted, I may look away briefly and miss when the build has finished and find I'm staring at it waiting for it to change, but it just doesn't.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.fantasyobchod.cz/image/cache/boggle-16934-33080-1000x1000.jpg" alt="Boggle" /&gt;&lt;/p&gt;
&lt;p&gt;I'm a big fan of Boggle, with the exception of the timer. When I'm busy writing words or looking at dice, I don't want to be wasting time glancing at the falling sand. Sometimes we all forget to look, and find that we missed the end, and the timer stopped &lt;em&gt;at some unknown point&lt;/em&gt; in the past. I'm sure you all know how infuriating this is. Whenever I play, I set a timer on my phone to beep when the three minutes is up. We can all concentrate, and we get to play with less frustration.&lt;/p&gt;
&lt;p&gt;Being as smart as you are, I'm sure you've deduced by now that I wanted the Angular build process to beep when it had finished. The initial problem was that the process wasn't finishing, because the --watch flag keeps it running. Had it been a single process, I could have put it in a batch file with an &lt;code&gt;echo ^G&lt;/code&gt; afterwards. (Those who didn't spend their teenage years on an Amstrad PC1512 may not know that Ctrl+G is &lt;a href="http://www.columbia.edu/kermit/ascii.html"&gt;ASCII 7&lt;/a&gt;, i.e. BEL (a beep)). You can type &lt;code&gt;echo Ctrl+g&lt;/code&gt; into a command prompt to make a beep, which is more pleasing than it should be.&lt;/p&gt;
&lt;p&gt;However, there's no post-build hook in Angular (that I could find), so I needed something else. Twelve years ago I discovered C#'s &lt;code&gt;FileSystemWatcher&lt;/code&gt; for a project I was working on. It's the only time I've used it, but now was the time to bring it out again. I think of it often, but it sits there in a drawer, waiting for a special occasion, next to my bowtie. A &lt;code&gt;FileSystemWatcher&lt;/code&gt; keeps an eye on a file or directory, and if a file changes, it fires an event. My plan was to get this dude to camp out with his binoculars looking at the Angular output directory and if anything happens, send me an E in morse code.&lt;/p&gt;
&lt;p&gt;So in a new console app, I lovingly sculpted a new watcher:
&lt;script type="text/javascript" src="https://gist.github.com/harriyott/82d2e2ca4daf036966ef99f0ee508909.js"&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p&gt;When it fires, it makes just the right kind of beep.
&lt;script type="text/javascript" src="https://gist.github.com/harriyott/60a7b51d4f47129a2fdf248ffd92779b.js"&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p&gt;I ran it, saved an Angular file, and it beeped when it finished. Several times, in fact, as several files changed. I wanted just the one, so I used a short timer to catch the changes:
&lt;script type="text/javascript" src="https://gist.github.com/harriyott/44f3c334551d4384ff9d8e2e2ce18d33.js"&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p&gt;This timer delays the beep for 0.3 seconds, and doesn't let any other file changes trigger a new beep in the interim. As an aside, when you're playing Boggle, and you see the word TIMER, it's &lt;a href="http://www.timescolonist.com/news/local/victoria-boy-s-new-word-levidrome-on-its-way-to-oxford-dictionary-1.23102948"&gt;Levidrome&lt;/a&gt; is REMIT, so you can write that immediately. Obviously there's TIME too, whose Levidrome is EMIT, so you've got four words from one without thinking about it.&lt;/p&gt;
&lt;p&gt;So now I'm happily coding in Angular in Visual Studio, pressing &lt;code&gt;Ctrl+Shift+S&lt;/code&gt;, clicking on the browser window containing the app, hovering my finger over the &lt;code&gt;F5&lt;/code&gt; key, and then staring out of the window until I hear the beep, when I tap the &lt;code&gt;F5&lt;/code&gt; key immediately. Problem solved; happy Simon.&lt;/p&gt;</description>
      <pubDate>Fri, 30 Mar 2018 11:31:23 GMT</pubDate>
    </item>
    <item>
      <title>DRY routes between C# and Angular</title>
      <link>https://harriyott.com/2018/dry-routes-between-c-and-angular</link>
      <description>&lt;p&gt;I have an Angular 5 app talking to a C# api. Having magic strings for the end-points in both the TypeScript and the C# route attributes seems fragile to me:&lt;/p&gt;
&lt;script type="text/javascript" src="https://gist.github.com/harriyott/4498e713d82f30d6302ca57cd32b7abd.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="https://gist.github.com/harriyott/c07cf856c549d78f1621c719ddc4ccfa.js"&gt;&lt;/script&gt;
&lt;p&gt;I wanted a single source of urls, so the first step was to create a C# class of constants and use them in the controller.&lt;/p&gt;
&lt;script type="text/javascript" src="https://gist.github.com/harriyott/0fbb7e0fa0b9764c9c5046c8aaeabfcc.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="https://gist.github.com/harriyott/28e552f1bb6e380b5dafc71f4eb24aa7.js"&gt;&lt;/script&gt;
&lt;p&gt;Manually typing an equivalent TypeScript class may make the code a fraction less fragile, but it would still be writing the same code twice. Ideally the Angular app would be able to access the C# constants, but there's no direct way. I've been a big fan of code generation over the years, using &lt;a href="http://nimbletext.com/" target="nt"&gt;NimbleText&lt;/a&gt;, &lt;a href="http://www.codesmithtools.com/product/generator" target="cs"&gt;CodeSmith&lt;/a&gt;, Resharper templates and T4. My plan was to generate a TypeScript class from the C# class. I plumped for T4.
&lt;script type="text/javascript" src="https://gist.github.com/harriyott/732666987ea89478a1bddcbcc36ef39e.js"&gt;&lt;/script&gt;
This uses reflection on the class to get a list of the class's constants containing the urls. It loops round them, checking for parameters, e.g. &lt;code&gt;{kittenId}&lt;/code&gt;. If there are any, the template generates a function that accepts the parameters and returns the formatted string. If there are no paramters, then a property is generated instead. The output of this template is included in the Angular app, and is accessible from the rest of the code.
&lt;script type="text/javascript" src="https://gist.github.com/harriyott/47c705bbfaeb1fdb969d93fc3771bcc6.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="https://gist.github.com/harriyott/85bc193881885657cc382cc7f93d82e2.js"&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p&gt;A much less fragile way of linking front- and back-ends through code generation.&lt;/p&gt;</description>
      <pubDate>Wed, 07 Mar 2018 13:00:00 GMT</pubDate>
    </item>
    <item>
      <title>Tip for class and property names in SQL queries</title>
      <link>https://harriyott.com/2017/tip-for-class-and-property-names-in-sql-queries</link>
      <description>&lt;p&gt;A common pattern is using an &lt;abbr title="Object Relational Mapper"&gt;ORM&lt;/abbr&gt; for writes, and &lt;abbr title="Structured Query Language"&gt;SQL&lt;/abbr&gt; (e.g. via Dapper) for reads. A possible cause of bugs is having SQL in magic strings.&lt;/p&gt;
&lt;pre class="brush: csharp"&gt;var query = "SELECT DISTINCT TeamName FROM InternatonalPlayers";&lt;/pre&gt;
&lt;p&gt;There may be a typo in a table or column name, which wouldn't be caught at compile time, or renaming something in the future that causes a database schema change could break the SQL.&lt;/p&gt;
&lt;p&gt;To mitigate these problems, I've started using&amp;nbsp;&lt;span class="brush: csharp"&gt;nameof&lt;/span&gt; in SQL statements:&lt;/p&gt;
&lt;pre class="brush: csharp"&gt;var query = $"SELECT DISTINCT {nameof(InternationalPlayers.TeamName)} FROM {nameof(InternationalPlayers)}";&lt;/pre&gt;
&lt;p&gt;If there's a spelling mistake, the code won't compile. If either the class or property are renamed, the editor will update them automatically, or a compile error will occur.&lt;/p&gt;
&lt;h3&gt;Why you might not want to do this&lt;/h3&gt;
&lt;p&gt;Unit or integrating test will catch typos or renames in magic string SQL, but this will occur later than compile time.&lt;/p&gt;
&lt;p&gt;It makes the SQL longer. In the example above, you could create a teamName variable for the long property name, which will add another line of code (and possibly blank line for clarity).&lt;/p&gt;
&lt;p&gt;Foreign key Id columns may have an _id or Id suffix added by the ORM, which don't appear in the property names. You could add this into the SQL directly after the nameof closing curly brace, but then we've introduced magical stringyness. We could look this up from the ORM configuration, but I'm sure you'll posit that this is going a bit far.&lt;/p&gt;
&lt;p&gt;Anyway, there we are. Take it or leave it. HTH.&lt;/p&gt;</description>
      <pubDate>Fri, 29 Sep 2017 12:00:27 GMT</pubDate>
    </item>
    <item>
      <title>Possessive apostrophes in C# identifiers</title>
      <link>https://harriyott.com/2017/possessive-apostrophes-in-c-identifiers</link>
      <description>&lt;p&gt;Having a strong inclination towards grammar &lt;span style="text-decoration: line-through;"&gt;pedantry&lt;/span&gt; precision is both a blessing and a curse. Just now I was irritated with an ambiguous variable name, &lt;code&gt;playersTeams&lt;/code&gt;. Is it one player&lt;span&gt;ʼ&lt;/span&gt;s teams, or many players&lt;span&gt;ʼ&lt;/span&gt; teams? The apostrophe placement is important in written English to distinguish (ooh, accidental rhyme!) the number of players referred to.&lt;/p&gt;
&lt;p&gt;Pressing the ' key to create an apostrophe in the variable name is obviously disallowed, as it is used in C# to delimit character values:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://c1.staticflickr.com/5/4370/36639835554_837ab6dc6b_z.jpg" alt="Single quote" width="592" height="154" /&gt;&lt;/p&gt;
&lt;p&gt;However, that key isn&lt;span&gt;ʼ&lt;/span&gt;t actually an apostrophe - it&lt;span&gt;ʼ&lt;/span&gt;s just used as one for convenience. There is an &lt;a href="http://graphemica.com/%CA%BC" target="_blank"&gt;actual proper Unicode apostrophe character&lt;/a&gt; though:&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;span style="font-size: 6em;"&gt;ʼ&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This can be used in identifiers, thus:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://farm5.staticflickr.com/4369/36639835404_7b40c3b6e1_z.jpg" alt="Apostrophe in identifier" width="569" height="174" /&gt;&lt;/p&gt;
&lt;p&gt;Now I&lt;span&gt;ʼ&lt;/span&gt;m pretty sure what will happen when this code is reviewed. It does look wrong, and I&lt;span&gt;ʼ&lt;/span&gt;m not sure if I will actually use it. It does, however, help with one of the &lt;a href="http://harriyott.com/notes/two-problems"&gt;hard problems in computer science&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;n.b. You can select and copy &lt;em&gt;the big apostrophe above,&amp;nbsp;&lt;/em&gt;and paste it into your code editor.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="border-radius: 2px; text-indent: 20px; width: auto; padding: 0px 4px 0px 0px; text-align: center; font: bold 11px/20px 'Helvetica Neue',Helvetica,sans-serif; color: #ffffff; background: #bd081c url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMzBweCIgd2lkdGg9IjMwcHgiIHZpZXdCb3g9Ii0xIC0xIDMxIDMxIj48Zz48cGF0aCBkPSJNMjkuNDQ5LDE0LjY2MiBDMjkuNDQ5LDIyLjcyMiAyMi44NjgsMjkuMjU2IDE0Ljc1LDI5LjI1NiBDNi42MzIsMjkuMjU2IDAuMDUxLDIyLjcyMiAwLjA1MSwxNC42NjIgQzAuMDUxLDYuNjAxIDYuNjMyLDAuMDY3IDE0Ljc1LDAuMDY3IEMyMi44NjgsMC4wNjcgMjkuNDQ5LDYuNjAxIDI5LjQ0OSwxNC42NjIiIGZpbGw9IiNmZmYiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxIj48L3BhdGg+PHBhdGggZD0iTTE0LjczMywxLjY4NiBDNy41MTYsMS42ODYgMS42NjUsNy40OTUgMS42NjUsMTQuNjYyIEMxLjY2NSwyMC4xNTkgNS4xMDksMjQuODU0IDkuOTcsMjYuNzQ0IEM5Ljg1NiwyNS43MTggOS43NTMsMjQuMTQzIDEwLjAxNiwyMy4wMjIgQzEwLjI1MywyMi4wMSAxMS41NDgsMTYuNTcyIDExLjU0OCwxNi41NzIgQzExLjU0OCwxNi41NzIgMTEuMTU3LDE1Ljc5NSAxMS4xNTcsMTQuNjQ2IEMxMS4xNTcsMTIuODQyIDEyLjIxMSwxMS40OTUgMTMuNTIyLDExLjQ5NSBDMTQuNjM3LDExLjQ5NSAxNS4xNzUsMTIuMzI2IDE1LjE3NSwxMy4zMjMgQzE1LjE3NSwxNC40MzYgMTQuNDYyLDE2LjEgMTQuMDkzLDE3LjY0MyBDMTMuNzg1LDE4LjkzNSAxNC43NDUsMTkuOTg4IDE2LjAyOCwxOS45ODggQzE4LjM1MSwxOS45ODggMjAuMTM2LDE3LjU1NiAyMC4xMzYsMTQuMDQ2IEMyMC4xMzYsMTAuOTM5IDE3Ljg4OCw4Ljc2NyAxNC42NzgsOC43NjcgQzEwLjk1OSw4Ljc2NyA4Ljc3NywxMS41MzYgOC43NzcsMTQuMzk4IEM4Ljc3NywxNS41MTMgOS4yMSwxNi43MDkgOS43NDksMTcuMzU5IEM5Ljg1NiwxNy40ODggOS44NzIsMTcuNiA5Ljg0LDE3LjczMSBDOS43NDEsMTguMTQxIDkuNTIsMTkuMDIzIDkuNDc3LDE5LjIwMyBDOS40MiwxOS40NCA5LjI4OCwxOS40OTEgOS4wNCwxOS4zNzYgQzcuNDA4LDE4LjYyMiA2LjM4NywxNi4yNTIgNi4zODcsMTQuMzQ5IEM2LjM4NywxMC4yNTYgOS4zODMsNi40OTcgMTUuMDIyLDYuNDk3IEMxOS41NTUsNi40OTcgMjMuMDc4LDkuNzA1IDIzLjA3OCwxMy45OTEgQzIzLjA3OCwxOC40NjMgMjAuMjM5LDIyLjA2MiAxNi4yOTcsMjIuMDYyIEMxNC45NzMsMjIuMDYyIDEzLjcyOCwyMS4zNzkgMTMuMzAyLDIwLjU3MiBDMTMuMzAyLDIwLjU3MiAxMi42NDcsMjMuMDUgMTIuNDg4LDIzLjY1NyBDMTIuMTkzLDI0Ljc4NCAxMS4zOTYsMjYuMTk2IDEwLjg2MywyNy4wNTggQzEyLjA4NiwyNy40MzQgMTMuMzg2LDI3LjYzNyAxNC43MzMsMjcuNjM3IEMyMS45NSwyNy42MzcgMjcuODAxLDIxLjgyOCAyNy44MDEsMTQuNjYyIEMyNy44MDEsNy40OTUgMjEuOTUsMS42ODYgMTQuNzMzLDEuNjg2IiBmaWxsPSIjYmQwODFjIj48L3BhdGg+PC9nPjwvc3ZnPg==') no-repeat scroll 3px 50% / 14px 14px; position: absolute; opacity: 1; z-index: 8675309; display: none; cursor: pointer;"&gt;Save&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="border-radius: 2px; text-indent: 20px; width: auto; padding: 0px 4px 0px 0px; text-align: center; font: bold 11px/20px 'Helvetica Neue',Helvetica,sans-serif; color: #ffffff; background: #bd081c url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMzBweCIgd2lkdGg9IjMwcHgiIHZpZXdCb3g9Ii0xIC0xIDMxIDMxIj48Zz48cGF0aCBkPSJNMjkuNDQ5LDE0LjY2MiBDMjkuNDQ5LDIyLjcyMiAyMi44NjgsMjkuMjU2IDE0Ljc1LDI5LjI1NiBDNi42MzIsMjkuMjU2IDAuMDUxLDIyLjcyMiAwLjA1MSwxNC42NjIgQzAuMDUxLDYuNjAxIDYuNjMyLDAuMDY3IDE0Ljc1LDAuMDY3IEMyMi44NjgsMC4wNjcgMjkuNDQ5LDYuNjAxIDI5LjQ0OSwxNC42NjIiIGZpbGw9IiNmZmYiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxIj48L3BhdGg+PHBhdGggZD0iTTE0LjczMywxLjY4NiBDNy41MTYsMS42ODYgMS42NjUsNy40OTUgMS42NjUsMTQuNjYyIEMxLjY2NSwyMC4xNTkgNS4xMDksMjQuODU0IDkuOTcsMjYuNzQ0IEM5Ljg1NiwyNS43MTggOS43NTMsMjQuMTQzIDEwLjAxNiwyMy4wMjIgQzEwLjI1MywyMi4wMSAxMS41NDgsMTYuNTcyIDExLjU0OCwxNi41NzIgQzExLjU0OCwxNi41NzIgMTEuMTU3LDE1Ljc5NSAxMS4xNTcsMTQuNjQ2IEMxMS4xNTcsMTIuODQyIDEyLjIxMSwxMS40OTUgMTMuNTIyLDExLjQ5NSBDMTQuNjM3LDExLjQ5NSAxNS4xNzUsMTIuMzI2IDE1LjE3NSwxMy4zMjMgQzE1LjE3NSwxNC40MzYgMTQuNDYyLDE2LjEgMTQuMDkzLDE3LjY0MyBDMTMuNzg1LDE4LjkzNSAxNC43NDUsMTkuOTg4IDE2LjAyOCwxOS45ODggQzE4LjM1MSwxOS45ODggMjAuMTM2LDE3LjU1NiAyMC4xMzYsMTQuMDQ2IEMyMC4xMzYsMTAuOTM5IDE3Ljg4OCw4Ljc2NyAxNC42NzgsOC43NjcgQzEwLjk1OSw4Ljc2NyA4Ljc3NywxMS41MzYgOC43NzcsMTQuMzk4IEM4Ljc3NywxNS41MTMgOS4yMSwxNi43MDkgOS43NDksMTcuMzU5IEM5Ljg1NiwxNy40ODggOS44NzIsMTcuNiA5Ljg0LDE3LjczMSBDOS43NDEsMTguMTQxIDkuNTIsMTkuMDIzIDkuNDc3LDE5LjIwMyBDOS40MiwxOS40NCA5LjI4OCwxOS40OTEgOS4wNCwxOS4zNzYgQzcuNDA4LDE4LjYyMiA2LjM4NywxNi4yNTIgNi4zODcsMTQuMzQ5IEM2LjM4NywxMC4yNTYgOS4zODMsNi40OTcgMTUuMDIyLDYuNDk3IEMxOS41NTUsNi40OTcgMjMuMDc4LDkuNzA1IDIzLjA3OCwxMy45OTEgQzIzLjA3OCwxOC40NjMgMjAuMjM5LDIyLjA2MiAxNi4yOTcsMjIuMDYyIEMxNC45NzMsMjIuMDYyIDEzLjcyOCwyMS4zNzkgMTMuMzAyLDIwLjU3MiBDMTMuMzAyLDIwLjU3MiAxMi42NDcsMjMuMDUgMTIuNDg4LDIzLjY1NyBDMTIuMTkzLDI0Ljc4NCAxMS4zOTYsMjYuMTk2IDEwLjg2MywyNy4wNTggQzEyLjA4NiwyNy40MzQgMTMuMzg2LDI3LjYzNyAxNC43MzMsMjcuNjM3IEMyMS45NSwyNy42MzcgMjcuODAxLDIxLjgyOCAyNy44MDEsMTQuNjYyIEMyNy44MDEsNy40OTUgMjEuOTUsMS42ODYgMTQuNzMzLDEuNjg2IiBmaWxsPSIjYmQwODFjIj48L3BhdGg+PC9nPjwvc3ZnPg==') no-repeat scroll 3px 50% / 14px 14px; position: absolute; opacity: 1; z-index: 8675309; display: none; cursor: pointer;"&gt;Save&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="border-radius: 2px; text-indent: 20px; width: auto; padding: 0px 4px 0px 0px; text-align: center; font: bold 11px/20px 'Helvetica Neue',Helvetica,sans-serif; color: #ffffff; background: #bd081c url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMzBweCIgd2lkdGg9IjMwcHgiIHZpZXdCb3g9Ii0xIC0xIDMxIDMxIj48Zz48cGF0aCBkPSJNMjkuNDQ5LDE0LjY2MiBDMjkuNDQ5LDIyLjcyMiAyMi44NjgsMjkuMjU2IDE0Ljc1LDI5LjI1NiBDNi42MzIsMjkuMjU2IDAuMDUxLDIyLjcyMiAwLjA1MSwxNC42NjIgQzAuMDUxLDYuNjAxIDYuNjMyLDAuMDY3IDE0Ljc1LDAuMDY3IEMyMi44NjgsMC4wNjcgMjkuNDQ5LDYuNjAxIDI5LjQ0OSwxNC42NjIiIGZpbGw9IiNmZmYiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxIj48L3BhdGg+PHBhdGggZD0iTTE0LjczMywxLjY4NiBDNy41MTYsMS42ODYgMS42NjUsNy40OTUgMS42NjUsMTQuNjYyIEMxLjY2NSwyMC4xNTkgNS4xMDksMjQuODU0IDkuOTcsMjYuNzQ0IEM5Ljg1NiwyNS43MTggOS43NTMsMjQuMTQzIDEwLjAxNiwyMy4wMjIgQzEwLjI1MywyMi4wMSAxMS41NDgsMTYuNTcyIDExLjU0OCwxNi41NzIgQzExLjU0OCwxNi41NzIgMTEuMTU3LDE1Ljc5NSAxMS4xNTcsMTQuNjQ2IEMxMS4xNTcsMTIuODQyIDEyLjIxMSwxMS40OTUgMTMuNTIyLDExLjQ5NSBDMTQuNjM3LDExLjQ5NSAxNS4xNzUsMTIuMzI2IDE1LjE3NSwxMy4zMjMgQzE1LjE3NSwxNC40MzYgMTQuNDYyLDE2LjEgMTQuMDkzLDE3LjY0MyBDMTMuNzg1LDE4LjkzNSAxNC43NDUsMTkuOTg4IDE2LjAyOCwxOS45ODggQzE4LjM1MSwxOS45ODggMjAuMTM2LDE3LjU1NiAyMC4xMzYsMTQuMDQ2IEMyMC4xMzYsMTAuOTM5IDE3Ljg4OCw4Ljc2NyAxNC42NzgsOC43NjcgQzEwLjk1OSw4Ljc2NyA4Ljc3NywxMS41MzYgOC43NzcsMTQuMzk4IEM4Ljc3NywxNS41MTMgOS4yMSwxNi43MDkgOS43NDksMTcuMzU5IEM5Ljg1NiwxNy40ODggOS44NzIsMTcuNiA5Ljg0LDE3LjczMSBDOS43NDEsMTguMTQxIDkuNTIsMTkuMDIzIDkuNDc3LDE5LjIwMyBDOS40MiwxOS40NCA5LjI4OCwxOS40OTEgOS4wNCwxOS4zNzYgQzcuNDA4LDE4LjYyMiA2LjM4NywxNi4yNTIgNi4zODcsMTQuMzQ5IEM2LjM4NywxMC4yNTYgOS4zODMsNi40OTcgMTUuMDIyLDYuNDk3IEMxOS41NTUsNi40OTcgMjMuMDc4LDkuNzA1IDIzLjA3OCwxMy45OTEgQzIzLjA3OCwxOC40NjMgMjAuMjM5LDIyLjA2MiAxNi4yOTcsMjIuMDYyIEMxNC45NzMsMjIuMDYyIDEzLjcyOCwyMS4zNzkgMTMuMzAyLDIwLjU3MiBDMTMuMzAyLDIwLjU3MiAxMi42NDcsMjMuMDUgMTIuNDg4LDIzLjY1NyBDMTIuMTkzLDI0Ljc4NCAxMS4zOTYsMjYuMTk2IDEwLjg2MywyNy4wNTggQzEyLjA4NiwyNy40MzQgMTMuMzg2LDI3LjYzNyAxNC43MzMsMjcuNjM3IEMyMS45NSwyNy42MzcgMjcuODAxLDIxLjgyOCAyNy44MDEsMTQuNjYyIEMyNy44MDEsNy40OTUgMjEuOTUsMS42ODYgMTQuNzMzLDEuNjg2IiBmaWxsPSIjYmQwODFjIj48L3BhdGg+PC9nPjwvc3ZnPg==') no-repeat scroll 3px 50% / 14px 14px; position: absolute; opacity: 1; z-index: 8675309; display: none; cursor: pointer;"&gt;Save&lt;/span&gt;&lt;/p&gt;</description>
      <pubDate>Wed, 27 Sep 2017 10:42:44 GMT</pubDate>
    </item>
    <item>
      <title>Code Reviews</title>
      <link>https://harriyott.com/2017/code-reviews</link>
      <description>&lt;p&gt;I started my current freelance role 6 years ago. I created an intranet, which I have been expanding and maintaining mostly by myself. Recently, a new senior developer has joined the team, as a permanent member of staff. During the induction process we discussed how we should work together, and decided early on that code reviews would be useful. They would enable me to check that he's using the existing (well, legacy to him) code in the right way, and secondly to ensure the code he produces is of suitable quality.&lt;/p&gt;
&lt;p&gt;The notion of &lt;em&gt;suitable quality&lt;/em&gt; is somewhat subjective, so we talked at length how we could make it more &lt;em&gt;objective&lt;/em&gt;. There are horror stories about code reviews causing tensions between coders, and we wanted to avoid starring in our own. Having objective ways to assess the code under review would help.&lt;/p&gt;
&lt;p&gt;We also agreed that the code was under review, not the coder, so any perceived criticism need not be taken personally.&lt;/p&gt;
&lt;p&gt;The objective criteria we came up with are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The code should compile and run&lt;/li&gt;
&lt;li&gt;The unit tests should all pass&lt;/li&gt;
&lt;li&gt;The code should implement the specification&lt;/li&gt;
&lt;li&gt;The coding standards have been adhered to&lt;/li&gt;
&lt;li&gt;The code is SOLID&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Being objective, this allows us to write code in our own personal style; after all, there are many ways to solve the same problem. So long as the criteria are met, the code is acceptable.&lt;/p&gt;
&lt;p&gt;I decided that my code should be reviewed too. For years I've been working mostly alone with no review process, and it would do the codebase and me some good. This meant a bit of source control reconfiguration; I used to code on the main github repo, but now I have a fork that I develop on, and submit pull requests to the main repo for review. The github pull request and code review process is really good (with the possible exception of a feisty red cross icon against any comment).&lt;/p&gt;
&lt;p&gt;Well, so far it's going well. As we know our code will be reviewed, we run through the checklist before submitting the pull request, so the code is already improved before the review starts. There have been no major infractions: most of the requested changes have been typos, suggested renamings, the odd tabs / spaces inconsistency. There was one small but clear bug; I think a &lt;em&gt;less than&lt;/em&gt; that should have been a &lt;em&gt;greater than&lt;/em&gt; or something like that; a code review is a much cheaper place to find it than on the live site.&lt;/p&gt;
&lt;p&gt;The best moments so far though, have been when noticing small code smells. This has led to a couple of good conversations discussing what the problem might be (they weren't immediately obvious), and suggesting possible refactorings. After a bit of recoding, the resubmitted code was leaner, more SOLID, more readable, and just plainly &lt;em&gt;better&lt;/em&gt;. If it wasn't obvious beforehand, it certainly was afterwards.&lt;/p&gt;
&lt;p&gt;In all, I'm really pleased with how the code reviews have been going. I feel more confident about the codebase, and my coding skills. I've learnt a couple of new things from reading every line of someone else's code too, which is a side-effect I hadn't anticipated.&lt;/p&gt;</description>
      <pubDate>Tue, 03 Jan 2017 22:57:37 GMT</pubDate>
    </item>
    <item>
      <title>Measuring Software Development Progress</title>
      <link>https://harriyott.com/2016/measuring-software-development-progress</link>
      <description>&lt;p&gt;After recently having a brief discussion about &lt;em&gt;progress&lt;/em&gt;, I thought I would try to write a list of all the ways people have tried to measure how a project is going.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lines of code written&lt;/li&gt;
&lt;li&gt;Number of bugs fixed&lt;/li&gt;
&lt;li&gt;Man hours spent&lt;/li&gt;
&lt;li&gt;Number of deadlines hit&lt;/li&gt;
&lt;li&gt;Deadline overrun&lt;/li&gt;
&lt;li&gt;Elapsed time&lt;/li&gt;
&lt;li&gt;Story points completed&lt;/li&gt;
&lt;li&gt;Velocity&lt;/li&gt;
&lt;li&gt;Burn down rate&lt;/li&gt;
&lt;li&gt;Money earnt (or saved) by the users&lt;/li&gt;
&lt;li&gt;Time saved by the users&lt;/li&gt;
&lt;li&gt;Bugs introduced&lt;/li&gt;
&lt;li&gt;Uptime&lt;/li&gt;
&lt;li&gt;User complaints&lt;/li&gt;
&lt;li&gt;Scope creep&lt;/li&gt;
&lt;li&gt;Working software&lt;/li&gt;
&lt;/ol&gt;</description>
      <pubDate>Wed, 14 Sep 2016 15:46:17 GMT</pubDate>
    </item>
    <item>
      <title>I never knew that was there</title>
      <link>https://harriyott.com/2015/02/i-never-knew-that-was-there</link>
      <description>&lt;p&gt;In Visual Studio, I use Ctrl+F12 to go to the implementation of a method. Just now, I accidentally pressed Alt+F12 and what happened next blew my mind. An inner editor window opened under the line of code I was on, with the code for the method I wanted to see visible.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;I have no idea what this feature is called, or whether it is from Visual Studio or Resharper, but I'll be using it a lot, especially with test-driven development.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I just googled it, and it's called &lt;a href="https://msdn.microsoft.com/en-us/library/dn160178.aspx"&gt;Peek Definition&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img title="Inner Editor" src="/upload/Images/InnerEditor.PNG" alt="Inner Editor" /&gt;&lt;/p&gt;</description>
      <pubDate>Wed, 18 Feb 2015 15:17:50 GMT</pubDate>
    </item>
    <item>
      <title>Being more organised</title>
      <link>https://harriyott.com/2014/11/being-more-organised</link>
      <description>&lt;p&gt;I was talking to a self-confessed disorganised person recently. I used to say I was disorganised, but I've put so many systems in place over the years to counter this, I might well now be considered organised. Anyway, here are a few of my systems, for his benefit, and anyone else.&lt;/p&gt;
&lt;h3&gt;Phoning myself&lt;/h3&gt;
&lt;p&gt;If I'm at work, and suddenly remember something I have to do at home, I'll phone my home landline and leave myself a message on the answer machine. When I get home, I'll see there's a message, and play it. "Hi Simon, it's Simon. Don't forget to ..."&lt;/p&gt;
&lt;p&gt;Likewise, when I'm just about to drift off to sleep, I often remember something to do at work, so I'll phone my work voicemail.&lt;/p&gt;
&lt;h3&gt;Doormat&lt;/h3&gt;
&lt;p&gt;If I have to remember to take something with me when I go out later, or in the morning. I leave it on the front doormat. When it's time to go out, I would have to move it to open the door, which reminds me it needs to come too.&lt;/p&gt;
&lt;h3&gt;Homes for things&lt;/h3&gt;
&lt;p&gt;Everything should have a home. Whenever I finish using something, I put it back in it's home. It doesn't matter how random or strange the home is, so long as it lives there. My hammer lives in the bottom of the coat cupboard, for example. It's always there, unless I'm using it, and then it goes back.&lt;/p&gt;
&lt;h3&gt;Keys, phone, wallet&lt;/h3&gt;
&lt;p&gt;The holy trinity of misplaced objects. Again, these need homes. Two homes. One home for when I'm out and about, and the other for when I'm in my home. The first home is in my trousers. Phone and wallet in my front left pocket, keys and coins in my front right. They will always be there, unless I'm using them, and then they go back. They don't go in my jacket pocket, or in a back pocket, or on the table at the pub, or anything else. They stay in their pocket.&lt;/p&gt;
&lt;p&gt;At home, when I go to bed, I put my phone on to charge, next to my bed, as I use it as an alarm clock. When I get dressed, I put it in my trousers. My keys and wallet live in my trousers overnight. If I want to wear different trousers, I transfer everything across before putting them on.&lt;/p&gt;
&lt;p&gt;It sounds like a bit of hassle, and perhaps it takes a while to get used to, but once it becomes habit, it doesn't require any thought, and you'll always know where they are.&lt;/p&gt;
&lt;h3&gt;Camera phone as a notepad&lt;/h3&gt;
&lt;p&gt;I realised a while ago that taking notes isn't always practical, and taking photos of things is a super quick way to remember things. I use it to take photos of books that I might want to buy in the future, notes that have been scribbled on a bit of paper that will probably get lost, timetables and so on.&lt;/p&gt;
&lt;h3&gt;Turning around before walking out&lt;/h3&gt;
&lt;p&gt;When leaving a caf&amp;eacute; or restaurant, I turn round for a last look at the table before walking out, to make sure I've not left anything on or under the table, and not left my laptop charger plugged in. It's now a habit.&lt;/p&gt;
&lt;h3&gt;Pre-packed laptop bag&lt;/h3&gt;
&lt;p&gt;I make sure that I have anything I could need in my laptop backpack, and they live there until used, and go back: mains adapter, phone charger, mouse, notepad, pen, business cards, deodourant and penknife. Whenever I go out with it, I know all I need to do is put my laptop in the bag, and I'm ready to go. I've had to buy a spare charger and mouse, but it's worth it to save the hassle of forgetting them.&lt;/p&gt;
&lt;h3&gt;Calendar and diary&lt;/h3&gt;
&lt;p&gt;This used to be such a problem with my wife and I double-booking things, but we've finally solved it using a shared Google calendar, synchronised on our phones. Don't book anything until you've checked the calendar on your phone.&lt;/p&gt;
&lt;h3&gt;Backup&lt;/h3&gt;
&lt;p&gt;I use Dropbox, but there are other things that are just as good. The important thing is that it is automatic. My phone automatically uploads photos and my computer files are saved in dropbox folders by default. Once it has been set up, it doesn't need thinking about.&lt;/p&gt;
&lt;h3&gt;On the way home could you just ...&lt;/h3&gt;
&lt;p&gt;When I used to commute by car, and had something to do on the way home, I'd often arrive home having forgotten to do it. The reason was driving on autopilot. I solved this by working out exactly where I needed to change my normal route - the particular junction. Before setting off home, I'd visualise it, and when I came up to it, I'd remember to turn off. Once I'd turned off, I wasn't on my usual route, so I wouldn't forget any more.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <pubDate>Sat, 22 Nov 2014 19:35:54 GMT</pubDate>
    </item>
    <item>
      <title>Continuous JavaScript Testing</title>
      <link>https://harriyott.com/2014/07/continuous-javascript-testing</link>
      <description>&lt;p&gt;Being used to NCrunch continuously running my unit tests as I type, I was looking for a similar solution for my JavaScript tests. I'm using Jasmine, which runs from a single HTML page. When I want to re-run the tests, I manually refresh the page in the browser.&lt;/p&gt;
&lt;p&gt;Obviously this is a PITN, so by adding a meta tag I kept from the 90s, I've got my page refreshing every 2 seconds and running the tests.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;meta http-equiv="refresh" content="2"&amp;gt;&lt;/code&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 10 Jul 2014 12:43:58 GMT</pubDate>
    </item>
    <item>
      <title>Ninject Controller Testing</title>
      <link>https://harriyott.com/2013/october-2013/ninject-controller-testing</link>
      <description>&lt;p&gt;I've been converting an old, large project to use NInject to inject services and repositories into my controllers, removing the base controller's code that newed them up. I wrote a handy unit test to make sure all the bindings worked for the controllers' constructors, which caught a few missing bindings before it passed.&lt;/p&gt;
&lt;pre data-gist-id="7137271"&gt;[TestMethod]
public void Can_create_controllers()
{
    var kernel = new StandardKernel();
    SiteResolver.RegisterComponents(kernel);

    typeof(AccountController)
        .Assembly
        .GetTypes()
        .Where(t =&amp;gt; typeof(Controller).IsAssignableFrom(t))
        .ToList()
        .ForEach(c =&amp;gt; kernel.Get(c));

    Assert.IsTrue(true);
}
&lt;/pre&gt;</description>
      <pubDate>Thu, 24 Oct 2013 15:10:15 GMT</pubDate>
    </item>
    <item>
      <title>Footballers on your software project team</title>
      <link>https://harriyott.com/2013/september-2013/footballers-on-your-software-project-team</link>
      <description>&lt;p&gt;Different roles in the software team map rather nicely to various types of footballer.&lt;/p&gt;
&lt;h3&gt;Developer: Striker&lt;/h3&gt;
&lt;p&gt;The player that actually achieves the goals of the project. Makes things happen, and gets the credit for a good result. Best paid.&lt;/p&gt;
&lt;h3&gt;Tester: Defender&lt;/h3&gt;
&lt;p&gt;Stops bad things happening, but nobody notices them until a bad thing happens. Any good things they do, they pass on to a striker, who will then do another good thing and take the credit.&lt;/p&gt;
&lt;h3&gt;Project Manager: Midfielder&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;Organises everyone around them, does lots of chasing about, convinces everyone that they're a necessary interface between the defenders and strikers to encourage a beautiful game and prevent long-ball development.&lt;/p&gt;
&lt;h3&gt;Graphic Designer: Tactician or Performance Analyst&lt;/h3&gt;
&lt;p&gt;Fully-trained expert in their field whose input and ideas are sought and ignored by the players. Because any player or coach can come up with a tactic that looks good to them on paper, and the results are hard to measure without comparisons, they're seen as optional and expendible when there's a limited budget.&lt;/p&gt;
&lt;h3&gt;Client: Goalkeeper&lt;/h3&gt;
&lt;p&gt;Supposedly supplies content and photographs, but mainly just shouts at the other players when something has gone wrong and it's too late to do anything about it. Too often just kicks the bug right up to a striker, when they should throw it short to a tester.&lt;/p&gt;
&lt;h3&gt;Users: Fans&lt;/h3&gt;
&lt;p&gt;Will let you know instantly if they don't like anything without suggesting a better solution, even though they know they can do a better job than just about anyone on the team. There'll be a few that are always singing no matter what, and the manager will try to please them even if it doesn't make financial sense to.&lt;/p&gt;</description>
      <pubDate>Mon, 30 Sep 2013 17:49:14 GMT</pubDate>
    </item>
    <item>
      <title>The Perfect Geek Cafe</title>
      <link>https://harriyott.com/2013/06/the-perfect-geek-cafe</link>
      <description>&lt;p&gt;Today, Relly &lt;a href="https://twitter.com/RellyAB/status/350568628376174592"&gt;tweeted about starting a geeky teashop&lt;/a&gt;. I have spent time trying to find the perfect laptop-friendly coffee-shop, and so have a few feature suggestions.&lt;/p&gt;
&lt;h2&gt;Sockets&lt;/h2&gt;
&lt;p&gt;Each table should have sockets to power the laptops. Preferably, under the table, or even as part of the table. Tripping over cables is bad.&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Usage Guidelines&lt;/h2&gt;
&lt;p&gt;One of my bigger social awkwardnesses is working out how many coffees per hour I should buy, and whether that goes down if I have lunch, but then if it's only the soup, does it go up a bit again? What should I do if it gets busy? Should I give up my seat in favour of another diner, or am I just as valid a customer? Whatever they are, the guidelines should be clearly displayed at the ordering point, on the menu and the website.&lt;/p&gt;
&lt;h2&gt;Ordering&lt;/h2&gt;
&lt;p&gt;I'll tell you what is a nuisance; wanting to buy another coffee, but not wanting to leave valuables on the table to go to the counter, so awkwardly carrying a laptop, wondering if anyone would really bother to steal the charger, or should I take that too? Table service would be good, but could be seen as a way to force the customer to buy more or get out. A button, as used to someone flight attendants should be provided.&lt;/p&gt;
&lt;p&gt;Also, twitter would be used for ordering. When you visit the cafe, you follow their twitter account, which will auto-follow back. When requiring sustenance, the order is DMed with the table number, and delivered when ready.&lt;/p&gt;
&lt;p&gt;&lt;a title="Lloyds Coffee House by Simon Harriyott, on Flickr" href="http://www.flickr.com/photos/harriyott/3984416269/"&gt;&lt;img src="http://farm3.staticflickr.com/2605/3984416269_d432e52851_n.jpg" alt="Lloyds Coffee House" width="320" height="240" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Comfort Breaks&lt;/h2&gt;
&lt;p&gt;A bit like ordering, carrying a laptop to the loo is disquieting. This needs a bit more thought, but the flight-attendant button could be useful here too. Once summoned, the table attendant could combine keeping an eye on the customer's gear whilst clearing the nearby tables.&lt;/p&gt;
&lt;h2&gt;Wi-fi&lt;/h2&gt;
&lt;p&gt;Obviously free, fast, and the password clearly displayed, but a common problem with cafe wi-fi is that it stops working, and a reset is required. All staff should know how to reset the router. Resets would be requested with the flight-attendant button.&lt;/p&gt;
&lt;p&gt;For bonus points, each table will contain a hub with CAT5 cables in place (probably glued or something).&lt;/p&gt;
&lt;h2&gt;Music&lt;/h2&gt;
&lt;p&gt;There wouldn't be any. Especially not Heart FM.&lt;/p&gt;</description>
      <pubDate>Fri, 28 Jun 2013 13:53:55 GMT</pubDate>
    </item>
    <item>
      <title>Managing the development process</title>
      <link>https://harriyott.com/2013/06/managing-the-development-process</link>
      <description>&lt;p&gt;To set the scene; three years ago, I started on a large software development project for a football club, and I was working as the sole developer. I employed the release-early-and-often approach, for a number of reasons. When the club got to use the software early on, they could see other areas that it could be used for, and so a couple of major additions were requested.&lt;/p&gt;
&lt;p&gt;There wasn't much in the way of a spec, as the club wanted a prototypical approach so they could see how things worked before deciding how to proceed. It's not the most efficient way, but this was the first software project the club had commissioned. Hey, at least it wasn't waterfall.&lt;/p&gt;
&lt;p&gt;The software was being used in production whilst development continued, which drove further requirements, and after a year or so, we had a stable release, and development stopped. The project was considered a success, and was being used around the world.&lt;/p&gt;
&lt;h3&gt;Project management&lt;/h3&gt;
&lt;p&gt;Being just me, I was the developer, business analyst, and project manager. Being mainly a developer, that was where I focussed my attention. I considered project management a lower priority, as I was the only &amp;ldquo;resource&amp;rdquo; on the project (n.b. I don't like being called a resource).&lt;/p&gt;
&lt;p&gt;My process then was to keep track of the requirements in FogBugz. New features and bugs were either emailed or telephoned in by the one&amp;nbsp;&lt;em&gt;stakeholder&lt;/em&gt;&amp;nbsp;who I dealt with from the club. We'd discuss which ones were the highest priority, and I'd update FogBugz accordingly. As a single developer, I could use the free version of FogBugz, and there didn't seem much point paying for a second user so the stakeholder could have access.&lt;/p&gt;
&lt;p&gt;Once I'd finished a few tasks, I'd deploy a release to the staging server, and email the stakeholder with an overview of the changes I'd made, and asked him to check it over before it went live. Usually this was fine, but there were times when things were overlooked, and bugs or misunderstandings were uncovered only when users started using the live version in earnest. As I had different branches in git for live, staging and main development, it was quick and easy to fix and deploy a new version.&lt;/p&gt;
&lt;p&gt;&lt;a title="The Football League by Simon Harriyott, on Flickr" href="http://www.flickr.com/photos/harriyott/5713302843/"&gt;&lt;img src="http://farm4.staticflickr.com/3498/5713302843_aea2ac330a_n.jpg" alt="The Football League" width="320" height="240" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Moving on&lt;/h3&gt;
&lt;p&gt;So, once finished, I moved on to some other projects, for a couple of years. A month or so ago, the club asked me for a new phase of development. They had been collecting some new requirements; small improvements to existing features, and some brand new things too.&lt;/p&gt;
&lt;p&gt;I guess I've become a little wiser in the last couple of years, (and looking at the code, a much better developer too) and wanted to improve the process. The first thing we did was to meet up and talk through all the requirements in detail. This involved putting their list of requirements into a Google Drive document, me asking lots of difficult questions about what exactly things meant, but hang on, if we do that, then what happens with this other thing, and oh yeah, maybe we don't mean that, but actually we do, so we'll have to work round that by doing this, but perhaps there's a better way, and so on. I didn't let him off the hook on anything, so if he didn't understand what he wanted after all that, we got rid of it.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;We were editing the document as we discussed each feature, which meant a really tight feedback loop. I've seen people read a spec or requirements document, ask for some changes, and a week later, get another draft, and get bored because it's mostly similar to the last one, and even with changes tracked, it's hard to read and notice the differences, and it's really dull. This way, we did it all in real time. The next step was to convert this document into tasks; into a plan.&lt;/p&gt;
&lt;p&gt;In one of my subsequent projects, working with a team of around half a dozen developers, a tester and a product manager, in a fairly agile environment, we used Pivotal Tracker to manage the workload. We found a few problems with Pivotal Tracker, and perhaps we weren't being as agile as we wanted to be, but it was mostly workable.&lt;/p&gt;
&lt;p&gt;Since then, I've been using Trello to keep track of what I'm doing, but really only as a to-do list for myself. This time, however, I want to make the process much more transparent to the client, and for him to be more involved. Being free, I invited the client to Trello, and gave him access to the &lt;em&gt;board&lt;/em&gt; for the project. A board is basically a list of lists.&lt;/p&gt;
&lt;p&gt;I then added the following columns, or lists, to the board:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Backlog&lt;/li&gt;
&lt;li&gt;Current phase&lt;/li&gt;
&lt;li&gt;In development&lt;/li&gt;
&lt;li&gt;Code complete&lt;/li&gt;
&lt;li&gt;In staging to test&lt;/li&gt;
&lt;li&gt;Testing failed in staging&lt;/li&gt;
&lt;li&gt;Testing passed in staging&lt;/li&gt;
&lt;li&gt;Live&lt;/li&gt;
&lt;li&gt;Done&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Backlog&lt;/h3&gt;
&lt;p&gt;I went through the requirements / spec document, and for each discrete task, I added a &lt;em&gt;card&lt;/em&gt; to the backlog column. I gave the card a brief title, and copied and pasted the requirement text from the Google document into the card description. Each Trello card has its own URL, so I copied and pasted that back into the spec document to allow easy cross-referencing.&lt;/p&gt;
&lt;h3&gt;Current phase&lt;/h3&gt;
&lt;p&gt;The idea is for the cards to migrate from left to right across the board's columns. Initially, we agree which cards from the backlog I should work on next, and put them in the current phase column. This week, I let the client do this himself!&lt;/p&gt;
&lt;h3&gt;In development&lt;/h3&gt;
&lt;p&gt;I pick which ever card from the current phase column, and move it into the third column. At this point, I may add a checklist to the card for the specific development tasks, so I can tick them off when I've done them. It makes me feel happy, and the card seems less daunting. Whilst I'm developing, I may discover more things I need to add to the list.&lt;/p&gt;
&lt;h3&gt;Code complete&lt;/h3&gt;
&lt;p&gt;Once I've finished development, the card goes to the right again. I'll add another checklist to the card, for the acceptance tests, which I'll describe in a bit. I'll then work on the next card in the current phase. Once they're all done, then I'm ready for a staging release. The client may not be ready, however, if he hasn't finished testing the last staging release. This is fine, as he has plenty of his own work to do. I'll just add another item or two from the backlog into the current phase and continue developing.&lt;/p&gt;
&lt;h3&gt;In staging to test&lt;/h3&gt;
&lt;p&gt;Once we're both ready for a staging release, I rebase the staging branch in the git repository, and deploy to the server. I'll move all the cards from the code complete column, which all have acceptance tests.&lt;/p&gt;
&lt;p&gt;To avoid the problems I used to have with bugs appearing on the live site, I've made a bigger deal of the client testing on the staging site. A quick look over is no longer good enough. Each card has enough tests in the checklist to cover the feature, and is based on the requirements, which are handily at the top of the card. The client can add extra tests too. He then works through the tests on the card and ticks them off when they pass.&lt;/p&gt;
&lt;h3&gt;Testing failed in staging&lt;/h3&gt;
&lt;p&gt;If any one of the acceptance tests fails, the card is moved here, with a comment from the client explaining why. There have been other reasons for cards ending up here, which have been useful too. The client has noticed that a feature wasn't completely defined, or once he's actually used it, something else needs changing. Technically it isn't a bug, or perhaps even a legitimate reason to fail a card, but it raises the problem, so it's a good thing. In these cases, I've added a new card to the current phase describing the enhancements, which then follows the usual process to the right. The original failing card is then considered to have passed, and moves to the next column.&lt;/p&gt;
&lt;p&gt;Valid test failures are then moved back to the current phase. If the testing hasn't started immediately after a staging release, this could be a problem, as I'll have already started a new phase, and have new cards in the second and third columns. I saw two ways round it; a new column for failed cards in development, or to distinguish the cards in the existing columns. Using Trello's labels, I marked failed cards that are back in the current phase with an orange label, so I can clearly see the difference. I checkout the staging branch in git, and work on the fixes as a priority.&lt;/p&gt;
&lt;p&gt;When all the rework is complete, I do another staging release.&lt;/p&gt;
&lt;h3&gt;Testing passed in staging&lt;/h3&gt;
&lt;p&gt;Once all the tests have passed, we are, in theory, ready for a live release. I make sure I ask the client if he really is ready, I rebase the live git branch from the staging branch and deploy.&lt;/p&gt;
&lt;h3&gt;Live&lt;/h3&gt;
&lt;p&gt;The cards are moved into the live column. In the last release with delayed testing, another phase was ready for staging, but had to wait, like a train at a signal. Once the code was deployed to live, I released the next version onto staging for testing straight away.&lt;/p&gt;
&lt;h3&gt;Done&lt;/h3&gt;
&lt;p&gt;There is the possibility that something breaks the live site, so I keep a separate column for done. The most recently deployed cards are left in the live column, as a reminder for me in the event of a bug report from the live site. As it happens, there haven't been any yet. Before doing a new live release, I'll move everything from the previous release in the live column to the done column.&lt;/p&gt;
&lt;h3&gt;Wrapping up&lt;/h3&gt;
&lt;p&gt;I've made an &lt;a href="https://trello.com/board/example/51c4ca598eb62c8502000e9a" target="_blank"&gt;example board&lt;/a&gt; with a single card you can look at.&lt;/p&gt;
&lt;p&gt;I've found the benefits of this approach are that the client has more visibility into exactly what I'm doing, and the current progress and status of the project, which helps when his boss asks how things are going.&lt;/p&gt;
&lt;p&gt;The releases are more stable, and the client is more confident of the quality of the code.&lt;/p&gt;
&lt;p&gt;I'm happier, and more relaxed, as some of the responsibility is now with the client. We all know that developers are the worst people to test their own code. In the absence of a trained software tester, a happy client is the next best thing.&lt;/p&gt;
&lt;p&gt;In all, I wanted to explain how, as a one-man-band, I manage a project. I'm not claiming it is the right way, nor that it should be labelled agile, nor that I won't change it. If having read this, you choose to review your process, by taking on some of my ideas, or working out your own, then that's great.&lt;/p&gt;
&lt;p&gt;Really, the previous paragraph is partly a pre-emptive strike against potential commenters wanting to tell me I'm doing it wrong. I feel a little vulnerable admitting past mistakes, and possibly current ones too, but this stuff doesn't come as easy to me as coding.&lt;/p&gt;</description>
      <pubDate>Sat, 22 Jun 2013 00:23:33 GMT</pubDate>
    </item>
    <item>
      <title>Refactoring with actions</title>
      <link>https://harriyott.com/2013/04/refactoring-with-actions</link>
      <description>&lt;p&gt;Consider the following code, which fetches and parses some data (from a &lt;a href="https://github.com/dustin/py-github/blob/master/github/data/org.members.public.xml" target="_blank"&gt;random gist on the web&lt;/a&gt;) into properties on an object:&lt;/p&gt;
&lt;pre data-gist-id="79e7864bd22333f32961"&gt;public void Parse(string fileName)
{
    var userAccounts = new Collection();

    var html = new HtmlDocument();
    html.Load(fileName);
    var document = html.DocumentNode;
    var users = document.QuerySelectorAll("user");
    foreach (var userNode in users)
    {
        var userAccount = new UserAccount();

        var node = userNode.ChildNodes["name"];
        if (node != null &amp;amp;&amp;amp; !string.IsNullOrEmpty(node.InnerText))
        {
            userAccount.Name = node.InnerText;
        }
        node = userNode.ChildNodes["company"];
        if (node != null &amp;amp;&amp;amp; !string.IsNullOrEmpty(node.InnerText))
        {
            userAccount.Company = node.InnerText;
        }
        node = userNode.ChildNodes["blog"];
        if (node != null &amp;amp;&amp;amp; !string.IsNullOrEmpty(node.InnerText))
        {
            userAccount.Blog = node.InnerText;
        }
        node = userNode.ChildNodes["email"];
        if (node != null &amp;amp;&amp;amp; !string.IsNullOrEmpty(node.InnerText))
        {
            userAccount.Email = node.InnerText;
        }
        node = userNode.ChildNodes["id"];
        if (node != null &amp;amp;&amp;amp; !string.IsNullOrEmpty(node.InnerText))
        {
            userAccount.Id = int.Parse(node.InnerText);
        }
        node = userNode.ChildNodes["followers-count"];
        if (node != null &amp;amp;&amp;amp; !string.IsNullOrEmpty(node.InnerText))
        {
            userAccount.Followers = int.Parse(node.InnerText);
        }
        userAccounts.Add(userAccount);
    }
}
&lt;/pre&gt;
&lt;p&gt;There's lots of duplicated code, and the first target for refactoring are the longest lines, which can be extracted into a method to check whether a child node is valid:&lt;/p&gt;
&lt;pre data-gist-id="08c8712383fc5277f64b"&gt;private static bool IsNodeValid(HtmlNode node)
{
    return node != null &amp;amp;&amp;amp; !string.IsNullOrEmpty(node.InnerText);
}
&lt;/pre&gt;
&lt;p&gt;Better, but there's still scope to reduce duplication.&lt;/p&gt;
&lt;pre data-gist-id="59997bdb002f0abd01fa"&gt;...
node = userNode.ChildNodes["email"];
if (IsNodeValid(node))
{
    userAccount.Email = node.InnerText;
}
node = userNode.ChildNodes["id"];
if (IsNodeValid(node))
{
    userAccount.Id = int.Parse(node.InnerText);
}
...
&lt;/pre&gt;
&lt;p&gt;Ideally, the blocks of five lines would be refactored into a new method. Because we're accessing different properties of the &lt;code&gt;userAccount&lt;/code&gt;, we can't pass them in directly, nor can we pass them in as references, as they're not variables. Also, some of the properies are &lt;code&gt;int&lt;/code&gt;s, so we'd need two methods.&lt;/p&gt;
&lt;p&gt;We can, however, pass in an action to the new method, which performs the assignment (the only unique code in the five lines):&lt;/p&gt;
&lt;pre data-gist-id="26d94a7d1295582e86c9"&gt;private void CopyNodeData(HtmlNode userNode, string name, Action func)
{
    var node = userNode.ChildNodes[name];
    if (node != null &amp;amp;&amp;amp; !string.IsNullOrEmpty(node.InnerText))
    {
        func(node.InnerText);
    }
}
&lt;/pre&gt;
&lt;p&gt;This now means each assignment can now take a single line:&lt;/p&gt;
&lt;pre data-gist-id="c2688215ff1657e98630"&gt;CopyNodeData(userNode, "name", s =&amp;gt; userAccount.Name = s);
CopyNodeData(userNode, "company", s =&amp;gt; userAccount.Company = s);
CopyNodeData(userNode, "blog", s =&amp;gt; userAccount.Blog = s);
CopyNodeData(userNode, "email", s =&amp;gt; userAccount.Email = s);
CopyNodeData(userNode, "id", s =&amp;gt; userAccount.Id = int.Parse(s));
CopyNodeData(userNode, "followers-count", s =&amp;gt; userAccount.Followers = int.Parse(s));
&lt;/pre&gt;
&lt;p&gt;The third parameter is a lambda that will be called only if the node is valid. We've removed lots of duplication, making the code more readable.&lt;/p&gt;</description>
      <pubDate>Fri, 26 Apr 2013 16:14:28 GMT</pubDate>
    </item>
  </channel>
</rss>