<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Western Devs</title>
  
  <link href="/feeds/kyle_baley" rel="self" type="application/atom+xml"/>
  <link href="https://westerndevs.com" rel="alternate" type="application/atom+xml"/>
  
  <updated>2026-03-22T17:42:31.819Z</updated>
  <id>https://westerndevs.com/</id>
  
  <author>
    <name>Western Devs</name>
	<uri>https://westerndevs.com</uri>
    <email>info@westerndevs.com</email>
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title type="html">An exploration of Azure Functions for a side project</title>
    <link href="https://westerndevs.com/_/azure-functions-for-side-projects/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/_/azure-functions-for-side-projects/</id>
    <published>2024-06-23T04:00:00.000Z</published>
    <updated>2026-03-22T17:42:31.819Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>This is a short PSA for people (or, more likely, just future me) to describe two issues I ran into while migrating a bash file to Azure Functions. Namely:</p><ul><li>How do I see the exceptions that happened?</li><li>How do I configure an Azure Function app to save a PDF to a Google Drive folder</li></ul><p>This is from the perspective of someone who doesn't need their app to be highly available/scalable/reliable/tenable/affable/inscrutable/explicable, which the documentation for all cloud products seems to assume (and, it must be said, rightfully so).</p><a id="more"></a><p>I like crossword puzzles. And I like doing them on paper. I subscribe to a few services, including the New York Times, <a href="https://avxwords.com/" target="_blank" rel="noopener">AVCX</a>, <a href="https://xwordcontest.com/" target="_blank" rel="noopener">Matt Gaffney</a>, the <a href="https://pmxwords.com/" target="_blank" rel="noopener">Muller Monthly Music Meta</a>, and a couple of others, both free and paid. For years, I've maintained a <a href="https://github.com/kbaley/xword-downloader" target="_blank" rel="noopener">bash file</a> that downloads PDFs from various services that allow it (and some that likely don't) and merges them into a single document for printing. It works fine but I wanted to try my hand at moving it to an Azure Function app that ran on a timer rather than on demand like I do with the bash file.</p><p>So the app in question is pretty simple and the code itself isn't super interesting. Here are the logistics:</p><ul><li>Runs nightly</li><li>Downloads PDFs from various crossword providers (three so far)</li><li>Saves the PDF to Google Drive</li></ul><p>The last one is only because that's where I keep them after they're printed. I have an archive of the puzzles I've downloaded going back a number of years. I've no idea why except that I have the storage available. Maybe I'll run through them again in retirement but the more likely scenario is that my children will mass delete them in a housecleaning exercise after I die.</p><p>With that in mind, let's get to the two areas where I struggled.</p><h2>App Insights</h2><p>By most accounts, App Insights on Azure is a powerful tool and telemetry in general is a big business. But I've always shied away from the Diagnose/Investigate/Metrics tabs because they're just plain overwhelming for an aging hillbilly who's used to scrolling through IIS log files. So when I wake up in the morning and the latest Times puzzle isn't there, I need to figure out where to go.</p><p>My solution (which may not align with <em>the</em> solution) is pretty simple in the end:</p><ul><li>Navigate to the Function app in Azure</li><li>Go to Monitoring | Logs</li><li>Query the <code>exceptions</code> table with no other qualifers and with an appropriate time range</li></ul><p>This shows the exceptions and the stack traces within the time period. Given this runs nightly, there likely won't be more than a couple in the last 24 hours.</p><h2>Connect to a Google Drive folder</h2><p>Ugh...</p><p>I've said this on more than one occasion: I hate all things security-related including, but not limited to: certificates, OAuth, face ID, fingerprint scanners, digital signatures, encryption, authentication providers, 2FA, MFA, SSL, VPN, CVE, IAM, JWT, SSO, AES, TSA, passkeys, passwords, passports, key fobs, car alarms, and bike locks.</p><p>Setting up an Azure Function App to connect to Google Drive touches on several of these pet peeves and adds a few more. Here's what I eventually did to get this to work:</p><ul><li>Create a project in my Google Developer Console</li><li>Enable the Google Drive API (it wasn't enabled by default)</li><li>Create a <a href="https://cloud.google.com/iam/docs/service-account-overview" target="_blank" rel="noopener">service account</a> credential</li><li>Under the new service account, create a <em>key</em>. This downloads a file to your computer.</li><li>Upload the file to the Azure Storage account for the Azure Functions app in a File Share folder called <code>secrets</code></li><li>Create two environment variables for the Azure Function:<ul><li>GoogleApiSecretsFileName: the name of the file (include the .json extension) I downloaded for the key</li><li>GoogleDriveFolderId: The ID of the folder where the puzzles should be saved. (Navigate to the folder in a browser. The ID is everything after <code>folder/</code> in the URL.)</li></ul></li><li>Share the Google drive folder with the Google API service account (with Editor access). The email address is on the Credentials page in the Google Developer Console.</li></ul><p>Oh, and also write the actual code.</p><p>The <a href="https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-google" target="_blank" rel="noopener">Azure documentation</a> and ChatGPT suggest strongly that OAuth2 credentials (instead of a service account) should work if you set up Google as an authenticator on the Azure Functions app. I ran into problems with this and I <em>think</em> it's because I was running locally. I have the app set up to do everything through a console app instead of through the Azure Functions host when running in DEBUG mode and I suspect the problems are because my local app hasn't gone through the necessary authentication process. Either way, a service account explicitly says it's for unattended server-to-server scenarios, which this is, so I've justified it in my head, even if the Azure documentation suggests something else.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;This is a short PSA for people (or, more likely, just future me) to describe two issues I ran into while migrating a bash file to Azure Functions. Namely:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How do I see the exceptions that happened?&lt;/li&gt;
&lt;li&gt;How do I configure an Azure Function app to save a PDF to a Google Drive folder&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is from the perspective of someone who doesn&#39;t need their app to be highly available/scalable/reliable/tenable/affable/inscrutable/explicable, which the documentation for all cloud products seems to assume (and, it must be said, rightfully so).&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title type="html">Truly syncing multiple calendars</title>
    <link href="https://westerndevs.com/_/syncing-multiple-calendars/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/_/syncing-multiple-calendars/</id>
    <published>2024-06-17T04:00:00.000Z</published>
    <updated>2026-03-22T17:42:31.828Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>For reasons that I really need to investigate, I'm apparently a busy fellow. Such that I now maintain a rather ungainly number of calendars. Three to be exact. That's one personal calendar and one for each of two clients.</p><a id="more"></a><p>It's a fairly simple process to <em>view</em> all of these calendars in a single place. All the major calendar providers generously provide iCal links at the very least to let you combine everything in a single view and you can even turn individual calendars on and off.</p><p>I have two problems with this.</p><p>First, I often don't want to have all of these calendars visible in a single view on my screen. Screen-sharing is common and on more than one occasion, I've said, &quot;let's schedule a meeting&quot; and switched to the shared calendar view where all my personal <a href="https://en.wikipedia.org/wiki/Dudeism" target="_blank" rel="noopener">Dudeism meetings</a> are there for all to see.</p><p>Second (and more important than risking an argument with a nihilist), people booking meetings with me don't see the unavailable slots on the other calendars, leading to double-bookings. I didn't know how good I had it in high school when this was...let's just say, &quot;not a problem&quot;.</p><p>So I went on the hunt for a solution that met these criteria:</p><ol><li>The ability to book an event on one calendar and have the time blocked on the other two.</li><li>Have the time on other calendar show as a generic &quot;busy&quot; or &quot;unavailable&quot;. I.e. don't broadcast the details of the events from other calendars.</li><li>Have a centralized view <em>somewhere</em> of all the events <em>without</em> duplicates. I.e. without a bunch of &quot;busy&quot; or &quot;unavailable&quot; events.</li><li>Ideally, works with both Google and Microsoft calendars</li></ol><p><img src="/images/flintstones-camera.jpg" alt="The early days of online meetings">)</p><p>The easiest way to achieve this, of course, is to do it yourself. When you book an event on one calendar, you go through the motions of creating a generic event on the other two. This meets all the criteria but depending on how heavily your client leans into the &quot;all meetings/all the time&quot; project management style, this gets old real fast. But to be fair, it's served me reasonably well for many years.</p><p>This led naturally to a search for a technical solution, which yielded the following options:</p><ul><li><a href="https://www.onecal.io/" target="_blank" rel="noopener">OneCal</a></li><li><a href="https://calendarbridge.com/" target="_blank" rel="noopener">CalendarBridge</a></li><li><a href="https://flexibits.com/fantastical" target="_blank" rel="noopener">Fantastical</a></li><li><a href="https://calendly.com" target="_blank" rel="noopener">Calendly</a></li><li><a href="https://reclaim.ai/" target="_blank" rel="noopener">Reclaim.ai</a></li><li><a href="https://www.spikenow.com/" target="_blank" rel="noopener">Spike</a></li><li><a href="https://www.calendar.com/blog/how-to-sync-your-calendar-across-all-devices/" target="_blank" rel="noopener">Syncing calendars</a></li></ul><p>I have OneCal at the top because it's (currently) my favorite option. But I'll skim through the rest and provide reasons why I didn't go with them. I'll use my tried-and-true practice of claiming that my choice is the One True Way™ and everyone else is wrong as a means of encouraging people to comment with their own options, if only to prove me wrong. Which I'm quite happy to be. But still, that's not the case here. I'm right and you're wrong. Comment below if you disagree.</p><p>Back to the evaluations. I'll start with some of the quicker ones.</p><h3>Syncing calendars</h3><p>I'll be honest, I just kind of skimmed this article. I have kind of a vague idea of what they're going for but this seems geared more toward people that are managing someone else's calendar. Plus I don't <em>think</em> it meets the criteria of actually booking the time off on the other calendar. Either way, it looks kind of cumbersome to set up and to manage.</p><h3>Spike</h3><p>I didn't give this much more than a glance. It does <em>way</em> more than I want it to and calendar sync doesn't appear to be among their more popular features. In any case, the banner says &quot;email and chat app&quot; and the byline talks about teams and partners. I don't think I'm their target audience.</p><h3>Reclaim.ai</h3><p>Also has many features I don't need nor want though at least it's focused on calendars. Google Calendar specifically. Outlook is coming soon. So while calendar sync is one of the highlighted features, the lack of Outlook, as well as the focus on collaboration and AI and &quot;smart&quot; also suggests it's overkill.</p><h3>Calendly</h3><p>I've used Calendly before to send people links to schedule calls with me. It's a lovely app and the free version is quite powerful. I'd recommend it even over Google Calendar's built-in &quot;scheduling slots&quot; or &quot;appointment schedules&quot; or whatever they're calling the feature now.</p><p>That said, based on what I read, I don't think they offer what I need. It looks like the paid plans will let you connect to multiple calendars so that you can give someone a link with a more comprehensive view of your availability. But I need something for internal people who have direct access to my calendar.</p><h3>Fantastical</h3><p>This is just plain a gorgeous app, if you'll forgive an awkwardly-worded almost-oxymoron. I keep threatening to use it every couple of years but can't really justify it. The major selling points are syncing across multiple devices, the natural language parsing, the UX, and integration with a lot of other productivity tools I don't use. Seriously, you should check this app out...</p><p>...except if all you need is to sync events across multiple calendars. I'm still in the midst of testing the app but the process to ensure an event appears as a block of unavailable time on multiple calendars seems to be to duplicate it. Which, to be fair, they make <em>really</em> easy. And if you keep the details the same in all the clones, the unified calendar view removes the duplicates. But that doesn't meet criteria #2 for me above. And if you change the name, it shows as a separate event which is a bit clumsy. For this feature specifically, it's not much better than what I can achieve in Google Calendar natively. But still, give it a whirl.</p><h3>Final verdict: OneCal (CalendarBridge runner up)</h3><p>That leaves two options: OneCal and CalendarBridge. Both do the same thing though their respective marketing teams might squabble over technicalities. Their primary purpose is to sync multiple calendars and avoid double-bookings. Perfect.</p><p>Both services sync one calendar to another by cloning events between them and monitoring the source calendar for new events. Both give you control over what information you want to copy over and have similar options like excluding events of a given colour or excluding events where you're marked free (useful if you track birthdays in your calendar). Once syncing was set up, my calendars looked pretty much exactly how they did when I was doing it manually.</p><p>Neither service has a mobile app from what I can tell which is a shame, especially in the case of OneCal because unlike CalendarBridge, they offer a unified calendar view of all your calendars with the option to hide the cloned events. This is a nice touch and while the view is mobile friendly, it's missing some features, like a three-day view or the ability to put a widget on your phone.</p><p>Pricing is virtually identical on both services in that the one I would need runs about $100/year to sync three calendars. OneCal has the ability to do two-way syncs between calendars or to broadcast a one-way sync from one calendar to multiple other calendars, something that I don't <em>think</em> CalendarBridge does. In CalendarBridge, a two-way sync is done with two one-way syncs and their pricing reflects that. The basic plan for both essentially covers two calendars so I'd be looking at premium for both.</p><p>Setting up the syncs seemed nicer in OneCal to me though I can't 100% say why. Both essentially walk you through a three-ish-step wizard to do it. Maybe OneCal worded things better or laid things out more intuitively for me. I found myself wondering if I was doing it right a couple of times in CalendarBridge. OneCal also felt zippier in general.</p><p>Both apps give you booking links (a la Calendly) which is nice if you need that. OneCal apparently integrates with Zoom to automatically add a meeting link to appointments booked online but I didn't test that out since I don't need it. Neither requires a credit card to sign up which is an underrated feature these days.</p><p>A couple of little UX things. First, OneCal has another underrated feature: a big ol' Delete My Account button at the bottom of the settings page. This is great for someone who wants to test things out, decide it's not for them, and move on. I'm not planning to use CalendarBridge and I've cancelled my free trial but the account remains. There's no obvious way to delete it so I guess I'm a CalendarBridge user indefinitely now. I suppose that's good for them to pump up their user numbers in case someone wants to buy them out but doesn't do me any good.</p><p>Second thing is CalendarBridge's login mechanism. To log in, I enter my email address, they email me a code, I enter the code. It's basically the standard 2FA mechanism from fifteen years ago. There's no password, no Google/Apple/Facebook/etc option. It's kinda weird. Like the developers have a personal agenda against traditional authentication and this is their manifesto.</p><p>So both apps do what I want and cost roughly the same but I give the edge to OneCal for their usability and their unified Calendar view. The price point is high enough that I'll give it a few days before pulling the trigger so I can decide if I want this more than I want to watch seasons 2 of Silo and Severance.</p><p>The alternative is to set up an auto-decline automation for all meetings which I haven't fully taken off the table yet.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;For reasons that I really need to investigate, I&#39;m apparently a busy fellow. Such that I now maintain a rather ungainly number of calendars. Three to be exact. That&#39;s one personal calendar and one for each of two clients.&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title type="html">Go just big enough for your needs or go home</title>
    <link href="https://westerndevs.com/bash/Go-just-big-enough-for-your-needs-or-go-home/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/bash/Go-just-big-enough-for-your-needs-or-go-home/</id>
    <published>2020-05-18T19:42:18.000Z</published>
    <updated>2026-03-22T17:42:31.814Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>I love crossword puzzles. This post is only peripherally about crosswords specifically but I say it up front to filter my readership to those who might have a personal kinship with me and thus, are less likely to write angry comments.</p><a id="more"></a><p>For the longest time, the most commonly-used app on my phone by at least an order of magnitude was <a href="https://apps.apple.com/us/app/crosswords-classic/id284036524" target="_blank" rel="noopener">Crosswords Classic</a> by Stand Alone, Inc. (They have a newer version that's probably better but every time I've tried it, there's a &quot;who moved my cheese&quot; factor I've never really cared to overcome.) When I had an Android device, it was <a href="https://play.google.com/store/apps/details?id=com.totsp.crossword.shortyz&amp;hl=en" target="_blank" rel="noopener">Shortyz Crosswords</a>. Both excellent and both worth whatever price I paid for them (I've long forgotten).</p><p>However, in my advancing years, I've taken to the printed page more and more and in the last year or so, I've been printing crosswords and doing them in pen and paper instead. This was a good move for two reasons: 1) My screen time dropped by more than half, and 2) it opened up several crossword providers that, to my knowledge, aren't available in the apps. For reasons unknown to me, many crossword providers are not making their work available to these apps (e.g. USA Today, Washington Post, and New York Times). In fact, several of them have set up their own payment systems to subscribe to them (e.g. American Values, Matt Gaffney, Elizabeth Gorski).</p><p>The upside is that now I have a whole new world of crosswords in an arguably more leisure form. The downside is why you're reading this post: now I have to hunt and peck my way through half a dozen providers, using different delivery mechanisms, rather than have an app just go out and get them. My workflow for this used to be:</p><ol><li>As emails come in from email providers, save the PDFs in a special folder</li><li>For others:<ul><li>Navigate to the website</li><li>Navigate to each day</li><li>Download the PDF</li></ul></li><li>Once I have everything collected, merge the crosswords into a single PDF and print</li><li>Archive the ones I merged</li><li>Repeat when I run out of crosswords</li></ol><blockquote><p>Fun fact: Step 3 isn't as onerous as you might think. I spent a good half hour building an Automator action in MacOS to do this until I found out the functionality is built into the right-click menu.</p></blockquote><p>As you can imagine, this virtually reeks of needing automation. So being a good developer, I designed in my head a system that would meet my needs:</p><ul><li>An ASP.NET Core-based app with a React front-end</li><li>Authentication with multiple providers (Google, Facebook, etc.)</li><li>Azure storage to store the PDFs</li><li>Azure Logic Apps to parse emails that I send to a special address</li><li>Azure function triggered nightly to download from non-email providers</li><li>Some (hopefully free) PDF library to merge the PDFs</li><li>A utility to parse XML into an image to be exported as a PDF</li></ul><p>(That last one is mostly for USA Today but basically any puzzle provided by uclick.com. They have a special XML format that describes crosswords.)</p><blockquote><p>Let's ignore the cost-benefit of building all this vs. the 10 minutes each week I spend on it manually and chalk it up to: because it's there</p></blockquote><p>I'll skip ahead a bit so I can get to my point. I was a couple of days into this when I remembered <a href="https://www.reddit.com/r/crossword/comments/dqtnca/my_automatic_nyt_crossword_downloading_script/" target="_blank" rel="noopener">a Reddit post</a> I had read recently where someone had automated the download of New York Times crosswords (provided you had a subscription, of course) using a bash script. Then it occurred to me: Do I <em>need</em> all this infrastructure? Azure storage, hosting, logic apps, functions? Could I expand on this bash script from this random, helpful stranger instead?</p><p>Skipping ahead a bit further and the answers are, of course: <a href="https://github.com/kbaley/xword-downloader" target="_blank" rel="noopener">no I don't, and yes I can</a>. The new stack:</p><ul><li>bash</li><li>Zapier</li><li>Mailparser.io</li><li>C# console app</li><li>XML/XSLT/HTML/CSS Grid</li><li>Google Chrome (headless)</li><li>The built-in MacOS functionality I mentioned earlier</li></ul><p>I'm using Zapier/Mailparser.io to handle the email providers, C#/XML/XSLT for the uclick providers to transform the XML into HTML (there's very little C#), Chrome to print that HTML to PDF, built-in MacOS to merge the PDFs, and bash to tie it all together.</p><p>Here's my new workflow:</p><ol><li>Run the bash script</li><li>Print</li><li>Solve</li><li>Repeat when I run out</li></ol><p>... and the only reason I haven't automated #2 is because I haven't looked into the <code>lp</code> command yet and I like to spot-check the NYT puzzles before I print them.</p><p>Now all this is not polished by any definition of the word and it is highly customized to my environment. But by gum, it works. And it still gave me the same rush I've always felt when I've been working at something and it eventually works. Figuring out Zapier's and MailParser's rule systems, re-learning XSLT, seeing HTML files disappearing and working PDFs taking their place. Getting into the minutiae of something that should be easy (like URI-decoding text in XSLT 1.0) but isn't. Not to say, &quot;How can I learn Azure?&quot; but to break a problem down, find a tool, and execute until it works. This is the same routine I used almost 40 years ago when I thought to myself, &quot;I wonder if I can make it say, 'TAKE THAT LOSERS!!!' whenever I score a touchdown&quot; on the old ASCII-based football game in my dad's office computer they let me sit at while waiting for a dentist appointment.</p><p>It's likely I'll be the only one who uses this thing, and to be honest, I hope I am because the idea of supporting it gives me the shakes. My original design was certainly more ambitious and might be easier to turn into a conference talk. But it's overkill. Maybe there's a market for applications that collect and manage crossword puzzle PDFs; I'm past the point where I want to build an application that meets that need first and my personal need second.</p><p>Kyle the Unsolveable</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;I love crossword puzzles. This post is only peripherally about crosswords specifically but I say it up front to filter my readership to those who might have a personal kinship with me and thus, are less likely to write angry comments.&lt;/p&gt;
    
    </summary>
    
      <category term="bash" scheme="https://westerndevs.com/categories/bash/"/>
    
    
      <category term="bash" scheme="https://westerndevs.com/tags/bash/"/>
    
      <category term="XML" scheme="https://westerndevs.com/tags/XML/"/>
    
      <category term="XSLT" scheme="https://westerndevs.com/tags/XSLT/"/>
    
  </entry>
  
  <entry>
    <title type="html">I regret nothing!</title>
    <link href="https://westerndevs.com/career/I-regret-nothing/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/career/I-regret-nothing/</id>
    <published>2016-05-08T17:46:59.000Z</published>
    <updated>2026-03-22T17:42:31.806Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>One of the Western Devs posted <a href="http://thecodist.com/article/my-biggest-regret-as-a-programmer" target="_blank" rel="noopener">an article</a> on our Slack channel on someone's regrets as a programmer. I fundamentally disagree with the sentiment of this article and the remainder of this post will be sixteen paragraphs and three quotes belabouring this point.</p><a id="more"></a><p>In the article, the author describes their <a href="http://www.imdb.com/title/tt0100201/" target="_blank" rel="noopener">Mr. Destiny</a> moment where they wish they had gone into management rather than sticking with being &quot;just&quot; a programmer. Thus perpetuating the myth in our industry that you aren't worth anything unless you <em>Change The World™</em>.</p><p>To be fair, the author is careful not to be prescriptive. It's very much &quot;these are my regrets and thoughts&quot; and not &quot;you should do this in the same situation&quot;. I'm thankful for that and I'll pay the same courtesy. These are my thoughts as they apply to me personally. If you can relate to it in any way, that's not my fault.</p><img src="/images/yes-no.jpg" class="pull-right" width="400"><p>Twenty years ago, I suppose I was at my own crossroads though I didn't really recognize it as such until I thought about it just now. Ever since I was in grade 8, I was going to be an actuary. At the time, I was told &quot;you have to be good at math and you'll make a lot of money&quot;. What else do you need to hear when you're in grade 8?</p><p>So I set about my goal and got a degree, a Bachelor of Commerce I believe. I wasn't really sure what to make of the Faculty of Management, what with their wine and cheeses and people coming to class in suits and &quot;networking&quot; but whatever, I did the work, finished my studies, and started interviewing like you were supposed to.</p><p>At some point in that first six months of interviewing, I clued in to something: programming, which I had been doing for about 10 years by that point as a hobby, was a career choice! Was it a lucrative one? Was it better than being an actuary? Who cares, it was fun and people got paid to do it! So I went back to school. That is, to my ageing memory, the sum total of the thought I put into it. (This will be a recurring theme throughout this post.)</p><p>So I did it. I went back to school, got the degree, and started working. My first job was in the corporate world, an oil and gas company. My next, with a startup where I <a href="http://kyle.baley.org/2008/01/interview-question-tell-me-about-your-mother" target="_blank" rel="noopener">interviewed badly</a> and they hired me anyway. The third was with a consulting company. Since then: contracting, my own startup, and employeehood in some order. Along the way, I've learned, to varying degrees, VB6, classic ASP, .NET, RPG, Livelink, Sharepoint, JavaScript, Ruby, Java, SQL Server, Azure, Google Web Toolkit, Docker, CI, CD, CQRS, CORS, CSS and more acronymed software methodologies and techniques than I care to <s>put thought into remembering</s> admit. I've blogged (clearly), co-written a book, spoken at conferences, and created a user group that was the first of its kind in the country. (It lasted less than a year.)</p><p>While you're free to copy and paste all this into my obituary, there's a reason I list it out. I have never put much thought into my career and I don't have any intrinsic itch I've been meaning to scratch. How did I choose when and where to do all of these things? The opportunities came up and I said yes. Again, not much more thought goes into it than that.</p><p>I have no regrets (including <a href="http://kyle.baley.org/2011/04/brownfield-application-development-one-year-later" target="_blank" rel="noopener">the book</a>). Certainly not on a macro level. Should I have pursued a career as an actuary which, in all likelihood, would have ended up more lucrative financially? Was it a waste of time learning Livelink? Should I have gone into a managerial role?</p><p>Does it matter?</p><p>I am where I am now because of the choices I've made. I've often joked that the main criteria I use to choose my contracts/positions is whether I think it'll be fun. Not whether it was in a hot technology or a dynamic industry. I'm too lazy to figure out what constitutes either of those.</p><p>The net result of those criteria is that I can look back favourably on a career (and I use that term loosely) as &quot;just a programmer&quot;. One that I have no intention of leaving because why would I?</p><p>I am, and I can't emphasize this enough, having the time of my life.</p><p>Now to be fair, I've always had the power to see things through a pair of glasses a supernatural shade of rose. But consider the language from the post I mentioned earlier:</p><blockquote><p>I can only imagine how in demand I could have been.</p><p>My sister has 10X the assets I have.</p></blockquote><p>And my favourite one:</p><blockquote><p>And today I am still just a programmer. Who’s the weenie now?</p></blockquote><p>To me, this speaks of a chronic problem of chasing the wrong thing. What you're <em>supposed</em> to do versus what you <em>want</em> to do. And I've often wondered, if you had taken that other path, would you still have no regrets?</p><p>The world is filled with people who want to leave their mark, some of whom actually will. And I think it's great that these people exist. We need them. For my part, I see no point in feeling guilt or regret doing something I love doing in an industry that is privileged enough to provide me with a good living doing it.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;One of the Western Devs posted &lt;a href=&quot;http://thecodist.com/article/my-biggest-regret-as-a-programmer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;an article&lt;/a&gt; on our Slack channel on someone&#39;s regrets as a programmer. I fundamentally disagree with the sentiment of this article and the remainder of this post will be sixteen paragraphs and three quotes belabouring this point.&lt;/p&gt;
    
    </summary>
    
      <category term="career" scheme="https://westerndevs.com/categories/career/"/>
    
    
  </entry>
  
  <entry>
    <title type="html">Prairie Dev Con 2016 Presentation Materials</title>
    <link href="https://westerndevs.com/conferences/Prairie-Dev-Con-2016-Presentation-Materials/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/conferences/Prairie-Dev-Con-2016-Presentation-Materials/</id>
    <published>2016-04-18T20:14:02.000Z</published>
    <updated>2026-03-22T17:42:31.805Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>If you don't know by now, <a href="http://www.westerndevs.com/conferences/Prairie-Dev-Con-What-you-need-to-know/" target="_blank" rel="noopener">we love Prairie Dev Con</a> at Western Devs. And if you attended last week, hopefully you saw why. It's a high quality conference in a most unlikely setting. Here are the materials from each of my presentations.</p><h3>Death to the Batch Job</h3><iframe src="//www.slideshare.net/slideshow/embed_code/key/yVLz0SMbBhETVc" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/KyleBaley/death-to-the-batch-job" title="Death to the Batch Job" target="_blank">Death to the Batch Job</a> </strong> from <strong><a href="//www.slideshare.net/KyleBaley" target="_blank">Kyle Baley</a></strong> </div><p>In addition to the slides, you can find the source code for the presentation <a href="https://github.com/andreasohlund/DeathToTheBatchJob" target="_blank" rel="noopener">here</a>. There's also a <a href="http://particular.net/blog/death-to-the-batch-job" target="_blank" rel="noopener">blog post</a> that expands a little on the ideas.</p><h3>Docker</h3><iframe src="//www.slideshare.net/slideshow/embed_code/key/6s34SBJRwOt5nz" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/KyleBaley/docker-for-people-who-have-heard-of-docker-but-think-its-just-this-weird-linux-thing-that-doesnt-impact-me" title="Docker For People Who Have Heard of Docker But Think It&#x27;s Just This Weird Linux Thing That Doesn&#x27;t Impact Me" target="_blank">Docker For People Who Have Heard of Docker But Think It&#x27;s Just This Weird Linux Thing That Doesn&#x27;t Impact Me</a> </strong> from <strong><a href="//www.slideshare.net/KyleBaley" target="_blank">Kyle Baley</a></strong> </div><p>The two sample apps I showed during the presentation are:</p><ul><li><a href="http://github.com/stimms/AzureCodeCamp" target="_blank" rel="noopener">AzureCodeCamp</a></li><li><a href="https://github.com/MisterJames/GenFu" target="_blank" rel="noopener">GenFu</a></li></ul><p>For the first, I showed how to connect the app to PostgreSQL which can be running either locally or in Docker. For the second, I showed how to create a container for a .NET Core application running in Linux. If you want to play with this container, you can get it <a href="https://hub.docker.com/r/kbaley/genfu/" target="_blank" rel="noopener">here on Docker Hub</a>.</p><h3>Pre-game Show</h3><p>In addition to the presentations, <a href="http://localhost:4000/bios/donald_belcham/" target="_blank" rel="noopener">Donald</a> and I (but mostly me), with the help of the Winnipeg .NET User Group, hosted a fishbowl discussion and an evening of drinks and food the night before the conference courtesy of Particular Software. During the talk, we covered ways to build a solid team, the importance of company culture, and good ways to vet people. It was a great chat and it was nice to see so many people weigh in with their experience.</p><h3>I'd like to thank...</h3><p>It's hard to overstate the value this &quot;big little conference&quot; has for me. It has all the benefits of a larger conference while still being accessible to both the attendees and speakers. <a href="http://localhost:4000/bios/darcy_lussier/" target="_blank" rel="noopener">D'Arcy Lussier</a> is a force to be reckoned with in the conference circuit.</p><p>Many thanks to the <a href="http://www.winnipegdotnet.org/" target="_blank" rel="noopener">Winnipeg .NET User Group</a> for their help with the fishbowl event.</p><p>Finally, a big thank you to those of you who came to the the conference itself and continue making it great.</p>]]></content>
    
    <summary type="html">
    
      Materials from Death to the Batch Job and Docker presentations
    
    </summary>
    
      <category term="conferences" scheme="https://westerndevs.com/categories/conferences/"/>
    
    
  </entry>
  
  <entry>
    <title type="html">Why can&#39;t you just communicate properly?</title>
    <link href="https://westerndevs.com/communication/Why-can-t-you-just/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/communication/Why-can-t-you-just/</id>
    <published>2016-01-27T21:09:57.000Z</published>
    <updated>2026-03-22T17:42:31.804Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>Online communication bugs me. Actually, <em>bugs</em> isn't accurate. Maybe <em>saddens and fatigues</em>. When volleying with people hiding behind their keyboard shield and protected by three timezones, you have to make a conscious effort to remain optimistic. It's part of the reason I haven't taken to <a href="http://twitter.com/kyle_baley" target="_blank" rel="noopener">Twitter</a> as much as I probably should.</p><img src="https://upload.wikimedia.org/wikipedia/en/7/79/The_Simpsons-Jeff_Albertson.png" class="pull-left"><p>I've talked on this subject <a href="http://kyle.baley.org/2009/05/and-you-opened-your-mouthwhy-or-how-to-comment-for-the-greater-good" target="_blank" rel="noopener">before</a> and it's something I often have in the back of my mind when reading comments. It's come to the forefront recently with some conversations we've had at Western Devs, which led to our most recent <a href="http://www.westerndevs.com/podcasts/Podcast-Is-Web-Development-Terrible/" target="_blank" rel="noopener">podcast</a>. I wasn't able to attend so here I am.</p><p>There are certain phrases you see in comments that automatically seem to devolve a discussion. They include:</p><ul><li>&quot;Why don't you just...&quot;</li><li>&quot;Sorry but...&quot;</li><li>&quot;Can't you just...&quot;</li><li>&quot;It's amazing that...&quot;</li></ul><p>Ultimately, all of these phrases can be summarized as follows:</p><div style="font-size: 18px;margin: 15px;">I'm better than you and here's why...</div><p>In my younger years, I could laugh this off amiably and say &quot;Oh this wacky world we live in&quot;. But I'm turning 44 in a couple of days and it's time to start practicing my crotchety, even if it means complaining about people being crotchety.</p><p>So to that end: I'm asking, nay, <em>begging</em> you to avoid these and similar phrases. This is for your benefit as much as the reader's. These phrases don't make you sound smart. Once you use them, it's very unlikely anyone involved will feel better about themselves, let alone engage in any form of meaningful discussion. Even if you have a valid point, who wants to be talked down to like that? Have you completely forgot what it's like to learn?</p><div class="notice">"For fuck's sake, Mom, why don't you just type the terms you want to search for in the address bar instead of typing WWW.GOOGLE.COM into Bing?"</div><p>Now I know (from experience) it's hard to fight one's innate sense of superiority and the overwhelming desire to make it rain down on the unwashed heathen. So take it in steps. After typing your comment, remove all instances of &quot;just&quot; (except when just means &quot;recently&quot; or &quot;fair&quot;, of course). The same probably goes for &quot;simply&quot;. It has more of a condescending tone than a dismissive one. &quot;Actually&quot; is borderline. Rule of thumb: Don't start a sentence with it.</p><p>Once you have that little nervous tic under control, it's time to remove the negatives. Here's a handy replacement guide to get you started:</p><table><thead><tr><th>Original phrase</th><th>Replacement</th></tr></thead><tbody><tr><td>&quot;Can't you&quot;</td><td>&quot;Can you&quot;</td></tr><tr><td>&quot;Why don't you&quot;</td><td>&quot;Can you&quot;</td></tr><tr><td>&quot;Sorry but&quot;</td><td><em>no replacement; delete the phrase</em></td></tr><tr><td>&quot;It's amazing that...&quot;</td><td><em>delete your entire comment and have a <a href="http://www.gocomics.com/bloomcounty/2008/12/03" target="_blank" rel="noopener">dandelion break</a></em></td></tr></tbody></table><div style="margin-top:15px;"></div><p>See the difference? Instead of saying <em>Sweet Zombie Jayzus, you must be the stupidest person on the planet for doing it this way</em>, you've changed the tone to <em>Have you considered this alternative</em>? In both instances, you've made your superior knowledge known but in the second, it's more likely to get acknowledged. More importantly, you're less likely to look like an idiot when the response is: <em>I did consider that avenue and here are legitimate reasons why I decided to go a different route</em>.</p><p>To be fair, sometimes the author of the work you're commenting on needs to be knocked down a peg or two themselves. I have yet to meet one of these people who respond well to <em>constructive</em> <s>criticism</s> critique, let alone the <em>destructive</em> type I'm talking about here. Generally, I find they feel the need to cultivate an antagonistic personality but in my experience, they usually don't have the black turtlenecks to pull it off. Usually, it ends up backfiring and their dismissive comments become too easy to dismiss over time.</p><p>Kyle the Inclusive</p>]]></content>
    
    <summary type="html">
    
      Simply follow a few rules to improve engagement
    
    </summary>
    
      <category term="communication" scheme="https://westerndevs.com/categories/communication/"/>
    
    
      <category term="communication" scheme="https://westerndevs.com/tags/communication/"/>
    
  </entry>
  
  <entry>
    <title type="html">Migrating from Jekyll to Hexo: Part 2</title>
    <link href="https://westerndevs.com/jekyll/hexo/Migrating-from-Jekyll-to-Hexo-Part-2/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/jekyll/hexo/Migrating-from-Jekyll-to-Hexo-Part-2/</id>
    <published>2015-12-28T00:43:32.000Z</published>
    <updated>2026-03-22T17:42:31.804Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>In my <a href="http://www.westerndevs.com/jekyll/hexo/Migrating-from-Jekyll-to-Hexo/" target="_blank" rel="noopener">previous post</a>, I gave some general impressions from the recent conversion of WesternDevs from Jekyll to Hexo. Here, I'll outline specific issues we tackled during the process of migrating and converting to our own theme.</p><h3>Excerpts</h3><p>Jekyll and Hexo handle excerpts very differently. In Jekyll, you can specify an excerpt in the front matter. If one isn't provided, it will use the first few characters of the post itself as the excerpt.</p><p>In Hexo, the excerpt is set in one and only one way. You need to add <code>&lt;!--more--&gt;</code> somewhere in the post. Everything before that is considered the excerpt. If you don't have that tag in your post, no excerpt.</p><div style="float: right; width: 250px; border: 1px solid #ddd; background-color: #eee; padding: 8px; font-size:12px; margin: 8px;">One minor annoyance with the default handling of the excerpt in Hexo is that it escapes HTML. So if you have a link in your excerpt, it shows as &lt;a href='moo.com'&gt;My web page&lt;/a&gt; in the excerpt. Our current solution is to make sure these posts have an explicit excerpt in the front-matter.</div><p>Luckily, <a href="http://www.westerndevs.com/bios/amir_barylko/" target="_blank" rel="noopener">Amir</a> had some foresight and added code to our Jekyll site to support <code>&lt;!--more--&gt;</code>. Unluckily, we rarely used it. Some of our posts had excerpts, particularly the podcasts. But mostly, we relied on Jekyll to parse the post for the excerpt.</p><p>In the end, we did two things to remedy this. First we added the <a href="https://github.com/lalunamel/hexo-front-matter-excerpt" target="_blank" rel="noopener">https://github.com/lalunamel/hexo-front-matter-excerpt</a> package. This fixed all the posts that already had an <code>excerpt</code> in the front matter. For the rest, we went through all the remaining posts and added <code>&lt;!--more--&gt;</code> after the first paragraph. Ever the optimists, we also submitted a <a href="https://github.com/lalunamel/hexo-front-matter-excerpt/pull/2" target="_blank" rel="noopener">pull request</a> for the package to make it behave more like Jekyll.</p><h3>Syntax highlighting</h3><p>A code block in Jekyll looks like this</p><figure class="highlight django"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="template-tag">&#123;% <span class="name">highlight</span> html%&#125;</span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>moo<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="template-tag">&#123;% <span class="name">endhighlight</span> %&#125;</span></span><br></pre></td></tr></table></figure><p>And in Hexo, like this:</p><figure class="highlight django"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="template-tag">&#123;% <span class="name">codeblock</span> lang:html %&#125;</span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>moo<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="template-tag">&#123;% <span class="name">endcodeblock</span> %&#125;</span></span><br></pre></td></tr></table></figure><p>A series of &quot;Find and replace in files&quot; actions took care of this. We also found a <code>syntax.styl</code> file to our liking. Not sure if Jekyll has built-in support for syntax highlighting. I suspect not and that it was automatically included with the <a href="https://github.com/mmistakes/minimal-mistakes" target="_blank" rel="noopener">theme we chose for Jekyll</a>.</p><h3>CSS styles</h3><p>Jekyll uses Kramdown for parsing Markdown. One of the features it adds is the ability to specify a class on your elements like so.</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&#123;: .pull-right &#125;</span><br><span class="line">![<span class="string">My image</span>](<span class="link">http://my/image/reference</span>)</span><br></pre></td></tr></table></figure><p>This will add the <code>pull-right</code> CSS class to the image and, as the name suggests, this will float the image on the right.</p><p>As far as I know, there's no Kramdown support in NodeJS, likely because Kramdown is a Ruby gem. So we had to modify each instance of this syntax manually. For images, we converted to the Hexo <a href="https://hexo.io/docs/tag-plugins.html" target="_blank" rel="noopener">img tag plugin</a> like so:</p><figure class="highlight clojure"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;% img pull-right <span class="string">"http://my/image/reference"</span> <span class="string">"My image"</span>&#125;</span><br></pre></td></tr></table></figure><p>There are a few tag plugins in Hexo all ported from Octopress. Which likely means we could have used them in our Jekyll site but not much point in testing that out now...</p><p>For other instances of this kramdown-specific syntax that were applied to other elements, we just dropped down to native HTML in the Markdown:</p><figure class="highlight angelscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;div <span class="keyword">class</span>="<span class="symbol">notice</span>"&gt;<span class="symbol">This</span> <span class="symbol">is</span> <span class="symbol">a</span> <span class="symbol">notice</span>&lt;/<span class="symbol">div</span>&gt;</span><br></pre></td></tr></table></figure><h3>Aliases</h3><p>We changed the URL structure for our podcasts early on and to keep any links pointing to the existing ones valid, we used <a href="https://github.com/jekyll/jekyll-redirect-from" target="_blank" rel="noopener">the jekyll-redirect-from gem</a> to maintain the old link. We find a <a href="https://github.com/jekyll/jekyll-redirect-from" target="_blank" rel="noopener">suitable replacement</a> for it in Hexo. BUT that suitable replacement's official package on NPM doesn't support Hexo 3 at the time of writing so we had to reference it in our <code>package.json</code> file like so:</p><figure class="highlight 1c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"hexo-generator-alias"</span>: <span class="string">"git+https://github.com/hexojs/hexo-generator-alias.git"</span></span><br></pre></td></tr></table></figure><p>Bear in mind that this is a static site. So both of these plugins, the Jekyll and the Hexo one, handle redirects by generating an HTML page at the alias location with a redirect embedded in it:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Redirecting...<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"canonical"</span> <span class="attr">href</span>=<span class="string">"/podcasts/podcast-the-internet-of-things/"</span>&gt;</span><span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"refresh"</span> <span class="attr">content</span>=<span class="string">"0; url=/podcasts/podcast-the-internet-of-things/"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p>This isn't quite ideal for SEO purposes but it is the recommended approach if you can't do a server-side redirect.</p><h3>Empty categories</h3><p>This issue was probably the source of most of our trouble. Our permalink URL is of the form <code>/:category/:title</code>. For example: <code>/docker/yet-another-docker-intro/</code>. The issue is that the vast majority of our posts are uncategorized. And Jekyll and Hexo each handle uncategorized posts very differently in their permalinks.</p><p>In the front matter for Hexo, you can define a default category which all posts will use if a category is not assigned. So if you set the default category to &quot;moo&quot;, your permalink URL will be: <code>/moo/a-discussion-on-knockout/</code>.</p><p>In Jekyll, an uncategorized post appears at the root of the site. Like this: <code>http://www.westerndevs.com/Migrating-from-Jekyll-to-Hexo-Part-2</code>. But setting the default category to an empty string or to <code>/</code>, led to fully-qualified URLs that look like this: <code>//Migrating-from-Jekyll-to-Hexo-Part-2</code>. I believe this is a bug in the permalink generator. It's generating the permalink as /category/title and when there is no category, it's not going to treat it any differently.</p><p>Our initial solution to this was to stick with a default category of <code>uncategorized</code> but to alias each post to the root so that existing URLs would still work but would redirect to the new one. Alas, we have Disqus comments to deal with and those are tied to a specific URL. We could have migrated them but we also would have had to contend with sucky looking URLs that have <code>uncategorized</code> in them.</p><p>Our current solution: <a href="https://github.com/westerndevs/hexo" target="_blank" rel="noopener">fork Hexo</a>. In our fork, we add some handling when generating the URL for a post so that if it starts with a //, we trim one of them. Not the most elegant solution but workable for now. And if that's not hacky enough for you, check out the solution for...</p><h3>Permalinks/Disqus Comments</h3><p>After deployment, we discovered none of the existing Disqus comments were showing on our posts. The Disqus script was working because you could add new comments and it would show &quot;Also on Western Devs&quot; comments. Just no existing comments.</p><p>The culprit was, again, the permalink. Because of the leading //, Disqus thought the URL for our posts was (as an example): http://www.westerndevs.com//a-discussion-on-knockout. That double slash was enough to confuse it into not showing comments.</p><p>Our interim solution is to include this in our post template:</p><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> disqus_url = <span class="string">'&lt;%= page.permalink %&gt;'</span>.replace(<span class="string">".com//"</span>, <span class="string">".com/"</span>);</span><br></pre></td></tr></table></figure><p>That right there is some quality coding...</p><h3>Feed/Sitemap/iTunes</h3><p>This issue is more of a warning not to over-think things. Our feed is served up at <a href="http://www.westerndevs.com/feed" target="_blank" rel="noopener">http://www.westerndevs.com/feed</a>. In Jekyll, we created a <code>feed.xml</code> file and assumed that it did some magic to generate a <code>feed</code> file. And we had a hell of a time trying to get Hexo to generate that same file without the .xml extension. Even to the point where we created our own package that still didn't quite work.</p><p>Then someone realized that Jekyll was not actually generating a <code>feed</code> file, just a <code>feed.xml</code> file. Whatever GitHub Pages runs on allows you to access XML files without the extension. Jekyll's development server knows this. Hexo's doesn't. So when we run locally, we can't access our various XML files (for our RSS feed, our iTunes feed, and our sitemap) without specifying the extension. But on the deployed site, they work fine.</p><h3>Links in Headers</h3><p>There was a bug in the <a href="https://github.com/hexojs/hexo-renderer-marked" target="_blank" rel="noopener">default Markdown renderer</a> that caused rendering issues when you had links within headers. The bug has since been fixed but I'm including it here mostly to reiterate that you aren't locked into anything with Hexo. While we were converting, the bug was still active and all we did to fix it was to switch to <a href="https://github.com/celsomiranda/hexo-renderer-markdown-it" target="_blank" rel="noopener">a different renderer</a>.</p><p>One philosophical point: One of the reasons for this bug is that the default renderer will automatically add bookmarks for every heading. This is a <a href="https://github.com/hexojs/hexo-renderer-marked/issues/16" target="_blank" rel="noopener">design decision</a> that I'm not convinced has a lot of value though it's not a position I'll defend strongly. The renderer we've switched to includes this as an option you have to explicitly turn on which I think is a better way to go.</p><h3>Deployment</h3><p>Since GitHub Pages runs Jekyll, it's understandable that deployment with Jekyll is relatively easy. We did need to wrap it in a Rake task to accommodate working locally with a different <code>_config.yml</code> file than what is used in production but that wasn't hard to do.</p><p>For Hexo, our process is still mostly manual and has to be done locally rather than on our CI server (Travis). We generate the site, copy the static files to a folder, then check those files into the <code>gh-pages</code> branch. I imagine it <a href="https://sazzer.github.io/blog/2015/05/04/Deploying-Hexo-to-Github-Pages-with-Travis/" target="_blank" rel="noopener">won't be hard</a> to get our Travis process adapted to this but at present, we haven't got around to it yet. Just keep in mind, it won't be quite as easy as Jekyll.</p><h3>So which one is &quot;better&quot;?</h3><p>I forgot to answer this in the last post. As I mentioned, we're enjoying Hexo and it's nice having everyone excited about blogging again. For the Western Devs as a group, Hexo is the better choice.</p><p>But personally, I like Jekyll a little better. It feels more polished and doesn't require you to fork the product to get what you want. Typing this up, I was looking for a way to disable line numbers for individual codeblocks but I've come up short. Plus there's a larger and more comprehensive community behind it. That said, I am <em>really</em> enjoying the quick generation time in Hexo.</p><hr><p>I believe that covers the major gotchas we encountered in our conversion. It glosses over a few things, like whether to stick with Stylus as the default CSS pre-processor or move to SASS. Or whether to use <a href="http://jade-lang.com/" target="_blank" rel="noopener">Jade</a> as the templating language rather than the default, EJS. Those questions are quite a bit more subjective and I wanted to keep this discussion limited to the technical hurdles we encountered.</p><p>But if you run into an issue not mentioned here, add a comment and I will update the post.</p>]]></content>
    
    <summary type="html">
    
      Specific issues we ran into during the migration from Jekyll to Hexo
    
    </summary>
    
      <category term="jekyll" scheme="https://westerndevs.com/categories/jekyll/"/>
    
      <category term="hexo" scheme="https://westerndevs.com/categories/jekyll/hexo/"/>
    
    
  </entry>
  
  <entry>
    <title type="html">Migrating from Jekyll to Hexo</title>
    <link href="https://westerndevs.com/jekyll/hexo/Migrating-from-Jekyll-to-Hexo/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/jekyll/hexo/Migrating-from-Jekyll-to-Hexo/</id>
    <published>2015-12-22T19:00:42.000Z</published>
    <updated>2026-03-22T17:42:31.803Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>WesternDevs has a shiny new look thanks to graphic designer extraodinaire, <a href="http://www.karenchudobiak.ca/" target="_blank" rel="noopener">Karen Chudobiak</a>. When implementing the design, we also decided to switch from Jekyll to Hexo. Besides having the opportunity to learn NodeJS, the other main reason was Windows. Most of us use it as our primary machine and Jekyll doesn't officially support it. There are <a href="http://jekyll-windows.juthilo.com/" target="_blank" rel="noopener">instructions</a> available by people who were obviously more successful at it than we were. And there are even <a href="https://davidburela.wordpress.com/2015/11/28/easily-install-jekyll-on-windows-with-3-command-prompt-entries-and-chocolatey/" target="_blank" rel="noopener">simpler ones</a> that I discovered during the course of writing this post and that I wish existed three months ago.</p><img src="https://avatars2.githubusercontent.com/u/6375567?v=3&s=200" class="pull-right"><p>Regardless, here we are and it's already been a positive move overall, not least because the move to Node means more of us are available to help with the maintenance of the site. But it wasn't without it's challenges. So I'm going to outline the major ones we faced here in the hopes that it will help you make your decision more informed than ours was.</p><p>To preface this, note that I'm new to Node and in fact, this is my first real project with it. That said, I'm no expert in Ruby either, which is what Jekyll is written in. And the short version of my first impressions is: Jekyll feels more like a real product but I had an easier time customizing Hexo once I dug into it. Here's the longer version</p><h3>Documentation/Resources</h3><p>You'll run into this very quickly. Documentation for Hexo is decent but incomplete. And once you start Googling, you'll discover many of the resources are in Chinese. I found very quickly that there is <code>posts</code> collection and that each post has a <code>categories</code> collection. But as to what these objects look like, I couldn't tell. They aren't arrays. And you can't <code>JSON.stringify</code> them because they have circular references in them. <code>util.inspect</code> works but it's not available everywhere.</p><h3>Multi-author support</h3><p>By default, Hexo doesn't support multiple authors. Neither does Jekyll, mind you, but we found a <a href="https://github.com/mmistakes/minimal-mistakes" target="_blank" rel="noopener">pretty complete</a> theme that does. In Hexo, there's a <a href="https://www.npmjs.com/package/hexo-multiauthor" target="_blank" rel="noopener">decent package</a> that gets you partway there. It lets you specify an author ID on a post and it will attach a bunch of information to it. But you can't, for example, get a full list of authors to list on a Who We Are page. So we created a separate data file for the authors. But we also haven't figured out how to use that file to generate a .json file to use for the Featured section on the home page. So at the moment, we have author information in three places. Our temporary solution is to disallow anyone from joining or leaving Western Devs.</p><h3>Customization</h3><p>If you go with Hexo and choose an <a href="https://hexo.io/themes/" target="_blank" rel="noopener">existing themes</a>, you won't run into the same issues we did. Out of the box, it has good support for posts, categories, pagination, even things like tags and aliases with the <a href="https://hexo.io/plugins/" target="_blank" rel="noopener">right plugins</a>.</p><p>But we started from a design <em>and</em> were migrating from an existing site with existing URLs and had to make it work. I've mentioned the challenge of multiple authors already. Another one: maintaining our URLs. Most of our posts aren't categorized. In Jekyll, that means they show up at the root of the site. In Hexo, that's not possible. At least at the moment and I suspect this is a bug. We eventually had to fork Hexo itself to maintain our existing URLs.</p><p>Another challenge: excerpts. In Jekyll, excerpts work like this: Check the front matter for an <code>excerpt</code>. If one doesn't exist, take the first few characters from the post. In Hexo, excerpts are empty by default. If you add a <code>&lt;!--more--&gt;</code> tag in your post, everything before that is considered an excerpt. If you specify an <code>excerpt</code> in your front matter, it's ignored because there is already an <code>excerpt</code> property on your posts.</p><p>Luckily, there's a <a href="https://github.com/lalunamel/hexo-front-matter-excerpt" target="_blank" rel="noopener">plugin</a> to address the last point. But it still didn't address the issue of all our posts without an excerpt where we relied solely on the contents of the post.</p><p>So if you're looking to veer from the scripted path, be prepared. More on this later in the &quot;good parts&quot; section.</p><h3>Overall feeling of rawness</h3><p>This is more a culmination of the previous issues. It just feels like Hexo is a work-in-progress whereas Jekyll feels more like a finished product. There's a strong community behind Jekyll and plenty of help. Hexo still has bugs that suggest it's just not used much in the wild. Like <a href="https://github.com/hexojs/hexo-renderer-marked/issues/16" target="_blank" rel="noopener">rendering headers with links in them</a>. It makes the learning process a bit challenging because with Jekyll, if something didn't work, I'd think <em>I'm obviously doing something wrong</em>. With Hexo, it's <em>I might be doing something wrong or there might be a bug</em>.</p><hr><h3>The good parts</h3><p>I said earlier that the move to Hexo was positive overall and not just because I'm optimistic by nature. There are two key benefits we've gained just in the last two weeks.</p><h4>Generation time</h4><p>Hexo is fast, plain and simple. Our old Jekyll site took six seconds to generate. Doesn't sound like much but when you're working on a feature or tweaking a post, then saving, then refreshing, then rinsing, then repeating, that six seconds adds up fast. In Hexo, a full site generation takes three seconds. But more importantly, it is smart enough to do incremental updates while you're working on it. So if you run <code>hexo server</code>, then see a mistake in your post, you can save it, and the change will be reflected almost instantly. In fact, it's usually done by the time I've switched back to the browser.</p><h4>Contributors</h4><p>We had logistical challenges with Jekyll. To the point where we had two methods for Windows users that wanted to contribute (i.e. add a post). One involved a <a href="http://www.westerndevs.com/docker-and-western-devs/" target="_blank" rel="noopener">Docker image</a> and the other <a href="http://www.westerndevs.com/using-azure-arm-to-deploy-a-docker-container/" target="_blank" rel="noopener">Azure ARM</a>. Neither was ideal as they took between seconds and minutes to refresh if you made changes. Granted, both methods furthered our collective knowledge in both Docker and Azure but they both kinda sucked for productivity.</p><p>That meant that realistically, only the Mac users really contributed to the maintenance of the site. And our Docker/Azure ARM processes were largely ignored as we would generally just test in production. I.e. create a post, check it in, wait for the site to deploy, make necessary changes, etc, etc.</p><p>With the switch to Hexo, we've had no fewer than five contributors to the site's maintenance already. Hexo just works on Windows. And on Mac. Best of both worlds.</p><h4>Customization</h4><p>This is listed under the challenges but ever the optimist, I'm including it here as well. We've had to make some customizations for our site, including <a href="https://github.com/westerndevs/hexo" target="_blank" rel="noopener">forking Hexo</a> itself. And for me personally, once I got past the <em>why isn't this working the way I want?</em> stage, it's been a ton of fun. It's crazy simple to muck around in the node modules to try stuff out. And just as simple to fork something and reference it in your project when the need arises. I mentioned an earlier issue rendering links in headers. No problem, we just swapped out the markdown renderer for <a href="https://github.com/celsomiranda/hexo-renderer-markdown-it" target="_blank" rel="noopener">another one</a>. And if that doesn't work, we'll tweak something until it does.</p><hr><p>I want to talk more on specific conversion issues we ran into as a guide to those following in our footsteps. But there are enough of them to warrant a follow up post without all this pre-amble. For now, we're all feeling the love for Hexo. So much so that no less than three other Western Devs are in the process of converting their personal blogs to it.</p>]]></content>
    
    <summary type="html">
    
      The Western Devs website has a sporty new look and a shiny new technology behind it. In this post, we&#39;ll look at the good and bad with migrating from Jekyll to Hexo
    
    </summary>
    
      <category term="jekyll" scheme="https://westerndevs.com/categories/jekyll/"/>
    
      <category term="hexo" scheme="https://westerndevs.com/categories/jekyll/hexo/"/>
    
    
  </entry>
  
  <entry>
    <title type="html">Testing With Data</title>
    <link href="https://westerndevs.com/_/testing-with-data/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/_/testing-with-data/</id>
    <published>2015-11-20T00:02:10.000Z</published>
    <updated>2026-03-22T17:42:31.828Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>It's not a coincidence that this is coming off the heels of Dave Paquette's <a href="http://www.westerndevs.com/realistic-sample-data-with-genfu/" target="_blank" rel="noopener">post on GenFu</a> and Simon Timms' <a href="http://www.westerndevs.com/source-control-for-sql-databases/" target="_blank" rel="noopener">post on source control for databases</a> in the same way it was probably not a coincidence that Hollywood released three body-swapping movies in the 1987-1988 period (four if you include Big).</p><a id="more"></a><p>I was asked recently for some advice on generating data for use with integration and UI tests. I already have some ideas but asked the rest of the Western Devs for some elucidation. My tl;dr version is the same as what I mentioned in our <a href="http://www.westerndevs.com/on-ui-testing/" target="_blank" rel="noopener">discussion on UI testing</a>: it's hard. But manageable. Probably.</p><p>The solution needs to balance a few factors:</p><ul><li>Each test must start from a predictable state</li><li>Creating that predictable state should be fast as possible</li><li>Developers should be able to figure out what is going on by reading the test</li></ul><p>The two options we discussed both assume the first factor to be immutable. That means you either clean up after yourself when the test is finished or you wipe out the database and start from scratch with each test. Cleaning up after yourself might be faster but has more moving parts. Cleaning up might mean different things depending on which step you're in if the test fails.</p><p>So given that we will likely re-create the database from scratch before each and every test, there are two options. My <em>current</em> favourite solution is a hybrid of the two.</p><h3>Maintain a database of known data</h3><p>In this option, you have a pre-configured database. Maybe it's a SQL Server .bak file that you restore before each test. Maybe it's a <code>GenerateDatabase</code> method that you execute. I've done the latter on a Google App Engine project, and it works reasonably well from an implementation perspective. We had a class for each domain aggregate and used dependency injection. So adding a new test customer to accommodate a new scenario was fairly simple. There are a number of other ways you can do it, some of which Simon touched on in his post.</p><p>We also had it set up so that we could create only the customer we needed for that particular test if we needed to. That way, we could use a step like <code>Given I'm logged into 'Christmas Town'</code> and it would set up only that data.</p><p>There are some drawbacks to this approach. You still need to create a new class for a new customer if you need to do something out of the ordinary. And if you need to do something only <em>slightly</em> out of the ordinary, there's a strong tendency to use an existing customer and tweak its data ever so slightly to fit your test's needs, other tests be damned. With these tests falling firmly in the <em>long-running</em> category, you don't always find out the effects of this until much later.</p><p>Another drawback: it's not obvious in the test exactly what data you need for that specific test. You can accommodate this somewhat just with a naming convention. For example, <code>Given I'm logged into a company from India</code>, if you're testing how the app works with rupees. But that's not always practical. Which leads us to the second option.</p><h3>Create an API to set up the data the way you want</h3><p>Here, your API contains steps to fully configure your database exactly the way you want. For example:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Given I have a company named &quot;Christmas Town&quot; owned by &quot;Jack Skellington&quot;</span><br><span class="line">And I have 5 product categories</span><br><span class="line">And I have 30 products</span><br><span class="line">And I have a customer</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>You can probably see the major drawback already. This can become <em>very</em> verbose. But on the other hand, you have the advantage of seeing exactly what data is included which is helpful when debugging. If your test data is wrong, you don't need to go mucking about in your source code to fix it. Just update the test and you're done.</p><p>Also note the lack of specifics in the steps. Whenever possible, I like to be very vague when setting up my test data. If you have a <a href="https://github.com/MisterJames/GenFu" target="_blank" rel="noopener">good framework for generating test data</a>, this isn't hard to do. And it helps uncover issues you may not account for using hard-coded data (as anyone named D'Arcy O'Toole can probably tell you).</p><hr><p>Loading up your data with a granular API isn't realistic which is why I like the hybrid solution. By default, you pre-load your database with <em>some</em> common data, like lookup tables with lists of countries, currencies, product categories, etc. Stuff that needs to be in place for the majority of your tests.</p><p>After that, your API doesn't need to be that granular. You can use something like <code>Given I have a basic company</code> which will create the company, add an owner and maybe some products and use that to test the process for creating an order. Under the hood, it will probably use the specific steps.</p><p>One reason I like this approach: it hides only the details you don't care about. When you say <code>Given I have a basic company and I change the name to &quot;Rick's Place&quot;</code>, that tells me, &quot;I don't care how the company is set up but the company name is important&quot;. Very useful to help narrow the focus of the test when you're reading it.</p><p>This approach will understandably lead to a whole bunch of different methods for creating data of various sizes and coarseness. And for that you'll need to...</p><h3>Maintain test data</h3><p>Regardless of your method, maintaining your test data will require constant vigilance. In my experience, there is a tremendous urge to take shortcuts when it comes to test data. You'll re-use a test company that doesn't quite fit your scenario. You'll alter your test to fit the data rather than the other way around. You'll duplicate a data setup step because your API isn't discoverable.</p><p>Make no mistake, maintaining test data is work. It should be treated with the same respect and care as the rest of your code. Possibly more so since the underlying code (in whatever form it takes) technically won't be tested. Shortcuts and bad practices should <em>not</em> be tolerated and let go because &quot;it's just test data&quot;. Fight the urge to let things slide. Call it out as soon as you see it. Refactor mercilessly once you see opportunities to do so.</p><p>Don't be afraid to flip over a table or two to get your point across.</p><p>-- Kyle the Unmaintainable</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;It&#39;s not a coincidence that this is coming off the heels of Dave Paquette&#39;s &lt;a href=&quot;http://www.westerndevs.com/realistic-sample-data-with-genfu/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;post on GenFu&lt;/a&gt; and Simon Timms&#39; &lt;a href=&quot;http://www.westerndevs.com/source-control-for-sql-databases/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;post on source control for databases&lt;/a&gt; in the same way it was probably not a coincidence that Hollywood released three body-swapping movies in the 1987-1988 period (four if you include Big).&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title type="html">Running a .NET app against a Postgres database in Docker</title>
    <link href="https://westerndevs.com/_/running-a-net-app-against-postgres-database-in-docker/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/_/running-a-net-app-against-postgres-database-in-docker/</id>
    <published>2015-10-25T21:24:14.000Z</published>
    <updated>2026-03-22T17:42:31.827Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>Some days/weeks/time ago, I did a presentation at MeasureUP called &quot;Docker For People Who Think Docker Is This Weird Linux Thing That Doesn't Impact Me&quot;. The slides for that presentation can be found <a href="http://www.slideshare.net/KyleBaley/docker-for-people-who-have-heard-of-docker-but-think-its-just-this-weird-linux-thing-that-doesnt-impact-me" target="_blank" rel="noopener">here</a> and the sample application <a href="https://github.com/stimms/AzureCodeCamp" target="_blank" rel="noopener">here</a>.</p><a id="more"></a><h3>Using the sample app with PostgreSQL</h3><p>The sample application is just a plain ol' .NET application. It is meant to showcase different ways of doing things. One of those things is data access. You can configure the app to access the data from SQL storage, Azure table storage, or in-memory. By default, it uses the in-memory option so you can clone the app and launch it immediately just to see how it works.</p><p><img src="http://i.imgur.com/xeKON0u.png" alt="PancakeProwler"></p><p>Quick summary: Calgary, Alberta hosts an annual event called the <a href="http://www.calgarystampede.com/" target="_blank" rel="noopener">Calgary Stampede</a>. One of the highlights of the 10-ish day event is the pancake breakfast, whereby dozens/hundreds of businesses offer up pancakes to people who want to eat like the pioneers did, assuming the pioneers had pancake grills the size of an Olympic swimming pool.</p><p>The sample app gives you a way to enter these pancake breakfast events and each day, will show that day's breakfasts on a map. There's also a recipe section to share pancake recipes but we won't be using that here.</p><p>To work with Docker we need to set the app up to use a data access mechanism that will work on Docker. The sample app supports Postgres so that will be our database of choice. Our first step is to get the app up and running locally with Postgres <em>without</em> Docker. So, assuming you have Postgres installed, find the <code>ContainerBuilder.cs</code> file in the <code>PancakeProwler.Web</code> project. In this file, comment out the following near the top of the file:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Uncomment for InMemory Storage</span></span><br><span class="line">builder.RegisterAssemblyTypes(<span class="keyword">typeof</span>(Data.InMemory.Repositories.RecipeRepository).Assembly)</span><br><span class="line">       .AsImplementedInterfaces()</span><br><span class="line">       .SingleInstance();</span><br></pre></td></tr></table></figure><p>And uncomment the following later on:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Uncomment for PostgreSQL storage</span></span><br><span class="line">builder.RegisterAssemblyTypes(<span class="keyword">typeof</span>(PancakeProwler.Data.Postgres.IPostgresRepository).Assembly)</span><br><span class="line">    .AsImplementedInterfaces().InstancePerRequest().PropertiesAutowired();</span><br></pre></td></tr></table></figure><p>This configures the application to use Postgres. You'll also need to do a couple of more tasks:</p><ul><li>Create a user in Postgres</li><li>Create a Pancakes database in Postgres</li><li>Update the <code>Postgres</code> connection string in the web project's <code>web.config</code> to match the username and database you created</li></ul><p>The first two steps can be accomplished with the following script in Postgres:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">DATABASE</span> <span class="string">"Pancakes"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">USER</span> <span class="string">"Matt"</span> <span class="keyword">WITH</span> <span class="keyword">PASSWORD</span> <span class="string">'moo'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">ALL</span> <span class="keyword">PRIVILEGES</span> <span class="keyword">ON</span> <span class="keyword">DATABASE</span> <span class="string">"Pancakes"</span> <span class="keyword">TO</span> <span class="string">"Matt"</span>;</span><br></pre></td></tr></table></figure><p>Save this to a file. Change the username/password if you like but be aware that the sample app has these values hard-wired into the connection string. Then execute the following from the command line:</p><pre><code>psql -U postgres -a -f &quot;C:\path\to\sqlfile.sql&quot;</code></pre><p>At this point, you can launch the application and create events that will show up on the map. If you changed the username and/or password, you'll need to update the Postgres connection string first.</p><p>You might have noticed that you didn't create any tables yet but the app still works. The sample is helpful in this regard because all you need is a database. If the tables aren't there yet, they will be created the first time you launch the app.</p><blockquote><p>Note: recipes rely on having a search provider configured. We won't cover that here but I hope to come back to it in the future.</p></blockquote><p>Next, we'll switch things up so you can run this against Postgres running in a Docker container.</p><h3>Switching to Docker</h3><p>I'm going to give away the ending here and say that there is no magic. Literally, all we're doing in this section is installing Postgres on another &quot;machine&quot; and connecting to it. The commands to execute this are just a little less click-y and more type-y.</p><p>The first step, of course, is installing Docker. At the time of writing, this means installing <a href="http://docs.docker.com/windows/started/" target="_blank" rel="noopener">Docker Machine</a>.</p><p>With Docker Machine installed, launch the Docker Quickstart Terminal and wait until you see an ASCII whale:</p><p><img src="http://i.imgur.com/UOgoWfK.png" alt="Docker Machine"></p><p>If this is your first time running Docker, just know that a lightweight Linux virtual machine has been launched in VirtualBox on your machine. Check your Start screen and you'll see VirtualBox if you want to investigate it but the <code>docker-machine</code> command will let you interact with it for many things. For example:</p><pre><code>docker-machine ip default</code></pre><p>This will give you the IP address of the default virtual machine, which is the one created when you first launched the Docker terminal. Make a note of this IP address and update the Postgres connection string in your web.config to point to it. You can leave the username and password the same:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">add</span> <span class="attr">name</span>=<span class="string">"Postgres"</span> <span class="attr">connectionString</span>=<span class="string">"server=192.168.99.100;user id=Matt;password=moo;database=Pancakes"</span> <span class="attr">providerName</span>=<span class="string">"Npgsql"</span> /&gt;</span></span><br></pre></td></tr></table></figure><p>Now we're ready to launch the container:</p><pre><code>docker run --name my-postgres -e POSTGRES_PASSWORD=moo -p 5432:5432 -d postgres`</code></pre><p>Breaking this down:</p><style>    .docker-table+table td {        padding: 8px;           border: 1px solid #ccc;    }    .docker-table+table td:nth-child(1) {        width:220px;    }    .docker-table+table tr:nth-child(even) td {        background-color: #eee;    }</style><div class="docker-table"></div><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><code>docker run</code></td><td>Runs a docker container from an image</td></tr><tr><td><code>--name my-postgres</code></td><td>The name we give the container to make it easier for us to work with. If you leave this off, Docker will assign a relatively easy-to-remember name like &quot;floral-academy&quot; or &quot;crazy-einstein&quot;. You also get a less easy-to-remember identifier which works just as well but is...less...easy-to-remember</td></tr><tr><td><code>-e POSTGRES_PASSWORD=moo</code></td><td>The <code>-e</code> flag passes an environment variable to the container. In this case, we're setting the password of the default postgres user</td></tr><tr><td><code>-p 5432:5432</code></td><td>Publishes a port from the container to the host. Postgres runs on port 5432 by default so we publish this port so we can interact with Postgres directly from the host</td></tr><tr><td><code>-d</code></td><td>Run the container in the background. Without this, the command will sit there waiting for you to kill it manually</td></tr><tr><td><code>postgres</code></td><td>The name of the image you are creating the container from. We're using the <a href="https://hub.docker.com/_/postgres/" target="_blank" rel="noopener">official postgres image</a> from Docker Hub.</td></tr></tbody></table><p>If this is the first time you've launched Postgres in Docker, it will take a few seconds at least, possibly even a few minutes. It's downloading the Postgres image from Docker Hub and storing it locally. This happens only the first time for a particular image. Every subsequent postgres container you create will use this local image.</p><p>Now we have a Postgres container running. Just like with the local version, we need to create a user and a database. We can use the same script as above and a similar command:</p><pre><code>psql -h 192.168.99.100 -U postgres -a -f &quot;C:\path\to\sqlfile.sql&quot;</code></pre><p>The only difference is the addition of <code>-h 192.168.99.100</code>. You should use whatever IP address you got above from the <code>docker-machine ip default</code> command here. For me, the IP address was 192.168.99.100.</p><p>With the database and user created, and your web.config updated, we'll need to stop the application in Visual Studio and re-run it. The reason for this is that the application won't recognize that we've changed database so we need to &quot;reboot&quot; it to trigger the process for creating the initial table structure.</p><p>Once the application has been restarted, you can now create pancake breakfast events and they will be stored in your Docker container rather than locally. You can even launch pgAdmin (the Postgres admin tool) and connect to the database in your Docker container and work with it like you would any other remote database.</p><h3>Next steps</h3><p>From here, where you go is up to you. The sample application can be configured to use <a href="https://www.elastic.co/" target="_blank" rel="noopener">Elastic Search</a> for the recipes. You could start an Elastic Search container and configure the app to search against that container. The principle is the same as with Postgres. Make sure you open both ports 9200 and 9300 and update the <code>ElasticSearchBaseUri</code> entry in <code>web.config</code>. The command I used in the presentation was:</p><pre><code>docker run --name elastic -p 9200:9200 -p 9300:9300 -d elasticsearch</code></pre><p>I also highly recommend Nigel Poulton's <a href="http://www.pluralsight.com/courses/docker-deep-dive" target="_blank" rel="noopener">Docker Deep Dive</a> course on Pluralsight. You'll need access to Linux either natively or in a VM but it's a great course.</p><p>There are also a number of posts right here on Western Devs, including an <a href="http://www.westerndevs.com/docker/yet-another-docker-intro/" target="_blank" rel="noopener">intro to Docker for OSX</a>, tips on <a href="http://www.westerndevs.com/getting-docker-running-on-windows-10/" target="_blank" rel="noopener">running Docker on Windows 10</a>, and a summary or two on a discussion <a href="http://www.westerndevs.com/westerndevs-learn-about-docker-part-2/" target="_blank" rel="noopener">we had on it internally</a>.</p><p>Other than that, Docker is great for experimentation. Postgres and Elastic Search are both available pre-configured in Docker on Azure. If you have access to Azure, you could spin up a Linux VM with either of them and try to use that with your application. Or look into Docker Compose and try to create a container with both.</p><p>For my part, I'm hoping to convert the sample application to ASP.NET 5 and see if I can get it running in a Windows Server Container. I've been saying that for a couple of months but I'm putting it on the internet in an effort to make it true.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Some days/weeks/time ago, I did a presentation at MeasureUP called &amp;quot;Docker For People Who Think Docker Is This Weird Linux Thing That Doesn&#39;t Impact Me&amp;quot;. The slides for that presentation can be found &lt;a href=&quot;http://www.slideshare.net/KyleBaley/docker-for-people-who-have-heard-of-docker-but-think-its-just-this-weird-linux-thing-that-doesnt-impact-me&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; and the sample application &lt;a href=&quot;https://github.com/stimms/AzureCodeCamp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title type="html">Windows Server Containers Are Coming Whether You Like It or Not</title>
    <link href="https://westerndevs.com/_/windows-server-containers-are-coming-whether-you-like-it-or-not/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/_/windows-server-containers-are-coming-whether-you-like-it-or-not/</id>
    <published>2015-08-31T19:01:20.000Z</published>
    <updated>2026-03-22T17:42:31.830Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p><strong>UPDATE: April 27, 2017</strong> Much of the information in this post is out-of-date and the links have been removed since they no longer exist.  For the latest on the state of containers on Windows, check out the <a href="https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/" target="_blank" rel="noopener">documentation</a>.</p><a id="more"></a><p>After posting giddily on <a href="http://www.westerndevs.com/docker-is-coming-whether-you-like-it-or-not/" target="_blank" rel="noopener">Docker in the Windows world</a> recently, Microsoft released Windows Server 2016 Technical Preview 3 with container support. I've had a chance to play with it a little so let's see where this goes...</p><h3>It's a preview</h3><p>Like movie previews, this is equal parts exciting and frustrating. Exciting because you get a teaser of things to come. Frustrating because you just want it to work <em>now</em>. And extra frustration points for various technical issues I've run into that, I hope, are due to the &quot;technical preview&quot; label.</p><p>For example, installing container support into an existing VM is mind-numbingly slow. Kudos to the team for making it easy to install but at the point where you run ContainerSetup.ps1, be prepared to wait for, by my watch, at least 45 minutes without any visual indication that something is happening. The only reason I knew something <em>was</em> happening is because I saw the size of the VM go up (slowly) on my host hard drive. This is on a 70Mbps internet connection so I don't think this can be attributed to &quot;island problems&quot; either.</p><p>I've heard tell of issues setting up container support in a Hyper-V VM as well. That's second-hand info as I'm using Fusion on a Mac rather than Hyper-V. If you run into problems setting it up on Hyper-V, consider switching to the <span style="text-decoration: line-through;">instructions for setting up containers on non-Hyper-V VMs instead</span> (no longer available).</p><p>There's also the Azure option. Microsoft was gracious enough to provide an Azure image for Windows Server 2016 pre-configured with container support. This works well if you're on Azure and I was able to run the nginx tutorial on it with no issues. I had less success with the IIS 10 tutorial even locally. I could get it running but was not able to create a new image based on the container I had.</p><h3>It's also a start</h3><p>Technical issues aside, I haven't been this excited about technology in Windows since...ASP.NET MVC, I guess, if my tag cloud is to be believed. And since this is a technical preview designed to garner feedback, here's what I want to see in the Windows container world</p><h4>Docker client <em>and</em> PowerShell support</h4><p>I love that I can use the Docker client to work with Windows containers. I can leverage what I've already learned with Docker in Linux. But I also love that I can spin up containers with PowerShell so I don't need to mix technologies in a continuous integration/continuous deployment environment if I already have PowerShell scripts set up for other aspects of my process.</p><h4>Support for legacy .NET applications</h4><p>I can't take credit for this. I've been talking with <a href="https://lostechies.com/gabrielschenker/" target="_blank" rel="noopener">Gabriel Schenker</a> about containers a lot lately and it was he who suggested they need to have support for .NET 4, .NET 3.5, and even .NET 2.0. It makes sense though. There are a lot of .NET apps out there and it would be a shame if they couldn't take advantage of containers.</p><h4>Smooth local development</h4><p>Docker Machine is great for getting up and running fast on a local Windows VM. To fully take advantage of containers, devs need to be able to work with them locally with no friction, whether that means a Windows Container version of Docker Machine or the ability to work with containers natively in Windows 10.</p><h4>ARM support</h4><p>At Western Devs, we have a <a href="http://www.westerndevs.com/using-azure-arm-to-deploy-a-docker-container/" target="_blank" rel="noopener">PowerShell script</a> that will spin up a new Azure Linux virtual machine, install docker, create a container, and run our website on it. It goes without saying (even though I'm saying it) that I'd like to do the same with Windows containers.</p><h4>Lots of images out of the gate</h4><p>I'd like to wean myself off VMs a little. I picture a world where I have one base VM and I use various containers for the different pieces of the app I'm working on. E.g. A SQL Server container, an IIS container, an ElasticSearch container, possibly even a Visual Studio container. I pick and choose which containers I need to build up my dev environment and use just one (or a small handful) of VMs.</p><hr><p>In the meantime, I'm excited enough about Windows containers that I hope to incorporate a small demo with them in my talk at <a href="http://measureup.io" target="_blank" rel="noopener">MeasureUP</a> in a few scant weeks so if you're in the Austin area, come on by to see it.</p><p>It is a glorious world ahead in this space and it puts a smile on this hillbilly's face to see it unfold.</p><p>Kyle the Barely Contained</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;strong&gt;UPDATE: April 27, 2017&lt;/strong&gt; Much of the information in this post is out-of-date and the links have been removed since they no longer exist.  For the latest on the state of containers on Windows, check out the &lt;a href=&quot;https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title type="html">Docker on Western Devs</title>
    <link href="https://westerndevs.com/_/docker-and-western-devs/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/_/docker-and-western-devs/</id>
    <published>2015-08-24T00:38:59.000Z</published>
    <updated>2026-03-22T17:42:31.821Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>In a month, I'll be attempting to hound my share of glory at <a href="http://measureup.io" target="_blank" rel="noopener">MeasureUP</a> with a talk on using Docker for people who may not think it impacts them. In it, I'll demonstrate some uses of Docker today in a .NET application. As I prepare for this talk, there's one thing we <a href="http://www.westerndevs.com" target="_blank" rel="noopener">Western Devs</a> have forgotten to talk about. Namely, some of us are already using Docker regularly just to post on the site.</p><a id="more"></a><p>Western Devs uses Jekyll. Someone suggested it, I tried it, it worked well, decision was done. Except that it doesn't work well on Windows. It's not officially supported on the platform and while there's a <a href="http://jekyll-windows.juthilo.com/" target="_blank" rel="noopener">good guide</a> on getting it running, we haven't been able to do so ourselves. Some issue with a gem we're using and Nokogiri and lib2xml and some such nonsense.</p><p>So in an effort to streamline things, <a href="http://www.westerndevs.com/bios/amir_barylko/" target="_blank" rel="noopener">Amir Barylko</a> create a <a href="https://github.com/westerndevs/western-devs-website/blob/source/Dockerfile" target="_blank" rel="noopener">Docker image</a>. It's based on the Ruby base image (version 2.2). After grabbing the base image, it will:</p><ul><li>Install some packages for building Ruby</li><li>Install the bundler gem</li><li>Clone the source code into the /root/jekyll folder</li><li>Run <code>bundle install</code></li><li>Expose port 4000, the default port for running Jekyll</li></ul><p>With this in place, Windows users can run the website locally without having to install Ruby, Python, or Jekyll. The command to launch the container is:</p><p><code>docker run -t -p 4000:4000 -v //c/path/to/code:/root/jekyll abarylko/western-devs:v1 sh -c 'bundle install &amp;&amp; rake serve'</code></p><p>This will:</p><ul><li>create a container based on the <code>abarylko/western-devs:v1</code> image</li><li>export port 4000 to the host VM</li><li>map the path to the source code on your machine to /root/jekyll in the container</li><li>run <code>bundle install &amp;&amp; rake serve</code> to update gems and launch Jekyll in the container</li></ul><p>To make this work 100%, you also need to expose port 4000 in VirtualBox so that it's visible from the VM to the host. Also, I've had trouble getting a container working with my local source located anywhere except C:\Users\<em>mysuername</em>. There's a permission issue somewhere in there where the container appears to successfully map the drive but can't actually see the contents of the folder. This manifests itself in an error message that says <code>Gemfile not found</code>.</p><p>Now, Windows users can navigate to localhost:4000 and see the site running locally. Furthermore, they can add and make changes to their posts, save them, and the changes will get reflected in the browser. Eventually, that is. I've noticed a 10-15 second delay between the time you press Save to the time when the changes actually get reflected. Haven't determined a root cause for this yet. Maybe we just need to soup up the VM.</p><p>So far, this has been working reasonably well for us. To the point, where fellow Western Dev, <a href="http://www.westerndevs.com/bios/dylan_smith/" target="_blank" rel="noopener">Dylan Smith</a> has automated the deployment of the image to Azure via <a href="https://github.com/westerndevs/western-devs-website/tree/source/_azure" target="_blank" rel="noopener">a Powershell script</a>. That will be the subject of a separate post. Which will give me time to figure out how the thing works.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;In a month, I&#39;ll be attempting to hound my share of glory at &lt;a href=&quot;http://measureup.io&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MeasureUP&lt;/a&gt; with a talk on using Docker for people who may not think it impacts them. In it, I&#39;ll demonstrate some uses of Docker today in a .NET application. As I prepare for this talk, there&#39;s one thing we &lt;a href=&quot;http://www.westerndevs.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Western Devs&lt;/a&gt; have forgotten to talk about. Namely, some of us are already using Docker regularly just to post on the site.&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title type="html">PSA: Setting Up Containers in a VM in Windows Server 2016 Tech Preview 3</title>
    <link href="https://westerndevs.com/_/psa-setting-up-containers-in-a-vm-in-windows-server-2016-tech-preview-3/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/_/psa-setting-up-containers-in-a-vm-in-windows-server-2016-tech-preview-3/</id>
    <published>2015-08-20T00:38:59.000Z</published>
    <updated>2026-03-22T17:42:31.826Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p><a href="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview" target="_blank" rel="noopener">Windows Server 2016 Tech Preview 3</a> has just been released and it has container support! There's documentation on it already to do basic stuff and it's easy to follow. So I'm going to <s>repeat it verbatim</s> quickly mention the one and only major issue I ran into.</p><a id="more"></a><p>I created a VM in Fusion for the server which went pretty smoothly. When presented with a list of operating systems, I selected Windows Server 2012 and it installed fine from the ISO file. After that, I started on the documentation and at the step where you run the ContainerSetup.ps1 powershell script, I got hit with an error:</p><pre><code>New-NetNat : No matching interface was found for prefix (null).At C:\ContainerSetup.ps1:247 char:5New-NetNat -Name ContainerNAT -InternalIPInterfaceAddressPrefix $ ...CategoryInfo : NotSpecified (MSFT_NetNat:root/StandardCimv2/MSFT_NetNat) [New-NetNat], CimExceptionFullyQualifiedErrorId : Windows System Error 1169,New-NetNat</code></pre><p><img src="http://i.imgur.com/W6BxfIE.png" alt="New-NetNat error"></p><p>This tripped me up for a while for a few reasons:</p><ol><li>I'm not crazy familiar with PowerShell</li><li>Nothing came up for the error message in the Bingoogle</li><li>InternalIPInterfaceAddressPrefix doesn't even appear in the documentation for <a href="https://technet.microsoft.com/en-us/library/dn283361(v=wps.630).aspx" target="_blank" rel="noopener">New-NetNat</a></li></ol><p>The answer, which came to me in a <a href="https://en.wikipedia.org/wiki/Rubber_duck_debugging" target="_blank" rel="noopener">rubber ducking</a> episode as I typed on a question on the forums, was in the VM's network configuration in VMWare Fusion:</p><p>![Network settings](http://i.imgur.com/nzfzr4X.png = 200x)</p><p>By default, VMs get created with the &quot;Share with my Mac&quot; setting. Changing this to Autodetect allowed the script to continue.</p><p>By that time, I had discovered that an Azure VM image already exists for Server 2016 Tech Preview 3 and even with the Containers Feature already enabled. So that's as far as I ventured with the VM.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Windows Server 2016 Tech Preview 3&lt;/a&gt; has just been released and it has container support! There&#39;s documentation on it already to do basic stuff and it&#39;s easy to follow. So I&#39;m going to &lt;s&gt;repeat it verbatim&lt;/s&gt; quickly mention the one and only major issue I ran into.&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title type="html">Docker Is Coming Whether You Like It or Not</title>
    <link href="https://westerndevs.com/_/docker-is-coming-whether-you-like-it-or-not/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/_/docker-is-coming-whether-you-like-it-or-not/</id>
    <published>2015-08-04T16:31:10.000Z</published>
    <updated>2026-03-22T17:42:31.821Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>I'm excited about Docker. Unnaturally excited, one might say. So much so that I'll be talking about it at <a href="http://measureup.io/" target="_blank" rel="noopener">MeasureUp</a> this September.</p><p>In the meantime, I have to temper my enthusiasm for the time being because Docker is still a Linux-only concern. Yes, you can run Docker containers on Windows but only Linux-based ones. So no SQL Server and no IIS.</p><a id="more"></a><p>But you can't stop a hillbilly from dreaming of a world of containers. So with a grand assumption that you know what Docker is roughly all about, here's what this coder of the earth meditates on, Docker-wise, before going to sleep.</p><h3>Microservices</h3><p>Microservices are a hot topic these days. We've <a href="http://www.westerndevs.com/podcasts/podcast-microservices/" target="_blank" rel="noopener">talked about them</a> at Western Devs already and Donald Belcham has a good and active <a href="https://github.com/dbelcham/microservice-material" target="_blank" rel="noopener">list of resources</a>. Docker is an eerily natural fit for microservices so much so that one might think it was created specifically to facilitate the architecture. You can package your entire service into a container and deploy it as a single package to your production server.</p><p>I don't think you can understate the importance of a technology like Docker when it comes to microservices. Containers are so lightweight and portable, you just naturally gravitate to the pattern through normal use of containers. I can see a time in the near future where it's almost negligent <strong>not</strong> to use microservices with Docker. At least in the Windows world. This might already be the case in Linux.</p><h3>Works On My Machine</h3><p>Ah, the crutch of the developer and the bane of DevOps. You set it up so nicely on your machine, with all your undocumented config entries and custom permissions and the fladnoogles and the whaztrubbets and everything else required to get everything perfectly balanced. Then you get your first bug from QA: can't log in.</p><p>But what if you could test your deployment on the <em>exact same image</em> that you deployed to? Furthermore, what if, when a bug came in that you can't reproduce locally, you could download the <em>exact container</em> where it was occurring? NO MORE EXCUSES, THAT'S WHAT!</p><h3>Continuous Integration Build Agents</h3><p>On one project, we had a suite of <a href="http://www.westerndevs.com/on-ui-testing/" target="_blank" rel="noopener">UI tests</a> which took nigh-on eight hours in TeamCity. We optimized as much as we could and got it down to just over hours. Parallelizing them would have been a lot of effort to set up the appropriate shared resources and configurations. Eventually, we set up multiple virtual machines so that the entire parallel test run could finish in about an hour and a half. But the total test time of all those runs sequentially is now almost ten hours and my working theory is that it's due to the overhead of the VMs on the host machine.</p><h3>Offloading services</h3><p>What I mean here is kind of like microservices applied to the various components of your application. You have an application that needs a database, a queue, a search components, and a cache. You could spin up a VM and install all those pieces. Or you could run a Postgres container, a RabbitMQ container, an ElasticSearch container, and a Redis container and leave your machine solely for the code.</p><p>When it comes right down to it, Docker containers are basically practical virtual machines. I've used VMs for many years. When I first started out, it was VMWare WorkStation on Windows. People that are smarter than me (including those that would notice that I should have said, &quot;smarter than <em>I</em>&quot;) told me to use them. &quot;One VM per client&quot; they would say. To the point that their host was limited to checking email and Twitter clients.</p><p>I tried that and didn't like it. I didn't like waiting for the boot process on both the host <em>and</em> each client and I didn't like not taking full advantage of my host's hardware on the specific client I happened to be working on at that moment.</p><p>But containers are lightweight. Purposefully so. <em>Delightfully</em> so. As I speak, the overworked USB drive that houses my VMs is down to 20 GB of free space. I cringe at the idea of having to spin up another one. But the idea of a dozen containers I can pick and choose from, all under a GB? That's a development environment I can get behind.</p><hr><p>Alas, this is mostly a future world I'm discussing. Docker is Linux only and I'm in the .NET space. So I have to wait until either: a) ASP.NET is ported over to Linux, or b) Docker supports Windows-based containers. And it's a big part of my excitement that <strong>BOTH</strong> of those conditions will likely be met within a year.</p><p>In the meantime, who's waiting? Earlier, I mentioned Postgres, Redis, ElasticSearch, and RabbitMQ. Those all work with Windows regardless of where they're actually running. Furthermore, Azure already has pre-built containers with all of these.</p><p>Much of this will be the basis of my talk at the upcoming <a href="http://measureup.io" target="_blank" rel="noopener">MeasureUP</a> conference next month. So...uhhh....don't read this until after that.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;I&#39;m excited about Docker. Unnaturally excited, one might say. So much so that I&#39;ll be talking about it at &lt;a href=&quot;http://measureup.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MeasureUp&lt;/a&gt; this September.&lt;/p&gt;
&lt;p&gt;In the meantime, I have to temper my enthusiasm for the time being because Docker is still a Linux-only concern. Yes, you can run Docker containers on Windows but only Linux-based ones. So no SQL Server and no IIS.&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title type="html">Microservices, or &quot;How to spread the love&quot;</title>
    <link href="https://westerndevs.com/_/microservices-or-how-to-spread-the-love/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/_/microservices-or-how-to-spread-the-love/</id>
    <published>2015-07-15T05:01:19.000Z</published>
    <updated>2026-03-22T17:42:31.824Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>For some time, people have been talking about <a href="https://www.nginx.com/blog/introduction-to-microservices/" target="_blank" rel="noopener">microservices</a>. I say &quot;some time&quot; for two reasons: 1) It's a good opening line, and 2) I have no clue how long people have been talking about them. I just heard the term for the first time about four months ago. So if I start talking about them now, while I still know virtually nothing, I can get at least two more future posts on the subject talking about how I was doing it wrong in the beginning.</p><a id="more"></a><p>In the meantime, I <em>have</em> been talking about them quite a bit recently. We've been using them on a project at <a href="http://www.clear-measure.com" target="_blank" rel="noopener">Clear Measure</a> and I'd like to think it's been successful but it's too soon to tell. I feel good about what we've done which, historically, has always been a good metric for me.</p><p>The topic has been covered at a technical and architectural level pretty well by Martin Fowler, so much so that he's even collected his discussions into a nice little <a href="http://martinfowler.com/microservices/" target="_blank" rel="noopener">Microservices Resource Guide</a>. In it, he and other ThoughtWorkians define them (to the extent that anything in software containing the word &quot;services&quot; <em>can</em> be defined), point out pros and cons compared to monolithic applications, describe testing strategies, and cover off the major success stories in the space.</p><p>That doesn't leave much ground for me to cover which, from a marketing standpoint, is almost surely the point. But I would like to add my voice if for no other reason than to plug the <a href="http://www.westerndevs.com/podcasts/podcast-microservices/" target="_blank" rel="noopener">podcast</a> on the subject.</p><p>One of the more interesting links on Fowler's Resource Guide is tucked away at the bottom. It's a series of posts on how SoundCloud is migrating from a monolith to microservices. <a href="https://developers.soundcloud.com/blog/building-products-at-soundcloud-part-1-dealing-with-the-monolith" target="_blank" rel="noopener">Part 1</a> discusses how they stopped working on the monolith and performed all new work in new microservices and <a href="https://developers.soundcloud.com/blog/building-products-at-soundcloud-part-2-breaking-the-monolith" target="_blank" rel="noopener">part 2</a> is on how they split the monolith up into microservices. There were challenges in both cases, leading to other architectural decisions like event sourcing.</p><p>The arguments for and against are, predictably, passionate and academic. &quot;Overkill!&quot; you say. &quot;Clean boundaries!&quot; sez I. &quot;But...DevOps!&quot; you counter. &quot;Yes...DevOps!&quot; I respond. But SoundCloud's experience, to me, is the real selling point of microservices. Unlike Netflix and Amazon, it's a scale that is still relatable to many of us. We can picture ourselves in the offices there making the same decisions they went through and running up against the same problems. These guys have BEEN THERE, man! Not moving to microservices because they <a href="https://plus.google.com/+RipRowan/posts/eVeouesvaVX" target="_blank" rel="noopener">have to</a> but because they had a real problem and needed a solution.</p><p>Now if you read the posts, there's a certain finality to them. &quot;We ran into this problem so we solved it by doing X.&quot; What's missing from the narrative is doubt. When they ran into problems that required access to an internal API, did anyone ask if maybe they defined the boundaries incorrectly? Once event sourcing was introduced, was there a question of whether they were going too far down a rabbit hole?</p><p>That's not really the point of these posts, which is merely to relay the decision factors to see if it's similar enough to your situation to warrant an investigation into microservices. All the same, I think this aspect is important for something still in its relative infancy, because there are plenty of people waiting to tell you &quot;I told you so&quot; as soon as you hit your first snag. Knowing SoundCloud ran into the same doubt can be reassuring. Maybe I'm just waiting for Microservices: The Documentary.</p><p>Regardless, there are already plenty of counter-arguments (or more accurately, counter-assumptions) to anecdotal evidence. Maybe the situation isn't the same. They have infrastructure. They have money and time to rewrite. They have confident, &quot;talented&quot; developers who always know how to solve architectural problems the right away.</p><p>So now I've more or less done what I always do when I talk microservices, which is talk myself into a corner. Am I for 'em or agin 'em? And more importantly, should <strong>you</strong>, reader, use them?</p><p>The answer is: absolutely, of course, and yes. On your current project? That's a little murkier. The experience is there and microservices have been done successfully. It's still a bit of a wild west which can be exciting if you ain't much for book learnin'. But &quot;exciting&quot; isn't always the best reason to decide on an architecture if someone else is paying the bills. As with any architectural shift, you have to factor in the human variables in your particular project.</p><p>For my limited experience, I like them. They solve one set of problems nicely and introduce a new set of problems that are not only tractable, but fun, in this hillbilly's opinion.</p><p>And why else did you get into the industry if not to have fun?</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;For some time, people have been talking about &lt;a href=&quot;https://www.nginx.com/blog/introduction-to-microservices/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;microservices&lt;/a&gt;. I say &amp;quot;some time&amp;quot; for two reasons: 1) It&#39;s a good opening line, and 2) I have no clue how long people have been talking about them. I just heard the term for the first time about four months ago. So if I start talking about them now, while I still know virtually nothing, I can get at least two more future posts on the subject talking about how I was doing it wrong in the beginning.&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title type="html">Outside the shack, or &quot;How to be a technology gigolo&quot;</title>
    <link href="https://westerndevs.com/_/outside-the-shack-or-how-to-be-a-gigolo/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/_/outside-the-shack-or-how-to-be-a-gigolo/</id>
    <published>2015-07-06T05:11:15.000Z</published>
    <updated>2026-03-22T17:42:31.825Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>Almost <a href="http://kyle.baley.org/2011/11/staying-home-for-the-night" target="_blank" rel="noopener">four years ago</a>, I waxed hillbilly on how nice it was to stick with what you knew, at least for side projects. At the time, my main project was Java and my side projects were .NET. Now, my main project is .NET and for whatever reason, I thought it would be nice to take on a side project.</p><p>The side project is <a href="http://www.westerndevs.com" target="_blank" rel="noopener">Western Devs</a>, a fairly tight-knit community of developers of similar temperament but only vaguely similar backgrounds. It's a fun group to hang out with online and in person and at one point, someone thought &quot;Wouldn't it be nice to build ourselves a website and have Kyle manage it while we lob increasingly ridiculous feature requests at him from afar?&quot;</p><p>Alas, I suffer from an unfortunate condition I inherited from my grandfather on my mother's side called &quot;Good Idea At The Time Syndrome&quot; wherein one sees a community in need and charges in to make things right and damn the consequences on your social life because dammit, these people need help! The disease is common among condo association members and school bus drivers. Regardless, I liked the idea and we're currently trying to pull it off.</p><p>The first question: what do we build it in? WordPress was an option we came up with early so we could throw it away as fast as possible. Despite some dabbling, we're all more or less entrenched in .NET so an obvious choice was one of the numerous blog engines in that space. Personally, I'd consider <a href="https://github.com/madskristensen/miniblog" target="_blank" rel="noopener">Miniblog</a> only because of its author.</p><p>Then someone suggested Jekyll hosted on GitHub pages due to its simplicity. This wasn't a word I usually assocated with hosting a blog, especially one in .NET, so I decided to give it a shot.</p><p>Cut to about a month later, and the stack consists of:</p><ul><li>Jekyll</li><li>GitHub Pages</li><li>Markdown</li><li>SASS</li><li>Slim</li><li>Rake</li><li>Travis</li><li>Octopress</li><li><a href="https://github.com/mmistakes/minimal-mistakes" target="_blank" rel="noopener">Minimal Mistakes Jekyll theme</a></li></ul><p>Of these, the one and only technology I had any experience with was Rake, which I used to automate UI tests at <a href="http://www.getbookedin.com" target="_blank" rel="noopener">BookedIN</a>. The rest, including Markdown, were foreign to me.</p><p>And Lord Tunderin' Jayzus I can not believe how quickly stuff came together. With GitHub Pages and Jekyll, infrastructure is all but non-existent. Octopress means no database, just file copying. Markdown, Slim and SASS have allowed me to scan and edit content files easier than with plain HTML and CSS. The Minimal Mistakes theme added so much built-in polish that I'm still finding new features in it today.</p><p>The most recent addition, and the one the prompted this post, was Travis. I'm a TeamCity guy and have been for years. I managed the <a href="http://teamcity.codebetter.com" target="_blank" rel="noopener">TeamCity server</a> for CodeBetter for many moons and on a recent project, had 6 agents running a suite of UI tests in parallel. So when I finally got fed up enough with our deploy process (one can type <code>git pull origin source &amp;&amp; rake site:publish</code> only so many times), TeamCity was the <a href="http://brendan.enrick.com/image.axd?picture=Golden-Hammer_1.png" target="_blank" rel="noopener">first hammer</a><sup>*</sup> I reached for.</p><p>One thing to note: I've been doing all my development so far on a MacBook. My TeamCity server is on Windows. I've done Rake and Ruby stuff on the CI server before without too much trouble but I still cringe inwardly whenever I have to set up builds involving technology where the readme says &quot;Technically, it works on Windows&quot;. As it is, I have an older version of Ruby on the server that is still required for another project and on Windows, Jekyll requires Python but not the latest version, and I need to install a later version of DevKit, etc, etc, and so on and so forth.</p><p>A couple of hours later, I had a build created and running with no infrastructure errors. Except that it hung somewhere. No indication why in the build logs and at that moment, my 5-year-old said, &quot;Dad, let's play hockey&quot; which sounded less frustrating than having to set up a local Windows environment to debug this problem.</p><p>After a rousing game where I schooled the kid 34-0, I left him with his mother to deal with the tears and I sat down to tackle the CI build again. At this point, it occurred to me I could try something non-Windows-based. That's where <a href="http://travis-ci.org" target="_blank" rel="noopener">Travis</a> came in (on a suggestion from <a href="http://localhost:4000/bios/dave_paquette/" target="_blank" rel="noopener">Dave Paquette</a> who I also want to say is the one that suggested Jekyll but I might be wrong).</p><p>Fifteen minutes. That's how long it took to get my first (admittedly failing) build to run. It was frighteningly easy. I just had to hand over complete access to my GitHub repo, add a config file, and it virtually did the rest for me.</p><p>Twenty minutes later, I had my first passing build which only built the website. Less than an hour later and our dream of continuous deployment is done. No mucking with gems, no installing frameworks over RDP. I updated a grand total of four files: .travis.yml, _config.yml, Gemfile, and rakefile. And now, whenever someone checks into the <code>source</code> branch, I am officially out of the loop. I had to do virtually nothing on the CI server itself, including setting up the Slack notifications.</p><p>This is a long-winded contradiction of my post of four years ago where my uncertainty with Java drove me to the comfort of .NET. And to keep perspective, this isn't exactly a mission critical, LOB application. All the same, for someone with 15-odd years of .NET experience under his obi, I'd be lying if I said I wasn't amazed at how quickly one can put together a functional website for multiple authors with non-Microsoft technology you barely have passing knowledge of.</p><p>To be clear, I'm fully aware of what people say about these things. I know Ruby is a fun language and I feel good about myself whenever I do anything substantial with it. And I know Markdown is all the rage with the kids these days. It's not really one technology on its own that made me approach epiphaniness. It's the way all the tools and libraries intermingle so well. Which has this optimistic hillbilly feeling like his personal life and professional life are starting to mirror each other.</p><p>Is there a lesson in here for others? I hope so as it would justify me typing all this out and <s>clicking publish</s> committing to the repository. But mostly, like everything else, I'm just happy to be here. As I've always said, if you've learned anything, that's your fault, not mine.</p><p>Kyle the Coalescent</p><p><sub><sup>* With credit to Brendan Enrick's and Steve Smith's <a href="http://brendan.enrick.com/post/Making-The-Software-Craftsmanship-Calendar-Images" target="_blank" rel="noopener">Software Craftsmanship Calendar 2016</a></sup></sub></p>]]></content>
    
    <summary type="html">
    
      The world outside is just awesome.
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title type="html">On UI Testing</title>
    <link href="https://westerndevs.com/_/on-ui-testing/" rel="alternate" type="text/html"/>
    <id>https://westerndevs.com/_/on-ui-testing/</id>
    <published>2015-06-30T12:09:05.000Z</published>
    <updated>2026-03-22T17:42:31.825Z</updated>
	<author>
	
	  
	  <name>Kyle Baley</name>
	  <email>kyle@baley.org</email>
	
	  <uri>https://westerndevs.com</uri>
	</author>
    
    <content type="html"><![CDATA[<p>A short while ago, a group of <a href="http://www.westerndevs.com" target="_blank" rel="noopener">Devs of the Western variety</a> had a chat. It was the latest in a series, depending on how you define &quot;series&quot;, where we gather together to discuss some topic, be it JavaScript frameworks, OO practices, or smoked meat. On this particular day, it was UI testing.</p><p>I don't recall all the participants but it was a good number of the people on <a href="/whoweare">this list</a>. Here, I'm going to attempt to summarize the salient points but given my memory, it'll more likely be a dissertation of my own thoughts. Which is just as well as I recall doing more talking than I should have.</p><h3>Should you UI test?</h3><p>This was a common thread throughout. Anyone who has done a significant amount of UI testing has asked a variant of this question. Usually in the form, &quot;Why the &amp;*%$ am I doing this?&quot;</p><p>Let it not be said that UI testing is a &quot;set it and forget it&quot; affair. Computers are finicky things, UI's seemingly more so. Sometimes things can take just that one extra second to render and all of a sudden your test starts acting out a Woody Allen scene: Where's the button? There's supposed to be a button. YOU TOLD ME THERE WOULD BE A BUTTON!!!</p><p>Eventually, we more or less agreed that they are <em>probably</em> worth the pain. From my own experience, working on a small team with no QA department, they saved us on several occasions. Yes, there are the obvious cases where they catch a potential bug. But there was also a time when we had to re-write a large section of functionality with no change to the UI. I felt <em>really</em> good about having the tests then.</p><p>One counter-argument was whether you could just have a comprehensive suite of integration tests. But there's something to be said for having a test that:</p><ol><li>Searches for a product</li><li>Adds it to the shopping cart</li><li>Browses more products</li><li>Checks out</li><li>Goes to PayPal and pays</li><li>Verifies that you got an email</li></ol><p>This kind of integration test is hard to do, especially when you want to verify all the little UI things in between, like whether a success message showed up or whether the number of items in the shopping cart incremented by 1.</p><p>We also had the opposite debate: If you have a comprehensive suite of UI tests and are practicing BDD, do you still need TDD and unit tests? That was an interesting side discussion that warrants a separate post.</p><h3>Maintenance</h3><p>...is ongoing. There's no getting around that. No matter how bullet-proof you make your tests, the real world will always get in the way. Especially if you integrate with third-party services (&lt;cough&gt;PayPal&lt;cough&gt;). If you plan to introduce UI tests, know that your tests will be needy at times. They'll fail for reasons unknown for several consecutive runs, then mysteriously pass again. They'll fail only at certain times of the day, when Daylight Savings Time kicks in, or only on days when Taylor Swift is playing an outdoor venue in the western hemisphere. There will be no rhyme or reason to the failures and you will never, <em>ever</em> be able to reproduce them locally.</p><p>You'll add <code>sleep</code> calls out of frustration and check in with only a vague hope that it will work. Your pull requests will be riddled with variations of &quot;I swear I wouldn't normally do this&quot; and &quot;I HAVE NO IDEA WHAT'S GOING ON&quot;. You'll replace elegant CSS selectors with XPath so grotesque that Alan Turing will rise from his grave only to have his rotting eyeballs burst into flames at the sight of it.</p><p>This doesn't really jibe with the &quot;probably worth it&quot; statement earlier. It depends on how often you have to revisit them and how much effort goes into it. From my experience, early on the answer to that is often and a lot. As you learn the tricks, it dwindles significantly.</p><p>One of those tricks is the <a href="http://martinfowler.com/bliki/PageObject.html" target="_blank" rel="noopener">PageObject pattern</a>. There was universal agreement that it is required when dealing with UI tests. I'll admit I hadn't heard of the pattern before the discussion but at the risk of sounding condescending, it sounds more like common sense than an actual pattern. It's something that, even if you don't implement it right away, you'll move toward naturally as you work with your UI tests.</p><h3>Data setup</h3><p>...is hard, too. At least in the .NET world. Tools like <a href="https://github.com/HeadspringLabs/Tarantino" target="_blank" rel="noopener">Tarantino</a> can help by creating scripts to prime and tear down a database. You can also create an endpoint (on a web app) that will clear and reset your database with known data.</p><p>The issue with these approaches is that the &quot;known&quot; data has to actually <em>be</em> known when you're writing your tests. If you change anything in it, Odin knows what ramifications that will have.</p><p>You can mitigate this a little depending on your technology. If you use SpecFlow, then you may have direct access to the code necessary to prime your database. Otherwise, maybe you can create a utility or API endpoints that allow you to populate your data in a more transparent manner. This is the sort of thing that a ReST endpoint can probably do pretty well.</p><h3>Mobile</h3><p>Consensus for UI testing on mobile devices is that it sucks more than that time after the family dinner when our cousin, Toothless Maggie, cornered---...umm... it's pretty bad...</p><p>We would love to be proven wrong but to our collective knowledge, there are no decent ways to test a mobile UI in an automated fashion. From what I gather, it's no picnic doing it in a manual fashion. Emulators are laughably bad. And there are more than a few different types and versions of mobile device so you have to use these laughably bad options about a dozen different ways.</p><h3>Outsourcing</h3><p>What about companies that will run through all your test scripts on multiple browsers and multiple devices? You could save some development pain that way. But I personally wouldn't feel comfortable unless the test scripts were <em>extremely</em> prescriptive. And if you're going to that length, you could argue that it's not a large effort to take those prescriptive steps and automate them.</p><p>That said, you might get some quick bang for your buck going this route. I've talked to a couple of them and they are always eager to help you. Some of them will even record their test sessions which I would consider a must-have if you decide to use a company for this.</p><h3>Tooling</h3><p>I ain't gonna lie. I like Cucumber and <a href="https://github.com/jnicklas/capybara" target="_blank" rel="noopener">Capybara</a>. I've tried <a href="http://www.specflow.org/" target="_blank" rel="noopener">SpecFlow</a> and it's probably as good as you can get in C#, which is decent enough. But it's hard to beat <code>fill_in 'Email', :with =&gt; 'hill@billy.edu'</code> for conciseness and readability. That said, do <strong>not</strong> underestimate the effort it takes to introduce Ruby to a .NET shop. There is a certain discipline required to maintain your tests and if everyone is scared to dive into your rakefile, you're already mixing stripes with plaid.</p><p>We also discussed <a href="http://lefthandedgoat.github.io/canopy/" target="_blank" rel="noopener">Canopy</a> and there was a general appreciation for how it looks though <a href="/bios/amir_barylko">Amir</a> is the only one who has actually used it. Seems to balance the readability of Capybara with the &quot;it's still .NET&quot; aspect of companies that fear anything non-Microsoft.</p><p>Of course, there's Selenium both the IDE and the driver. We mentioned it mostly because you're supposed to.</p><p>Some version of Visual Studio also provided support for UI tests, both recorded and coded. The CodedUI tests are supposed to have a pretty nice fluent interface and we generally agreed that coded tests are the way to go instead of recorded ones (as if that were ever in doubt).</p><p>Ed. note: Shout out to <a href="https://angular.github.io/protractor/#/" target="_blank" rel="noopener">Protractor</a> as well. We didn’t discuss it directly but as <a href="http://www.westerndevs.com/bios/dave_paquette/" target="_blank" rel="noopener">Dave Paquette</a> pointed out later, it helps avoid random Sleep calls in your tests because it knows how to wait until binding is done. Downside is that it’s specific to Angular.</p><p>Also: <a href="http://jasmine.github.io/" target="_blank" rel="noopener">jasmine</a> and <a href="http://phantomjs.org/" target="_blank" rel="noopener">PhantomJS</a> got passing mentions, both favorable.</p><h3>Continuous Integration</h3><p>This is about as close as we got to disagreement. There was a claim that UI tests shouldn't be included in CI due to the length of time it takes to run them. Or if they are included, run them on a schedule (i.e. once a night) rather than on every &quot;check in&quot; (by which we mean, every feature).</p><p>To me, this is a question of money. If you have a single server and a single build agent, this is probably a valid argument. But if you want to get full value from your UI tests, get a second agent (preferably more) and run only the UI tests on it. If it's not interfering with your main build, it can run as often as you like. Yes, you may not get the feedback right away but you get it sooner than if you run the UI tests on a schedule.</p><hr><p>The main takeaway we drew from the discussion, which you may have gleaned from this summary, is: damn, we should have recorded this. That's a mistake we hope to rectify for future discussions.</p>]]></content>
    
    <summary type="html">
    
      What happens when 12 people gather to talk about UI testing?
    
    </summary>
    
    
  </entry>
  
</feed>
