<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Joe Lennon</title>
		<atom:link href="https://joelennon.com/feed" rel="self" type="application/rss+xml" />
		<link>https://joelennon.com</link>
		<description>Developer of ProgrammingPodcasts.com. Founder of Subwoofr. Author of HTML5 in Action and Beginning CouchDB. IBM developerWorks Contributing Author.</description>
		<lastBuildDate>Thu, 09 Mar 2017 09:11:45 +0000</lastBuildDate>
		<generator>https://clob.io/</generator>
					<item>
				<title>Using reserved ngrok subdomains in Laravel Valet</title>
				<link>https://joelennon.com/using-reserved-ngrok-subdomains-in-laravel-valet</link>
				<guid>https://joelennon.com/using-reserved-ngrok-subdomains-in-laravel-valet</guid>
				<pubDate>Tue, 07 Mar 2017 15:07:21 +0000</pubDate>
				<description><![CDATA[<p>I ran into an interesting issue the other day while working on a Laravel project that handles inbound email using Amazon SES and SNS. Basically, I needed to be able to send a webhook from SNS to my application whenever an email was received in SES. As you may have experienced in the past, webhooks and local development environments don't exactly play nicely together - the webhook is expecting a public URL and your local machine is typically not going to be responding to requests over the Internet.</p>
<p>A great solution to this problem is to use <a href="https://ngrok.com/">ngrok</a>. Luckily, <a href="https://laravel.com/docs/5.4/valet">Laravel Valet</a> includes ngrok out of the box, and using it is a cinch:</p>
<pre><code class="language-bash">$ valet share</code></pre>
<p>This will route requests from a subdomain such as <code>ddcb9385.ngrok.io</code> to the local copy of your application while ngrok is running, giving you a public URL for testing your webhooks. This is great, but every time you run ngrok you'll get a different subdomain, so you'll need to update the services that call your webhook if you want them to continue to work.</p>
<p>Luckily, ngrok offers a premium feature for just $5/month called <strong>reserved domains</strong>. This allows you to reserve a specific subdomain within a selected region such as <code>yourproject.us.ngrok.io</code>. You can then tell ngrok to use this subdomain using the parameters <code>-subdomain=yourproject -region=us</code>. Brilliant.</p>
<p>Except when you try to pass those flags through to Valet, you'll get a whole bunch of <em>Failed to connect to 127.0.0.1 port 4040: Connection refused</em> errors. If you scroll up, you'll see a message:</p>
<pre><code>Tunnel session failed: Only paid plans may bind custom subdomains.
Failed to bind the custom subdomain 'yourproject' for an unauthenticated client.
Sign up at: https://ngrok.com/signup</code></pre>
<p>Makes sense that you'd need to authenticate to use your custom subdomain. You can do this with the authtoken that's shown on your ngrok Dashboard when you login at ngrok.com. Unfortunately Valet doesn't have a command to pass through to ngrok to do this for you. Thankfully, we can do it manually, and you only need to do it once per machine:</p>
<pre><code class="language-bash">$ ~/.composer/vendor/laravel/valet/bin/ngrok authtoken &lt;YOUR_AUTHTOKEN&gt;</code></pre>
<p>This will output a message <em>Authtoken saved to configuration file</em>, and now you should be able to use your reserved domain with the command (replace <code>yourproject</code> and <code>us</code> with the appropriate values):</p>
<pre><code class="language-bash">$ valet share -subdomain=yourproject -region=us</code></pre>
<p>This is also really useful for projects that use Stripe billing, making it easy to accept incoming webhooks in your local environment.</p>]]></description>
			</item>
					<item>
				<title>How I built Programming Podcasts</title>
				<link>https://joelennon.com/how-i-built-programming-podcasts</link>
				<guid>https://joelennon.com/how-i-built-programming-podcasts</guid>
				<pubDate>Wed, 01 Mar 2017 14:30:00 +0000</pubDate>
				<description><![CDATA[<p><img src="/img/progcasts-post/site.png" alt="The end result in all its glory, Programming Podcasts" /></p>
<p>Although I've been aware of podcasts for as long as they've been around, it's only in the past six months that I've become a huge fan of the medium. It all started with <a href="http://www.laravelpodcast.com/">The Laravel Podcast</a>, a show that I started listening to after a conversation with <a href="http://www.gordonmurray.com/">Gordon Murray</a> prompted me to go check it out. I was hooked almost instantly, and started listening to episodes in the car on my daily commute and eventually in the gym too.</p>
<p>I quickly ran into a pretty major problem - discovering new podcasts, particularly in the realm of software development, was a total pain in the ass. iTunes categories don't go beyond &quot;Technology&quot;, and when you search on Google you're presented with BuzzFeed-style <strong>15 Top-Notch Podcasts</strong>, <strong>11 podcasts I listen to</strong> and <strong>42 Podcasts for Developers and Programmers</strong> articles. These are great lists, but to figure out if I'd actually listen to any of the podcasts, I now had to go through each site one-by-one and check out an episode.</p>
<p>I knew that I could solve the problem by building a podcast directory that featured podcasts that focus on software development and related topics like devops, design and startups.</p>
<h2>Deciding should I build</h2>
<p>I start a lot of projects, but I complete few. Often this is due to becoming bored with the concept after a short time. Sometimes it is because I'm not all that interested in the subject matter, or I'm not solving a problem that I have myself. Other times it's because it becomes clear after a while that nobody actually wants what I'm working on.</p>
<p>With this idea, I already knew going in that it would have one loyal user at a mininum - me. But I wanted to know if anyone else would find it useful - this would dictate if I should spend more than a couple of hours on it. I used Google AdWords' <a href="https://adwords.google.com/KeywordPlanner">Keyword Planner</a> to research how many monthly searches there were for related search terms.</p>
<p><img src="/img/progcasts-post/keywords.png" alt="Google AdWords Keyword Planner" /></p>
<p>With 10k-100k average monthly searches across the topic, I felt there was enough of an audience to justify building something for more than just myself.</p>
<h2>Coming up with a name</h2>
<p>Because the site was a directory, and because I felt that the best approach to getting traffic would be through organic search and SEO, it was important that the site would have a descriptive name. No made-up brandable words or something that would require tons of marketing - something that would be highly relevant to what people would search for.</p>
<p>I was dead set on registering a .com name for the site, and it was pure luck that programmingpodcasts.com was available. Given the strong performance of the keyword in Google search results, and its high relevance to the content of the site, I went ahead and bought it.</p>
<h2>Basic site mechanics</h2>
<p>I wanted to keep the site as simple as possible - the goal was to make it easy to find podcasts and episodes, so I decided to avoid any superfluous content right from the start. As a result, the structure of the site wouldn't run too deep. I mapped out the following structure:</p>
<p><img src="/img/progcasts-post/sitemap.jpg" alt="Site Map" /></p>
<p>On the front page, the site would have a listing of the newest episodes, a signup form for the mailing list and a listing of the most popular podcasts on the site. One of the key benefits I wanted the site to offer was a solid categorisation of podcasts - to a much finer level than that offered by iTunes. I decided to define two taxonomies:</p>
<ul>
<li><strong>Categories</strong> - these are broad areas like <em>General Programming</em>, <em>Web Development</em>, <em>DevOps</em> and <em>Design</em></li>
<li><strong>Topics</strong> - these are specific programming languages, frameworks or products like <em>JavaScript</em>, <em>Ruby</em>, <em>PHP</em>, <em>React</em>, <em>Amazon Web Services</em> and <em>Laravel</em></li>
</ul>
<p>A list of categories and topics is on the front page, and drilling down into these will show just the podcasts or episodes in the selected taxonomy.</p>
<p>The &quot;Explore&quot; page would adapt based on the user's selection to show all items or a specific category, topic or search results. It would allow the results to be sorted based on date, popularity or alphabetically. It would also let the user flip the view between a listing of podcasts and a listing of episodes.</p>
<p><img src="/img/progcasts-post/explore.png" alt="Exploring the directory" /></p>
<p>Drilling to a podcast would display that podcast's information and a list of episodes. Drilling to an episode would display individual episode information. Anywhere an episode is shown to the user, they would have the option to play the audio right there in the browser. At launch, this was just a simple HTML5 audio element using the native browser controls for playback, but I had plans to make this much better in the future.</p>
<h2>The stack</h2>
<p>In the past couple of years, I've built virtually every side project I've worked on with the <a href="https://laravel.com/">Laravel</a> PHP framework. When I first came across Laravel, I was truly blown away by it. I hadn't used PHP since I was in college, and didn't have too fond memories of it. Since then, I'd used everything from Oracle and PL/SQL to ASP.NET MVC and C#, from Node.js and Express to Ruby and Sinatra. PHP had come a long way since the dark ages of PHP4, but Laravel was by far the most impressive Web frameworks I had ever used. It is a batteries-included framework, it comes with pretty much everything you'll need when building a Web application - but is still flexible enough to get out of your way when you need it to.</p>
<p>Having spent several years working on heavy client-side JavaScript applications, in recent years I have fallen back in love with applications that don't require much JavaScript at all. Don't get me wrong, I strongly believe that there are times when using something like React, Vue or Angular is justified - but for many projects they are completely overkill. So at first, Programming Podcasts was completely JavaScript-free. After all, it's a content site, so why would I really need it? When I launched the site, the only JavaScript code was to prevent multiple HTML5 audio elements playing simultaneously.</p>
<p>For search, I wanted to offer proper full-text search, no crappy MySQL <code>LIKE '%:query%'</code> clauses that would miss tons of relevant results. Using <a href="https://laravel.com/docs/5.4/scout">Laravel Scout</a> and <a href="https://www.algolia.com/">Algolia</a>, this took all of five minutes. Algolia is a little expensive for a project like this, so I might need to look at alternative solutions down the line, but to get up and running with almost no setup and configuration, nothing I've come across is faster.</p>
<p>As for deployment, I decided to keep things simple to start off. Using <a href="https://forge.laravel.com/">Laravel Forge</a>, I deployed to a single Linode box. If the site took off, it would be straightforward to move the database to its own box, implement load balancing and add multiple Web and queue worker servers if needs be. Forge makes the entire deployment process a breeze. It will provision a Linode box, DigitalOcean droplet or AWS EC2 instance for you, install and configure the relevant software and then set it up so you can deploy just by pushing to a Git repository. It also makes automatically provisioning and renewing SSL certificates completely painless.</p>
<p>I also use S3 for storing podcast and episode images, and use CloudFront CDN to serve them up. More on why I needed to do this later.</p>
<h2>Populating the database with feeds</h2>
<p>One of the aspects of the project that attracted me the most was that given the nature of how podcasts are distributed, it should have been possible to have the entire site run on auto-pilot. Almost every podcast has an RSS feed, and to get listed on iTunes there are specific guidelines on what data needs to be present in the feed - including hi-res cover images. RSS is straightforward to parse, and I found that almost every podcast I looked at published consistent and reliable feeds.</p>
<p><img src="/img/progcasts-post/rss.png" alt="RSS FTW" /></p>
<p>Once I had a feed URL for a podcast, I could then fetch everything else I needed from the XML. So the title, description, name of the host and all of the details of every episode could easily be extracted from the feed and added to the database. A scheduled job would then periodically check for new episodes (and podcasts) and automatically add them to the database.</p>
<p>The only manual effort I'd need to do past collecting the RSS feed URLs would be:</p>
<ul>
<li>Getting the iTunes URL for a podcast</li>
<li>Selecting a category, and if relevant, topic for the podcast</li>
</ul>
<p>I set a target of 200 podcasts for launch - this would be enough to make the directory feel complete, but not so high that manually adding iTunes URLs and categories would be too time consuming. I did write a little script that would go to the website of the feed and look for an iTunes link and present it to me if it found one - but I still manually checked everything just to make sure everything was right before launch.</p>
<p>With the 200 podcast target, I set off to fill the directory. Many of the lists I mentioned earlier came in handy, and I wrote a script that would search for podcasts on the Web, pull in their RSS feed data and I then queried this data for relevant keywords. This gave me a shortlist of podcasts, and I reviewed them to see which would be suitable for the site. I ended up launching with 201 feeds.</p>
<h2>Snags</h2>
<p>When I set out to build the site, it looked as though it would be very straightforward. But quickly enough I hit a bump in the road. At first I was simply hotlinking to the podcast and episode image URLs that were included in the RSS feed but when I started focusing on ensuring the site performed well I noticed that this was leading to huge images being downloaded. Some of the images were over 3 megabytes, all down to iTunes requirement that podcast artwork had minimum dimensions of 1400x1400 pixels.</p>
<p><img src="/img/progcasts-post/hugeimages.png" alt="Megabytes of images - no thank you!" /></p>
<p>The hotlinking of images also presented a second problem - mixed content security warnings for images hosted on a HTTP URL. As a result, I decided it would make more sense for me to download the images for each podcast and episode, resize them (I settled on 500x500) and if they were JPEGs compressed them to 60% quality. I then uploaded the modified image to Amazon S3, and put Amazon CloudFront CDN in front of this bucket to optimize the delivery of the assets to visitors around the world.</p>
<p><img src="/img/progcasts-post/mixedcontent.png" alt="Hotlinking mixed content warnings - yikes!" /></p>
<p>As the site contained over 15,000 episodes - the first run of this task took a while, but it drastically reduced the load time of the page and solved the mixed-content warning issue, so it was worth the effort.</p>
<h2>A better audio listening experience</h2>
<p>One of the aspects of the site that bothered me at launch was the fact that if you used the simple HTML5 audio player to listen to an episode, it would stop playing if you navigated away from the page, and would not remember where you left off if you went to play it again. So I decided early on that I would fix this post-launch with an audio player that remained static across the site and would not be interrupted as you browse.</p>
<p>I had actually previously solved this issue for music artist websites for my startup, <a href="https://subwoofr.com">Subwoofr</a>. At the time, I used the PJAX library to load internal links over XMLHttpRequest and just replace the section of the document that needed to be replaced with new content. This basically removed the need for page loads when navigating between pages, and allowed audio to continue playing as you browse. A nice bonus was that the site feels much faster as links appear to load instantly without the page flash that normally occurs.</p>
<p><img src="/img/progcasts-post/turbolinks.gif" alt="Turbolinks in action" /></p>
<p>The PJAX library is dependent on jQuery, and it hasn't been updated in some time. It also requires you to do some hackery to get things like page titles to update as you navigate from page to page. At some point in the past when working on a Ruby on Rails project, I had used <a href="https://github.com/turbolinks/turbolinks">Turbolinks</a> for a similar purpose. I checked out Turbolinks again and found it was perfect for what I wanted to achieve and didn't require me to load in jQuery or third-party libraries.</p>
<p>I then built a custom audio player using the native HTMLAudioElement API. I did so for a couple of reasons. First, when using the browser's native controls, Turbolinks will pause the audio between page loads. Second, the styling of the player is quite different on various browsers. Finally, I wanted to allow users the option of changing the playback speed, as I know many people like to listen to podcasts as 1.5x playback rate.</p>
<p>This worked great, but there was an edge cases where the audio would get cut off between page loads - if Turbolinks times out and does a hard page load. Also, I thought it would be nice if a user navigated away to another site and came back later, that the last episode they were listening to would resume where they left off, with their playback rate and muting preferences intact. To achieve this, I used HTML5 localStorage. I listen to several audio events, and update localStorage to reflect the current playtime, playback rate and whether or not the volume is muted. When the page loads, I check if there is data in localStorage, and if so I restore it into the player and the user can jump right back in to the episode.</p>
<h2>Launch</h2>
<p>I launched the website on Wednesday, 22nd March 2017 at 4pm. I posted a <a href="https://news.ycombinator.com/item?id=13706241">Show HN</a> to Hacker News, <a href="https://twitter.com/joelennon/status/834427525937893376">tweeted</a> about it, posted on a couple of <a href="https://www.reddit.com/r/SideProject/comments/5vjiy1/programmingpodcasts_a_curated_directory_of/">Subreddits</a> and added it to <a href="https://betapage.co/product/programming-podcasts">BetaPage</a>. I've never had a post do well on Hacker News, but this post performed even worse than anything I've put up in the past. This was very disappointing, as I thought the HN audience would really find it useful. A week later, someone posted an <a href="https://news.ycombinator.com/item?id=13747563">Ask HN</a> seeking everyone's favorite podcasts, and this made the front page. Perhaps my timing was bad - I posted at a very busy time for HN submissions - I knew this was a risk, but felt that if it got traction, the heavy volume of traffic would be worth it. Perhaps nobody gave a shit. I guess I'll never know!</p>
<p>On the Sunday after launch, I dropped an email to <a href="https://adamwathan.me/">Adam Wathan</a>, asking him to be one of the first guests on my soon-to-be-launched podcast (this is something I'm planning in the coming weeks). I told him about the new site and asked for his feedback. He loved it, retweeted one of my tweets and this drove a nice stream of traffic to the site, especially for a Sunday! Someone posted the site on <a href="https://www.producthunt.com/posts/programmingpodcasts-com">Product Hunt</a> the next Tuesday, but I didn't know this had happened until the next day, and it had barely even registered. I ran into problems trying to add images to the Product Hunt listing, presumably they were victims of the chronic <a href="https://news.ycombinator.com/item?id=13755673">S3 outage</a> that affected much of the Internet that day.</p>
<p><img src="/img/progcasts-post/traffic.png" alt="First Week Traffic Stats" /></p>
<p>Overall, I was pretty pleased with the first week's traffic. 786 unique users generated a little over 3,000 page views. Users spent an average of 2 and a half minutes on the site which was decent, but I'm hopeful that the fixed audio player (deployed today, a week after the initial launch) will lead to an uptick in this particular statistic. As you can see, most of the traffic stemmed following Adam's post on Sunday, and despite it dropping significantly the following day, it was good to see traffic rise again the next day.</p>
<h2>What's next?</h2>
<p>I'm hopeful that over time the site will start to rank well in Google's results for related keywords, and that this will drive decent organic traffic to the site. I also have some other ideas about how to take the project further:</p>
<ul>
<li>A new blog on the site that showcases podcasts and offers tips on hosting podcasts</li>
<li>A podcast of my own, interviewing the hosts of podcasts featured on the site</li>
<li>User sign up - logged in users will be able to set their preferred categories and topics to tailor the site to their interests, upvote and comment on podcasts and episodes, add podcasts and episodes to their collection, get custom RSS feeds for their collection, queue up multiple episodes for playback and subscribe for email updates whenever new episodes are published in their favorite podcasts</li>
<li>Automatically download and process audio files to generate transcripts and waveform data and perform further analysis, extend searchability and more</li>
<li>Native mobile app on iOS/Android, native desktop app on Mac/Linux/Windows</li>
<li>Add sister sites for other verticals (design, startups, business, others)</li>
</ul>
<p>I'd love to hear your thoughts on the above ideas, and if you have any features you'd like to see on the site I'd greatly appreciate any suggestions.</p>
<p>Whether or not I'll do all of this will depend on the success of the project. I haven't given too much thought to monetisation yet, obviously there is the potential to run ads or sponsorship slots on the site, mailing list and podcast, but I'm going to hold off on pursuing this until the site has a much larger audience. I have some rough ideas about related products that could provide a revenue stream, but I'll keep those close to my chest for now.</p>
<h2>In conclusion</h2>
<p>Overall this has been a great project to work on, and I'm glad I did it. The reaction from those that I have reached so far has been almost exclusively positive. Not to mention that I now have an endless supply of podcasts to listen to every morning and evening.</p>
<p>I'll post a follow up to this after the site's been live for one month - it will be interesting to see the difference between the progress in the first week and the first month, here's hoping it'll be a positive one!</p>]]></description>
			</item>
					<item>
				<title>Reducing the number of links in Laravel&#039;s default paginator template</title>
				<link>https://joelennon.com/reducing-the-number-of-links-in-laravels-default-paginator-template</link>
				<guid>https://joelennon.com/reducing-the-number-of-links-in-laravels-default-paginator-template</guid>
				<pubDate>Wed, 15 Feb 2017 17:15:15 +0000</pubDate>
				<description><![CDATA[<p>Laravel's pagination features make it really simple to paginate your database queries and provide your users with links to navigate between pages of results. By default, these links are styled using standard Bootstrap classes. If you want to change the template used for the pagination links, you can run the following artisan command:</p>
<pre><code class="language-bash">$ php artisan vendor:publish --tag=laravel-pagination</code></pre>
<p>This will create a new <code>vendor</code> subdirectory in your <code>views</code> folder, and inside here you'll find the standard pagination view under <code>pagination/default.blade.php</code>.</p>
<p>This is great, but by default Laravel will insert three dots (...) between pages when you have a large number of pages. This is much better than printing out links for every single page, but the number of links displayed is still quite high and will still typically lead to wrapping on mobile devices.</p>
<p>At first glance, customising this may seem difficult, as the pagination template loops over an <code>$elements</code> array that is defined in <code>Illuminate\Pagination\LengthAwarePaginator</code>. This uses a class named <code>Illuminate\Pagination\UrlWindow</code> to construct arrays of links and the three dots items as appropriate. Although the <code>get</code> method used here has an argument <code>onEachSide</code> that should allow you to configure how many links show either side of the dots, there is no straightforward way of hooking into this without creating your own pagination classes.</p>
<p>Fortunately, you can sidestep doing this if you're happy enough adding some logic to the pagination template to determine when to show the three dots. The code below will show no more than 2 pages either side of the current page, and will show three dots as well as the first page and last page links as appropriate. I've applied a <code>hidden-xs</code> class to the three dots and first page/last page links to ensure that the pagination links appear on a single line as much as possible on mobile.</p>
<pre><code class="language-html">@if ($paginator-&gt;hasPages())
    &lt;ul class="pagination pagination"&gt;
        {{-- Previous Page Link --}}
        @if ($paginator-&gt;onFirstPage())
            &lt;li class="disabled"&gt;&lt;span&gt;&amp;laquo;&lt;/span&gt;&lt;/li&gt;
        @else
            &lt;li&gt;&lt;a href="{{ $paginator-&gt;previousPageUrl() }}" rel="prev"&gt;&amp;laquo;&lt;/a&gt;&lt;/li&gt;
        @endif

        @if($paginator-&gt;currentPage() &gt; 3)
            &lt;li class="hidden-xs"&gt;&lt;a href="{{ $paginator-&gt;url(1) }}"&gt;1&lt;/a&gt;&lt;/li&gt;
        @endif
        @if($paginator-&gt;currentPage() &gt; 4)
            &lt;li class="disabled hidden-xs"&gt;&lt;span&gt;...&lt;/span&gt;&lt;/li&gt;
        @endif
        @foreach(range(1, $paginator-&gt;lastPage()) as $i)
            @if($i &gt;= $paginator-&gt;currentPage() - 2 &amp;&amp; $i &lt;= $paginator-&gt;currentPage() + 2)
                @if ($i == $paginator-&gt;currentPage())
                    &lt;li class="active"&gt;&lt;span&gt;{{ $i }}&lt;/span&gt;&lt;/li&gt;
                @else
                    &lt;li&gt;&lt;a href="{{ $paginator-&gt;url($i) }}"&gt;{{ $i }}&lt;/a&gt;&lt;/li&gt;
                @endif
            @endif
        @endforeach
        @if($paginator-&gt;currentPage() &lt; $paginator-&gt;lastPage() - 3)
            &lt;li class="disabled hidden-xs"&gt;&lt;span&gt;...&lt;/span&gt;&lt;/li&gt;
        @endif
        @if($paginator-&gt;currentPage() &lt; $paginator-&gt;lastPage() - 2)
            &lt;li class="hidden-xs"&gt;&lt;a href="{{ $paginator-&gt;url($paginator-&gt;lastPage()) }}"&gt;{{ $paginator-&gt;lastPage() }}&lt;/a&gt;&lt;/li&gt;
        @endif

        {{-- Next Page Link --}}
        @if ($paginator-&gt;hasMorePages())
            &lt;li&gt;&lt;a href="{{ $paginator-&gt;nextPageUrl() }}" rel="next"&gt;&amp;raquo;&lt;/a&gt;&lt;/li&gt;
        @else
            &lt;li class="disabled"&gt;&lt;span&gt;&amp;raquo;&lt;/span&gt;&lt;/li&gt;
        @endif
    &lt;/ul&gt;
@endif</code></pre>
<p>Note, if you have hundreds of pages and need to support mobile devices with narrow widths, such as the iPhone 5S or below, you may want to add a <code>pagination-sm</code> class on to the main <code>&lt;div&gt;</code> to avoid wrapping. Alternatively, you could resort to just showing the previous, next and current page links on mobile.</p>]]></description>
			</item>
					<item>
				<title>Minifying assets with Laravel Mix watch command</title>
				<link>https://joelennon.com/minifying-assets-with-laravel-mix-watch</link>
				<guid>https://joelennon.com/minifying-assets-with-laravel-mix-watch</guid>
				<pubDate>Wed, 15 Feb 2017 10:36:46 +0000</pubDate>
				<description><![CDATA[<p>Laravel Mix is the fantastic Webpack-based asset compilation framework that ships by default with Laravel 5.4, replacing the Gulp-based Laravel Elixir. It has a ton of sensible defaults, and it's likely that you can use the default scripts in most cases to compile your assets.</p>
<p>In development:</p>
<pre><code>$ npm run dev</code></pre>
<p>Or, if you want to monitor your assets folder and automatically recompile when changes are detected:</p>
<pre><code>$ npm run watch</code></pre>
<p>Both of these commands will use Webpack with the <code>NODE_ENV</code> environment variable set to <code>development</code>, which means your assets will not be minified. When you're building for production, you use the following command:</p>
<pre><code>$ npm run production</code></pre>
<p>This will set <code>NODE_ENV</code> to <code>production</code>, which will result in the compiled assets being minified. </p>
<p>Out of the box, Mix doesn't provide a way to combine the production pipeline with a watch command. I find it useful sometimes to minify assets when I am trying to reduce the size of assets by removing unnecessary dependencies towards the end of a project - so I can see the final file size before I ship to production. Thankfully, it's really simple to add a new npm script to handle this scenario.</p>
<p>Open the <code>package.json</code> file in the root of your project. You should see a section <code>scripts</code> that looks as follows:</p>
<pre><code class="language-javascript">"scripts": {
  "dev": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
  "watch": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
  "hot": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
  "production": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
}</code></pre>
<p>You can see exactly what is going on when you run one of these scripts - it's calling Webpack and passing in some flags to determine environment, progress reporting, watching and so on. We want to create something that's basically a hybrid of the <code>watch</code> and <code>production</code> scripts - I'll call it <code>wp</code>. Simply add it to the end of the <code>scripts</code> section:</p>
<pre><code class="language-javascript">"wp": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=production node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"</code></pre>
<p>Save <code>package.json</code> and now run the command:</p>
<pre><code>$ npm run wp</code></pre>
<p>This time, it will compile and minify your assets, but also watch the folder for any changes for automatic recompilation.</p>]]></description>
			</item>
					<item>
				<title>Announcing clob</title>
				<link>https://joelennon.com/announcing-clob</link>
				<guid>https://joelennon.com/announcing-clob</guid>
				<pubDate>Fri, 27 Jan 2017 16:00:00 +0000</pubDate>
				<description><![CDATA[<p>I'm excited to announce a new open source blog platform targeted at software developers. I call it <strong>clob</strong>. I keep getting platform fatigue and constantly bounce between different products when it comes to blogging. As a developer, I love reinventing the wheel, so thought why not create my own? Hence, <a href="https://github.com/theclob/clob">clob</a> was born.</p>
<h2>Why is it called clob?</h2>
<p>A CLOB, or Character Large Object, is a data type in some database systems used to store large amounts of character data. Seeing as how blog posts are typically stored in columns of this type, it seemed fitting.</p>
<p>(Ironically, the databases clob supports don't actually call these data types CLOB)</p>
<h2>What does it do?</h2>
<p>It displays a list of blog posts and when you click on one you can view it. Amazing stuff, I know. It has share links that are a bit more relevant to a developer like Hacker News and Reddit. Oh and they don't include 4 gigs of JavaScript just to show that nobody has liked your post or retweeted it. It displays code listings in a reasonably pretty way.</p>
<p>More importantly for me, it gives me a simple way to manage my blog posts. I like to write posts in Markdown, so I just add it to a big textarea and that's my writing done. It also gives me some options around previewing posts, publishing them at a future date and setting some super magic SEO tags so I appear on the eighth results page for some semi-relevant keyword. Go me!</p>
<h2>What does it look like?</h2>
<p>Erm, you're looking at it right now. Really, this blog post was written in clob. How meta of me, annoucing clob using clob. </p>
<p>You want to see the admin interface? Prepare to be amazed...</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/clob/images/posts.png" alt="No fancypants reactive angular vueified nonsense going on here, my friend!" /></p>
<p>Oh, and my editor is just insane altogether. It's basically a <code>&lt;textarea&gt;</code>.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/clob/images/edit.png" alt="WYSIWYG is for the weak." /></p>
<h2>How do I use it?</h2>
<p>Erm, you're using it right now.</p>
<p>...</p>
<p>Oh, you mean you want to actually use clob for your own blog? Really? Are you sure? There's a far less sarcastic set of instructions on the <a href="https://github.com/theclob/clob/blob/master/readme.md">GitHub repository</a>. You see, I can write like a boring bastard when I need to, too.</p>
<h2>Stop pointing me to GitHub, surely this blog platform has its own website?</h2>
<p>No, actually. That would be just too meta altogether. Given clob's stage of development, I'd probably end up outsourcing development of a clob website to a WordPress developer. </p>
<p>Alright, alright, I'll make a website for clob soon enough. You'll probably find it on <a href="https://clob.io"><a href="https://clob.io">https://clob.io</a></a> sometime in the next few weeks.</p>
<h2>Where do I go to beat you with a giant stick for wasting your time on this crap?</h2>
<p>Twitter would seem to be a suitable medium for such abuse. You can find clob there <a href="https://twitter.com/theclob">@theclob</a>. Alternatively you can go to our echo chamber on IRC - the #clob channel on Freenode. Chances are though that noone is actually there and you'll find yourself having a wonderful conversation with ChanServ. Have fun!</p>]]></description>
			</item>
					<item>
				<title>Homestead issues with NFS on Ubuntu</title>
				<link>https://joelennon.com/homestead-issues-with-nfs-on-ubuntu</link>
				<guid>https://joelennon.com/homestead-issues-with-nfs-on-ubuntu</guid>
				<pubDate>Fri, 27 Jan 2017 12:00:00 +0000</pubDate>
				<description><![CDATA[<p>If you're trying to run Homestead on an Ubuntu Linux host you might get a complaint that your machine does not support NFS when trying to run vagrant up for the first time.</p>
<p>To fix this, you need to install the nfs-common and nfs-kernel-server packages on your host system.</p>
<pre><code class="language-bash">$ sudo apt-get install nfs-common nfs-kernel-server</code></pre>
<p>Once these packages are installed you should no longer get the error about NFS.</p>]]></description>
			</item>
					<item>
				<title>Laravel Tests, SQLite and “no such column” errors</title>
				<link>https://joelennon.com/laravel-tests-sqlite-and-no-such-column-errors</link>
				<guid>https://joelennon.com/laravel-tests-sqlite-and-no-such-column-errors</guid>
				<pubDate>Fri, 27 Jan 2017 09:00:00 +0000</pubDate>
				<description><![CDATA[<p>I ran into a frustrating issue today while trying to run PHPUnit tests on a Laravel project I’m working on. Essentially, I was getting <code>PDOException</code> messages saying things like <em>no such column</em>.  Looking through the stack trace, the issue seemed to have something to do with the <code>Illuminate\Database\Console\Migrations\RollbackCommand</code> class, which suggested it may be an issue with a rollback migration.</p>
<p>After some digging around, I discovered that my rollback migrations where choking when running tests because I was dropping multiple columns at a time, but in separate statements, like as follows:</p>
<pre><code class="language-php">$table-&gt;dropColumn('read_time_minutes');
$table-&gt;dropColumn('word_count');</code></pre>
<p>This works fine in my main MySQL database, but it was causing trouble with my testing SQLite database. To resolve the issue, you need to ensure that these operations happen within the same transaction. An easy way to do this is as follows:</p>
<pre><code class="language-php">$table-&gt;dropColumn(['read_time_minutes', 'word_count']);</code></pre>
<p>And voilà, it works!</p>]]></description>
			</item>
					<item>
				<title>Using Laravel 5.4 Request Sanitization Middleware</title>
				<link>https://joelennon.com/using-laravel-54-request-sanitization-middleware</link>
				<guid>https://joelennon.com/using-laravel-54-request-sanitization-middleware</guid>
				<pubDate>Thu, 26 Jan 2017 12:00:00 +0000</pubDate>
				<description><![CDATA[<p>One of the new features introduced in Laravel 5.4 is a pair of middleware for sanitising data in requests. These middleware are:</p>
<ul>
<li><code>TrimStrings</code></li>
<li><code>ConvertEmptyStringsToNull</code></li>
</ul>
<p>Both of these middleware are very useful. <code>TrimStrings</code> will trim whitespace surrounding any request input values. <code>ConvertEmptyStringsToNull</code> will take any inputs that have been submitted with no value and convert them from an empty text string <code>''</code> to <code>null</code>.</p>
<p>The latter is very useful when you want to ensure that any empty fields are stored in your database as <code>NULL</code> and not empty strings. By doing this at a middleware level, you can ensure your application handles this consistently across the board.</p>
<p>In the past, you may have written something like the following when saving data following a form submit:</p>
<pre><code class="language-php">$post-&gt;published_at = request()-&gt;published_at ?: null;</code></pre>
<p>With this new middleware, you no longer need the <code>?: null</code> bit.</p>
<p>At the time of writing, the guidelines on using these middleware in the Laravel 5.4 release notes doesn’t work when upgrading a Laravel 5.3 application. To use the middleware, you need to add the following lines to the <code>$middleware</code> array in your <code>app\Http\Kernel.php</code> file:</p>
<pre><code class="language-php">\Illuminate\Foundation\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,</code></pre>
<p>One small problem you might run into when you enable the <code>ConvertEmptyStringsToNull</code> middleware is with form validation. If you have validation rules that check the format of a field (such as using the <code>date</code> rule to check for valid dates), but that field is not required, you’ll find that the validation will fail when the field is empty. To fix this, you need to ensure that you add the <code>nullable</code> rule to the field. For example:</p>
<pre><code class="language-php">public function rules()
{
    return [
        'title' =&gt; 'required',
        'published_at' =&gt; 'nullable|date',
        'image_url' =&gt; 'nullable|url',
    ];
}</code></pre>
<p>Now your validation rules will work as expected.</p>]]></description>
			</item>
					<item>
				<title>Using BootForms with Laravel 5.4</title>
				<link>https://joelennon.com/using-bootforms-with-laravel-54</link>
				<guid>https://joelennon.com/using-bootforms-with-laravel-54</guid>
				<pubDate>Wed, 25 Jan 2017 12:00:00 +0000</pubDate>
				<description><![CDATA[<p>Adam Wathan’s <a href="https://github.com/adamwathan/bootforms">BootForms</a> makes it painless to work with Bootstrap forms in your Laravel Blade templates. If you’ve been using BootForms and just upgraded to Laravel 5.4, you might have run into some issues with your views.</p>
<p>There’s an open <a href="https://github.com/adamwathan/bootforms/pull/125">pull request</a> which fixes this issue, so hopefully this post will be irrelevant very soon. Until then, however, the following will help you get BootForms working in your Laravel 5.4 application.</p>
<h2>Extend BootFormsServiceProvider</h2>
<p>The issue is in <code>\AdamWathan\BootForms\BootFormsServiceProvider</code> so we’ll create a new class that extends it and apply the fix there. Laravel 5.4 no longer uses Symfony’s <code>SessionInterface</code> for its session handlers and replaces it with a new <code>\Illuminate\Contracts\Session\Session interface</code>. This change means that any calls to the <code>getToken()</code> method on sessions must be replaced with <code>token()</code>. The <code>registerFormBuilder()</code> method in <code>BootFormsServiceProvider</code> calls <code>getToken()</code> so we need to change that.</p>
<p>First, create a new service provider class called <code>BootFormsServiceProvider</code>. The following Artisan command will scaffold this for you:</p>
<pre><code class="language-bash">$ php artisan make:provider BootFormsServiceProvider</code></pre>
<p>Open the newly created <code>app\Providers\BootFormsServiceProvider.php</code> in your project and change its contents to the following:</p>
<pre><code class="language-php">&lt;?php
namespace App\Providers;
use AdamWathan\Form\FormBuilder;
use AdamWathan\BootForms\BootFormsServiceProvider as OriginalProvider;
class BootFormsServiceProvider extends OriginalProvider
{
    /**
     * Override the registerFormBuilder method to use token()
     * instead of getToken() on new Session interface in Laravel 5.4
     *
     * @return FormBuilder
     */
    protected function registerFormBuilder()
    {
        $this-&gt;app-&gt;singleton('adamwathan.form', function ($app) {
            $formBuilder = new FormBuilder;
            $formBuilder-&gt;setErrorStore($app['adamwathan.form.errorstore']);
            $formBuilder-&gt;setOldInputProvider($app['adamwathan.form.oldinput']);
            $formBuilder-&gt;setToken($app['session.store']-&gt;token());
            return $formBuilder;
        });
    }
}</code></pre>
<p>If you are using a custom namespace for your application, be sure to change it above.</p>
<h2>Register the extended provider with your application</h2>
<p>Now, you just need to register this new provider in <code>config\app.php</code>. Comment out the line loading Adam’s provider (you’ll probably want to swap this back in when Laravel 5.4 support ships in BootForms itself) and load in your new provider class.</p>
<pre><code class="language-php">// AdamWathan\BootForms\BootFormsServiceProvider::class,
App\Providers\BootFormsServiceProvider::class,</code></pre>
<p>Once this change has been made, your BootForms-powered views will work again.</p>]]></description>
			</item>
					<item>
				<title>The real value gap in the music business</title>
				<link>https://joelennon.com/the-real-value-gap-in-the-music-business</link>
				<guid>https://joelennon.com/the-real-value-gap-in-the-music-business</guid>
				<pubDate>Tue, 03 May 2016 12:00:00 +0000</pubDate>
				<description><![CDATA[<p><img src="https://cdn-images-1.medium.com/max/1000/1*PaGl-kx-vD3e4PLZEpPYvQ.jpeg" alt="Photo Credit: Frantzou Fleurine" /></p>
<p>2015 was a landmark year for the music business.</p>
<ul>
<li>Global music revenues up 3.2%, the first increase in well over a decade.</li>
<li>Digital sales surpassed physical sales for the first time in history.</li>
<li>Streaming now accounts for 43% of digital music revenue.</li>
<li>900 million people used ad-support user upload services to access music.</li>
</ul>
<p>Source: <a href="http://www.ifpi.org/news/IFPI-GLOBAL-MUSIC-REPORT-2016">IFPI Global Music Report 2016</a></p>
<p>All of this is good news, but is not cause for complacency. The industry has a long way to go to repair the damage that has been done over the past 15 years.</p>
<h2>The value gap</h2>
<p>In her editorial in the 2016 IFPI Global Music Report, Frances Moore, CEO of IFPI, highlights the value gap in music. She defines the value gap as follows:</p>
<blockquote>
<p>“The value gap is about the gross mismatch between music being enjoyed by consumers and the revenues being returned to the music community.” — Frances Moore, CEO of IFPI</p>
</blockquote>
<p>On the surface, this definition makes a lot of sense. A vast number of people enjoy music every day, but don’t pay anything to do so. It’s probably safe to say that people listen to music because they enjoy doing so, so they are extracting more value than they are contributing back.</p>
<p>The thing is, the value being referred to by Moore is <em>economic value</em>. And the economic value of something is determined by what the consumer is <strong>willing</strong> and <strong>able</strong> to pay for it. And if we’ve learned anything from the years of decline in the industry — when it comes to music, the answer to that for many consumers is <strong>zero</strong>.</p>
<p>You could proffer that the success of paid streaming services like Spotify show that there is a cohort of people who are willing and able to pay for music. But I’d argue that those who pay for Spotify Premium don’t do so for the music (you can access the same music on Spotify Free), they do so for the features and convenience — like the ability to play any track ad-free on a mobile device and save tracks to listen offline.</p>
<h2>Here’s to the policy makers</h2>
<p>The most bewildering point from Moore’s editorial is the notion that the solution to the value gap is to convince the policy makers to change legislation. Are we really going to go and make the same mistakes all over again? Now that we’ve finally started to embrace innovation and technology, we’re going to go back and rely on the European Commission and US Copyright Office to eradicate the “safe harbour” regime? Oh yeah, because legislation and legal enforcement worked <em>so</em> well last time around.</p>
<p>The nature of ad-supported services is that it takes a <em>lot</em> more consumption to generate the same level of revenues as a paid-for service. If we force the issue and these services have to adopt the traditional royalty model it will likely be unsustainable for them to continue to be ad-supported. The reason 900 million people use the likes of YouTube to listen to music is because it is free — both in terms of cost and friction. No payment required, no user account required. Just click and listen. If we take that away, we are essentially driving these consumers back to piracy.</p>
<p>Even if legislation was the solution, I think it’s very unrealistic to expect that this change would come about quickly.</p>
<blockquote>
<p>“The European Commission has identified the problem and acknowledged that a legislative fix is needed. The US Copyright Office has launched a study into safe harbours to determine whether they are fit for purpose. These initiatives will be our industry’s priority focus over the next year.” — Frances Moore, CEO of IFPI</p>
</blockquote>
<p>The problem is that “acknowledgement” and “launched a study” indicates that neither organisation are anywhere close to formalising anything on this. So while the industry is busy focusing on legislation that may be years away, we’re likely to see everything around it evolve and change even further. The industry simply can’t afford to drop the ball again.</p>
<h2>The “real” value gap</h2>
<p>There is a value gap in the music business — but we’re looking for it in the wrong place. The crux of the problem is that we keep focusing on the <em>general music consumer</em> — basically everyone who listens to music. This includes casual listeners, fans, super fans and everyone in between. The products in today’s market are targeted at everyone, when the majority of revenue in the industry comes from a much smaller segment of the market — fans.</p>
<p>A <a href="http://www.nielsen.com/us/en/insights/news/2013/turn-it-up--music-fans-could-spend-up-to--2-6b-more-annually.html">Nielsen report in 2013</a> found that 40% of music consumers can be categorised as fans, and these fans account for 75% of all spend in the industry. The same report concluded that the industry is missing out on up to $2.6 billion a year in the United States alone by not servicing this segment of the market with the products they desire. A perhaps even more staggering insight is that a subgroup of fans, <em>aficionados</em>, make up 14% of music consumer population but account for a whopping 34% of spend. <a href="http://www.slideshare.net/musicbizassoc/mi-di-a">Mark Mulligan of MIDiA Research</a> believes the percentage of total spend by super fans to be as high as 61%.</p>
<p>The real value gap in the music industry is caused by the fact that we’re constantly chasing the wrong money by catering for the general music consumer, when we should be focusing on fans.</p>
<p>Fans are still willing to pay for music — the success of services like Bandcamp, Patreon and PledgeMusic are evidence enough of that. But the missing link for fans is in modern digital offerings that provide a deeper connection to the artist, a higher level of engagement and more unique products that offer an experience that is unique to each fan. That is the real value gap in music, and the sooner we shift to focus on fans, the sooner we’ll see serious growth in music again.</p>]]></description>
			</item>
					<item>
				<title>Startup Founder Required</title>
				<link>https://joelennon.com/startup-founder-required</link>
				<guid>https://joelennon.com/startup-founder-required</guid>
				<pubDate>Fri, 29 Apr 2016 12:00:00 +0000</pubDate>
				<description><![CDATA[<p>ABC Startup Company is looking for an experienced founder to lead their company’s initial growth and development, driving our success through to achieving a minimum of US$1 billion in annual revenue within five years.</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*A1EtMqt2zvQbsGrqPprDYw.jpeg" alt="Photo courtesy Snappa.io" /></p>
<p>The successful candidate will (in no particular order):</p>
<ul>
<li>Have a proven track record of success that gives confidence to investors that we will achieve our vision</li>
<li>Be responsible for setting the overall vision and strategy for the company</li>
<li>Define the product, design, user interface, user experience and requirements for all functionality across the product range</li>
<li>Perform market research and validation to ensure that we are building something people actually want</li>
<li>Set the pricing of your products, and inevitably deal with the challenges of changing that pricing, multiple times</li>
<li>Establish the company’s brand and message</li>
<li>Design, develop and publish the company’s corporate website</li>
<li>Drive inbound marketing and leads through blogs, social media and other content</li>
<li>Understand how to use Mailchimp to build mailing lists and manage email marketing campaigns</li>
<li>Run advertising campaigns on Google Adwords, Facebook Ads, Twitter Ads and various other online platforms</li>
<li>Run advertising campaigns in industry publications, newsletters, email bulletins, aggregators and more</li>
<li>Track campaign performance in Google Analytics, Mixpanel and other tools. Run retargeting campaigns and upsell campaigns to established customer.</li>
<li>Create engaging PR campaigns that gets the company coverage in industry press, and also the general press/paper of record we have visibility to investors</li>
<li>Evangelise the business at events, speaking at tech conferences and establishing us as thought leaders in our industry</li>
<li>Be extremely comfortable pitching our company to investors, sales prospects, startup accelerators, startup pre-accelerators, mentors, partners, customers and random people you meet</li>
<li>Sell the company through direct and indirect approaches. Be comfortable sending cold emails and picking up the phone, “reaching out” to people on Facebook Messenger or whatever the flavour of the month platform is</li>
<li>Spend hours scouring the Web and contact books to figure out leads into potential investors and customers</li>
<li>Jump on a plane and travel to places on a whim to close deals</li>
<li>Recruit out a team of superstar executives to be your COO, CTO, CFO, SVP of Sales and Marketing — you need to be especially good at this, as you’ll be promising them low salaries and long hours</li>
<li>Have plenty of time to search for, screen and interview candidates</li>
<li>Establish training and onboarding plans for new hires</li>
<li>Look after all HR — payroll, expenses, travel, working with recruiters, staff grievances, payrise requests</li>
<li>Constantly review employee performance, have tough conversations with underperformers and fire people if necessary</li>
<li>Understand the concepts of each core function of your business so you can effectively hire people in the first place</li>
<li>Be able to spot the bullshit when recruiting sales people — it turns out the sales people happen to be very good at selling themselves…</li>
<li>Define the initial product requirements, then own and manage the product roadmap</li>
<li>Be a rock-star developer who is a front-end ninja, but also has rock-solid back-end skills</li>
<li>Understand databases, in-memory caching, CDNs, several hundred AWS products and how to use them</li>
<li>Know the importance of high scalability and availability, and design solutions that can handle sudden spikes in growth</li>
<li>Figure out ways of getting enough AWS credits to keep hosting costs as close to zero for the first year or two</li>
<li>Be an accountant, with solid knowledge of bookkeeping, financial reporting and tax</li>
<li>Able to read lengthy EU documents telling you why you need to be able to charge about 100 different VAT rates depending on where your customer is</li>
<li>Be able to put together a slick 10 slide investor deck and then predict what order each individual person you pitch to will want you to present it in</li>
<li>Understand the difference between angel, seed and series A/B/C/D/E investment</li>
<li>Know who the decision makers are in VCs and customers</li>
<li>Understand how dilution and shares work, what an RSU is, why stock options are beneficial and when you’ll need an ESOP program for your staff</li>
<li>Have a solid exit strategy that makes you a valuable proposition to investors</li>
<li>Constantly flip-flop between investor-mode and customer-mode, the investor wants to hear completely different things to your customer</li>
<li>Know your LTV from your CAC from your ARR and your Churn</li>
<li>Differentiate between good and bad advice, but be nice when you’re talking to someone who clearly doesn’t know their ass from their elbow</li>
<li>Build a customer service knowledgebase and documentation library — preferably in your spare time, as you’ll be too busy to do this any other time</li>
<li>Set up a system to allow customers request help in a simple and efficient way</li>
<li>Answer customer queries, usually in the middle of the night on a weekend just after you’ve crawled into bed after a long day</li>
<li>Take on the chin anytime some random person uses your customer support system to tell you how overpriced your incredibly inexpensive product is</li>
</ul>
<p>The above is just a taster. You should basically be able to do anything, anytime, with little to no training or prior firsthand experience, and then be able to teach someone else how to do it effectively by the end of the day so you can do something else totally new tomorrow.</p>
<p>In return, you will benefit from:</p>
<ul>
<li>A highly competitive compensation package of €0 until we initial raise investment</li>
<li>No perks of any kind — you’re the founder, the unlimited vacations are for the rockstars you need to hire</li>
<li>100% shareholding of the business, although this will inevitably be diluted away each time we raise money</li>
<li>Health insurance, paid directly by yourself from your own savings — don’t skimp on this, you’ll probably need it soon</li>
<li>Increased time spent working by reducing the amount of time you waste each day sleeping and doing other non-productive tasks like eating meals without a laptop in front of you and enjoying time with your family and friends</li>
<li>A rollercoaster lifestyle, with thrilling highs and monumental slumps where you feel on the brink of doom</li>
<li>A corporate credit card that will routinely fail due to being constantly maxed out</li>
<li>Sleeping on couches or floors when you travel. It is a rite of passage to being a member of the founder elite</li>
</ul>
<p>ABC Startup Company is an equal opportunities employer. Please send all applications to careers@example.com. Agencies welcome — after all, 15–20% of $0 is $0.</p>]]></description>
			</item>
					<item>
				<title>Launching a startup in Cork</title>
				<link>https://joelennon.com/launching-a-startup-in-cork</link>
				<guid>https://joelennon.com/launching-a-startup-in-cork</guid>
				<pubDate>Thu, 14 Apr 2016 12:00:00 +0000</pubDate>
				<description><![CDATA[<p><img src="https://cdn-images-1.medium.com/max/1400/1*doBCSWcK3dxyJnnJ9xPSDQ.jpeg" alt="CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=81308" /></p>
<p>Last night I was on a panel for the first <a href="http://www.builtincork.com/">Built in Cork</a> event of 2016, alongside <a href="https://twitter.com/siliconbarry">Barry O’Sullivan</a> (CEO <a href="https://www.altocloud.com/">Altocloud</a>, Dragon’s Den), <a href="https://twitter.com/NiamhBushnell">Niamh Bushnell</a> (<a href="http://startupdublin.com/">Dublin Startup Commissioner</a>) and <a href="https://twitter.com/M_OToole">Mark O’Toole</a> (<a href="http://www.beachhutpr.com/">Beachhut PR</a>). As usual, it was hosted by the venerable <a href="https://twitter.com/donal_cahalane">DC Cahalane</a>, unofficial Cork Startup Commissioner (he’ll kill me for saying that). The theme of the event was a SWOT analysis on Cork as a place to launch a startup. It was great to see such a good turnout, and eventually the audience got over the usual Cork shyness and asked some good questions!</p>
<p>Panel discussions are great because they focus on engaging with the audience, but sometimes it can be hard to say everything you would like to, especially when you are sharing the stage with such esteemed and insightful people. Prior to the event, I spent quite a bit of time reflecting on launching a startup in Cork, why we decided to do it and the highs and lows we’ve come across in the process. In this post, I’ll delve a little deeper into what it’s like to do it from Cork.</p>
<h2>Why did we decide to start in Cork?</h2>
<p>When you start a business, it’s important to be able to get the operation off the ground. For us at <a href="https://subwoofr.com/">Subwoofr</a>, this meant raising a small angel round, building a beta of our product and engaging with our target market. Because both myself and my co-founder, Simon, have strong connections and networks here in Cork, we were able to leverage those to get off the ground fast.</p>
<p><img src="https://cdn-images-1.medium.com/max/1400/1*gv8t_i8PQyJjezv3JVrx_w.jpeg" alt="We launched a beta of Subwoofr just two months after starting the business. For us, hitting the market early was key to getting early adoption and validation of our idea, product and vision." /></p>
<p>We started the business in September, launched our beta at the end of October, closed our angel round in December, went live with our first post-beta artist in January and started earning revenue in February. I firmly believe we’ve been able to do achieve all of these things so quickly because we stayed in Cork.</p>
<p>We gave a lot of thought to location when we started out. Ireland is a tiny market in the music industry. The UK market is 33x the size of Ireland’s, in spite of their population being just 13x ours. The US market is 119x. (Source: <a href="http://www.ifpi.org/recording-industry-in-numbers.php">IFPI</a>) Suffice to say, the vast majority of our customers are ultimately not going to be in Ireland. We also considered moving on the basis of access to investment and the incentives for startups. It’s <a href="https://medium.com/the-lucey-fund/why-ireland-will-never-be-a-relevant-startup-hub-a8f74d83f217#.i9ulseilq">no secret</a> that the UK is far ahead of us when it comes to areas such as startup investment tax relief and capital gains.</p>
<p>Ultimately, we decided to stay because we knew that we would be able to iterate faster here in the early stages of our growth, and we can easily get to anywhere we need to from Cork. I can be in the centre of London in 2 hours. That’s faster than many people in England can get there. Dublin is just 2.5 hours by car or train. And we can get to pretty much anywhere we need to in the US or indeed the world with a single stopover in Heathrow or Amsterdam.</p>
<h2>Supports available in Cork</h2>
<p><img src="https://cdn-images-1.medium.com/max/600/1*pwCXs25I1VpF8jD5lQQuCA.jpeg" alt="The Cork LEO offer a range of financial and non-financial support to startups. They should be your first port of call when you start a business in Cork, and they are the gateway to EI." /></p>
<p>Financially, we’ve gotten assistance from the <a href="https://www.localenterprise.ie/CorkCity/">Cork Local Enterprise Office</a> which helps pay the salaries and cover part of some of our expenses. We’re very grateful for it, but if you’re thinking of starting up just make sure you are aware that it is issued on a receipt-and-claim basis, so you need to incur the cost first and then you get to claim back up part of some costs, like salaries and marketing expenses.</p>
<p>Seed funding and Enterprise Ireland support is nowhere near as easy to secure as many people make it out to be. If you think you’ll easily raise a seed round and get EI to match fund it you’re dreaming. It’s nowhere as simple as that — you need to prove a lot before you’ll get either seed funding or EI backing.</p>
<p>Both myself and Simon have strong personal networks that we were able to leverage to raise our angel round. That’s one thing I’d definitely say to people considering starting a business — never underestimate your own personal network. Former employers, co-workers, friends, friends-of-friends, family — when you’re starting out, especially in Ireland, these are the most likely sources of financial support you’re going to find.</p>
<h2>What about non-financial supports?</h2>
<p>Through Cork LEO we were assigned a mentor. I was skeptical at first about the whole mentor thing, but we’ve been blessed with a great mentor in <a href="https://www.linkedin.com/in/hanniganfrank">Frank Hannigan</a>. He’s on the board at RTE, was previously at Hot Press and has a wealth of knowledge and connections in media. Aside from all that, however, Frank has been great at helping us stay focused on our overall vision and ambition. When you launch a startup, especially one with grand, global ambitions, it can be very tempting to get sidetracked by small opportunities to keep yourself afloat. In some cases, you might have to take these, but they can be extremely distracting and set you completely off course from your vision and strategy. Frank has been brilliant at getting us to visualise and focus on our primary mission and ensure that we’re always striving to get there.</p>
<p>We’ve also been on the first phase of the <a href="http://www.newfrontiers.ie/">New Frontiers</a> program, and you have other programs available like <a href="http://www.ucc.ie/en/ignite/">IGNITE</a> in UCC, and the <a href="http://www.ludgate.ie/#home">Ludgate Hub</a> in Skibbereen where you can get office space and seed funding.</p>
<h2>The struggles</h2>
<p>This is definitely not limited to Cork, but access to investors isn’t great here. There’s only a handful of VCs here, and a lot of private investors here are not experienced investing in startups. For example, as a startup, our preference is to avoid putting a valuation on our company right now, as we are still early stage. Too low a valuation and we limit what we can raise without giving up too much equity, too high a valuation and we make it more difficult to raise down the line. Most savvy seed investors realise this, and are happy to invest using a convertible note for a discount or valuation cap on the next round — but we’ve found that inexperienced private investors tend to want straight equity, forcing a valuation.</p>
<p>We’ve heard from different people that raising seed money in Ireland is easy, but raising later stages here is difficult as the money just isn’t there. But we haven’t found seed money easy to come by at all. When we started off with a vision and plan, we were told we to come back when we had a product. When we had a product we were told to come back when we had revenue. When we started earning revenue we were told to come back when we had growth and traction. But the thing is, when we have growth and traction, we won’t really need seed investment any more, we’ll be closing in on series A country in terms of the funding needed.</p>
<p>I think one of the things that’s hurt us in this regard is that we’ve actually progressed too quickly with regards to reaching major milestones. We’ve always been one step away from the next rung on the ladder, investors have seen this and opted to wait to see how we get on with that next step. The problem with that of course is that we can only get so far before the next rung requires significant investment, and the danger is that it will come too late.</p>
<h2>The Cork startup community</h2>
<p>If I’m being totally honest, we’ve tried to stay away from the startup <em>industry</em> as much as possible to date. I’ve seen plenty of startups who have spent more time talking to other startups and networking with their peers than focusing on building a product and talking to their customers and targets. We’ve preferred to keep our heads down, build our product, validate the market and start earning revenue. We have had great support, advice and guidance from many people — including DC Cahalane, the likes of <a href="https://twitter.com/connorpm?lang=en">Connor Murphy</a>, <a href="https://twitter.com/chriskennedy">Chris Kennedy</a> of <a href="http://www.trustev.com/">Trustev</a> and <a href="https://twitter.com/garethcuddy">Gareth Cuddy</a> of <a href="https://www.vearsa.com/">Vearsa</a>. There are plenty of great success stories in Cork to get inspired by — <a href="https://www.teamwork.com/">Teamwork</a>, <a href="https://www.newsweaver.com/">Newsweaver</a>, <a href="https://www.sophos.com/en-us.aspx#">Barricade</a>, <a href="http://www.xanaduconsultancy.com/">Xanadu</a> to name but a few.</p>
<p><img src="https://cdn-images-1.medium.com/max/1200/1*3nDUb9c6LRtm0IRIynp9pA.png" alt="Source: https://www.startupgathering.ie/2015/09/30/become-a-part-of-corks-startup-ecosystem/" /></p>
<p>This strategy has worked well for us in many ways, but I do think we’ve probably been too disconnected from the community, and we are starting to engage more now. We’re currently on the <a href="http://www.up.co/communities/ireland/dublin/startup-next/8651">Startup Next</a> program in Dublin, and will be present at most startup events in the coming months. The community in Cork is definitely quite small and limited though, but events like Built in Cork and the <a href="http://www.itcork.ie/event/european-tech-summit-2016/">European Tech Summit</a> in May show that there’s a lot of potential for a very strong community here.</p>
<p><img src="https://cdn-images-1.medium.com/max/600/1*p1vauQVWuYu5uK15LBlKYA.png" alt="Cork’s startup community is relatively small, but there are many related communities that you should seek out and be a part of. You’ll find plenty on Meetup.com." /></p>
<p>I made the point last night and I’ll reiterate it here — there are plenty of communities in Cork related to startups, particularly software startups. A lot of founders ask me to recommend them a potential technical co-founder or CTO, which is a bit like asking me to find you someone to marry. You’re much better off going to any of the multitude of tech meetups around Cork, like <a href="http://corkdev.io/">Corkdev.io</a>, <a href="https://www.meetup.com/CorkSec/">Corksec</a>, Alt.net, the Cork PHP user group meetups and the likes. They’re usually focused on technical topics — but that’s where the tech heads in Cork go, so that’s where you’ll find them!</p>
<h2>Should you start in Cork?</h2>
<p>I’m going to sit on the fence on this one and say that it completely depends on your business and situation. If you’re starting a tech company with a global market, you can probably start your business wherever you like. If your market is located in specific places, then you should go there. We will need to expand to the big music countries — the US, UK, Germany and Japan very soon — Ireland has been great for testing the market, but it is tiny in the greater scheme of things.</p>
<p>I think it’s important not to underestimate the importance of your own personal circumstances and personal life when starting a business. If you have a family or are in a relationship, make sure you have your priorities in order. You can work hard and work smart, ensuring that you still have time for your personal life. If you’re miserable in your personal life, your work life will suffer — so don’t go doing anything rash or you’ll risk ruining both.</p>
<p>If you’re young, free and single and have plans for global domination I would definitely advise considering a move though, and probably heading to the US if you can. You’ll have a great personal experience, you’ll probably find it much easier to raise more money for your startup, and you’ll have bigger wins and failures to learn from. And like most people, you’ll probably end up back in Cork in the future anyway, <em>like</em>.</p>
<p>I’m not one for looking back and having regrets, but if I had taken a few more risks when I was younger (I’m 30 now), I might be telling a much different story in this post. There is never a good time to start a business, you can’t focus on <em>if I wait for…</em> or <em>when I have…</em> moments — they don’t happen in reality. Life will always change, and whether it’s a mortgage, getting married, having kids or something else, there will always be potential roadblocks or reasons why you <em>shouldn’t</em> start a business. But the thing is, none of those things matter. If you believe in something strongly enough, you’ll do it regardless of what’s in your way.</p>]]></description>
			</item>
					<item>
				<title>Never a good time</title>
				<link>https://joelennon.com/never-a-good-time</link>
				<guid>https://joelennon.com/never-a-good-time</guid>
				<pubDate>Tue, 18 Aug 2015 00:00:00 +0000</pubDate>
				<description><![CDATA[<p>There is a good time to take risks in your career. But it’s not when you might expect.
In my early career, I had several opportunities to move from my reliable, pensionable employment into riskier roles in startups and rapid-growth companies. Invariably, I declined all of these opportunities.</p>
<p>Looking back, the success-to-failure ratio of these opportunities is about 1:1. Some of them completely tanked, others went on to become major successes (including one that had an almost 9-figure exit). The one constant is that the people involved have all gone on to success since. Every single one of them.</p>
<p>In April 2014, I left my role as Head of Technology for CoreHR, a position I had grafted for almost seven years to attain. I left Core to join Vearsa (previously ePubDirect) as their CTO. It was my first real taste of risk. What made me finally take the plunge? Two things:</p>
<ol>
<li>They had a great team. Gareth Cuddy is an inspiring founder, Aiveen Hyland is an impressive COO and they had a wealth of experience across the board in finance, sales and marketing.</li>
<li>I knew I wanted to start my own business soon, but I wanted a taste of a volatile startup before I made the leap. Vearsa was a challenging environment, and it was the ultimate preparation for launching my own company.</li>
</ol>
<p>My timing was seen by some as borderline lunacy. My first child, my daughter Lily, was born in February of that year. I took a 25% paycut for the role, and would get a tiny amount of equity should I stay longer than a year (which I didn’t).</p>
<p>Fast-forward to February 2015, and I handed in my notice at Vearsa, having decided that now was the time to do my own thing. In April 2015, I started Array Software, a consulting company. Vearsa were my first customer, and I had enough work to keep me busy until August from the start.</p>
<p>My second child, my son Jamie, was born in June 2015. In August, I finally decided to stop doing consulting work and go full-time on my startup, Subwoofr. I managed to convince Simon Cogan, my co-founder, to join me. Again, people thought I was completely insane (I’m pretty sure it was the same for Simon). Two kids under two years of age, zero income and now I’m not even doing consulting work anymore? Did I fall and hit my head?</p>
<p>Maybe I am completely mad, but for me there is never a better time to take a risk than when everything depends on it being successful. If I had a cushion to fall on, an easy plan B, a fallback strategy that would allow me to fail gracefully — this would lead to me having less drive to succeed than I do right now. I need to win, I don’t have a choice. I need Subwoofr to work. My family’s future depends on it. I’m not a millenial or twenty-something who can just move on to the next thing if this opportunity doesn’t work out. If this doesn’t work, the dream is over and I’m back looking to work for someone else. What better motivator for success is there than that?</p>
<p>So for me, there is a good time to risk. And that’s the time that 99% of people think is the worst time to take a risk. The time when failure is not an option.</p>]]></description>
			</item>
					<item>
				<title>A tough job interview</title>
				<link>https://joelennon.com/a-tough-job-interview</link>
				<guid>https://joelennon.com/a-tough-job-interview</guid>
				<pubDate>Wed, 29 Oct 2014 00:00:00 +0000</pubDate>
				<description><![CDATA[<p><img src="https://cdn-images-1.medium.com/max/1400/1*tZA4jIha3_Muf4F49_egXA.jpeg" alt="" /></p>
<p>A few years ago, I was contacted by someone in Gilt Groupe about a potential role in their front-end engineering team in Dublin. At the time I was happy in my Product Manager role in Core and was routinely ignoring similar emails from recruitment agencies and other companies. I also generally had no interest in living in exile in Dublin — I’m from Cork, and us Corkonians generally don’t want to become <a href="https://en.wikipedia.org/wiki/Jackeen">jackeens</a> so avoid moving there like the plague.</p>
<p>With the Gilt role, however, I was intrigued. There was a potential future move to their HQ in New York, which was something that I found very tempting. I also liked the company — a very well funded startup with big ambitions. Their sites were pretty fantastic too, so it seemed like it could potentially be a good move. I responded favourably, sent over my CV (which I rushed to update after it had spent several years gathering dust) and a short time later I was called for interview.</p>
<p>I took the day off work and travelled the 160 miles to Dublin. I arrived early so went for a stroll around the area — Starbucks around the corner, loads of parking nearby, very close to the heart of the city — <em>I could work here</em>, I thought. I focused my thoughts and went in, greeted by Gilt’s HR lead, who brought me to a small meeting room and talked me through the agenda for the day.</p>
<p>It was at this point I started to feel a bit out of my depth. I hadn’t interviewed for a job in years, and I didn’t really know what to expect. All of my previous interviews (except an awful one with Oxford University) had been pretty straightforward, and I was surpised to hear that I would have four further meetings that day:</p>
<ol>
<li>With Gilt’s VP of Engineering, who was based in Dublin.</li>
<li>A technical interview with one of their senior front-end engineers.</li>
<li>With their CTO and another technical lead who were over from New York for the day.</li>
<li>With HR to cover some of the basics, go over my background and that kind of thing.</li>
</ol>
<p>The day was gruelling and exhausting. I think the VP of Eng liked me, although when I told him I had worked with Oracle and PL/SQL he asked me to whiteboard code some advanced joins. I completely blanked and struggled to come up with a solution. The technical interview was a mixed-bag — basic stuff like client-side performance and using sprites, but then some bizarre CSS questions that I can’t quite remember the detail behind. At one point I asked the interviewer if he would have known the answer to one of his questions prior to joining Gilt, and he said no.</p>
<p>The meeting with the CTO was a disaster. I remember talking about the <a href="https://liftweb.net/">Lift</a> framework, on which I had recently written an article for IBM developerWorks. Their CTO was seriously impressive, and went into great detail about what he didn’t like about Lift, and I was lost. I think the other guy in this meeting liked me, but one of his only questions had something to do with Linked Lists, and at this stage I was almost ready to give up. I don’t cry often, and I have never done so in a professional situation (and hopefully never will), but at one point during this meeting I had to fight hard to stop myself from doing so. I was completely mentally and emotionally drained.</p>
<p>The HR meeting was a piece of cake compared to what I had just been through, and after it the VP Eng asked me if I would join him for lunch. I had expected the interview to last an hour, maybe two max, so at this point I just wanted to get in my car, drive to Cork and forget that Dublin even existed. I initially said no and came up with some made-up excuse about needing to get home, but then managed to get my shit together and said that I would be delighted to.</p>
<p>At lunch we chatted a bit, and he asked me how much I wanted to work at Gilt. I was genuinely upset at myself for how badly the interview had gone — and I pushed back and sold myself to him. I said that I was gutted at how the interview had gone, nerves had gotten the better of me and that I was much, much better than the crap I had showed over the previous few hours. I said I wish I could prove to him how capable I was. He told me a story about how he loves Sudoku, and that if I could go home, build a solver in JavaScript (no jQuery or frameworks allowed) and send it to him that night, he would be really impressed and we might take things further.</p>
<p>I jumped at the lifeline, and despite the exhaustion I drove home, told Jill (my then girlfriend and now wife) how badly it had gone but that I had a chance to prove myself. I had played Sudoku before but had never attempted to write a solver. I hacked on the project for the night, and came up with something that I hoped would show that I can deliver when pushed. The <a href="https://github.com/joelennon/sudoku.js">original solver I wrote</a> is still available, unchanged from that night, on GitHub. It’s far from perfect, but I was happy with it, especially given the time constraints.</p>
<p>The next day, the VP Eng acknowleged that he had received my mail and would look over my work. Within a few days I got a call to say that he was impressed and wanted me to talk to two of their New York engineers. Yes, two more technical interviews. At first I was disappointed, as I had hoped hacking together the project would have been enough to prove myself. But I pulled up my socks and got prepared.</p>
<p>The first call went really well. The guy was really friendly, and asked me a ton of stuff about JavaScript — closures, XHR, scope, that kind of thing. I answered confidently and was happy with how it went. The second call was tougher — the guy was in a coffee shop so the connection wasn’t great, and he basically went through my Sudoku solver code with a fine comb. In fairness everywhere he found fault he gave me the opportunity to demonstrate how I might improve my solution. Overall I thought it went pretty well, and I was hopeful about a potential offer.</p>
<p>A week or so went past before I heard anything further. Eventually the email from Gilt came. It wasn’t good news. Apparently a hiring freeze had been put in place, so they couldn’t offer me the role. I was assured that I was the preferred candidate, but that they wouldn’t be making any offers at this time. I was absolutely gutted, and to be honest I was quite pissed off about it. I had given up the bones of two days of my time to prove my worth, and they didn’t even have the go ahead to hire someone in the first place? At first, I thought that I would have preferred if they had told me I wasn’t good enough, at least then I could chalk it down to experience — but in truth I was satisfied that they at least <em>said</em> that they wanted to hire me — it was a morale boost, if nothing else.</p>
<p>A couple of months later I received another call from Gilt. The hiring freeze had been lifted and they were ready to start the selection process again. At this point, the opportunity no longer appealed to me — I had been promoted to Head of Technology at Core, I was engaged to Jill, and I had put the whole idea of a move to bed. Besides, the last thing I wanted was to go through the pain of their recruitment process again. I said I was no longer interested but wished them well in their search.</p>
<p>I learned a lot from the interview process at Gilt. The main thing I learned was to be humble when the shoe was on the other foot. Every time I interview a developer, I think back to the Gilt process and remember just how tough it was, and then try to give the candidate every opportunity to prove themselves, without destroying their soul in the process. It’s also the primary reason I will never ask a candidate to code on a whiteboard — if you want someone to write code for you, give them a problem to work on, a computer to work with, some room to breathe and leave them at it.</p>]]></description>
			</item>
			</channel>
</rss>