Douglas F ShearerSoftware developer, mountainbiker.
http://douglasfshearer.com/
Fri, 24 Nov 2017 18:35:34 +0000Fri, 24 Nov 2017 18:35:34 +0000Jekyll v2.5.3A Look Through the Changelog.com Source Code<p><a href="https://changelog.com/">Changelog</a> recently open sourced the <a href="http://www.phoenixframework.org">Phoenix</a> application that provides the backend for their website and podcast publishing platform. They published an <a href="https://changelog.com/posts/changelog-is-open-source">announcement post</a> along with the <a href="https://github.com/thechangelog/changelog.com">source code on Github</a>.</p>
<p>This was a good oppurtunity for me to take a look at someone else’s Phoenix application, to see how they do things. Below you can find the notes I made while looking through the source.</p>
<h3 id="the-basics">The Basics</h3>
<ul>
<li>Phoenix 1.2</li>
<li>Elixir 1.3</li>
<li>Ecto 3.0</li>
<li>Webpack 2.0</li>
</ul>
<h3 id="dependencies">Dependencies</h3>
<p>The following dependencies were ones I didn’t recognise or found interesting:</p>
<ul>
<li><a href="https://github.com/bitwalker/timex">timex</a> - Full-featured Elixir Time Library</li>
<li><a href="https://github.com/thoughtbot/ex_machina">ex_machina</a> - Test data generator</li>
<li><a href="https://github.com/drewolson/scrivener">scrivener</a> - Pagination</li>
<li><a href="https://github.com/asaaki/cmark.ex">cmark</a> - Markdown rendering</li>
<li><a href="https://github.com/stavro/arc">arc</a> - File upload</li>
<li><a href="https://github.com/alco/hashids-elixir">hashids</a> - Reversible non-numerical IDs</li>
<li><a href="https://github.com/thoughtbot/bamboo">bamboo</a> - Transactional email</li>
<li><a href="https://github.com/gjaldon/ecto_enum">ecto_enum</a> - Enums for Ecto models</li>
<li><a href="https://github.com/sasa1977/con_cache">con_cache</a> - Key-Value store</li>
<li><a href="https://github.com/rrrene/credo">credo</a> - Elixir Static Analysis</li>
</ul>
<h3 id="routes">Routes</h3>
<ul>
<li>Separate <code>admin</code> and <code>public</code> pipelines.</li>
<li><code>admin</code> pipeline sets layout, requires admin.</li>
<li><code>auth</code> pipeline looks for current user.</li>
<li><code>public</code> pipeline uses <code>LoadPodcasts</code> plug to pre-load podcasts.</li>
<li>Most paths are served by <code>PageController</code>, which loads a template based on the action name.</li>
<li><code>/weekly</code> and <code>/nightly</code> routes are for mailing list subscriptions.</li>
<li>Sub-paths for subscribe, confirm, unsubscribe, etc.</li>
</ul>
<h3 id="lib">Lib</h3>
<ul>
<li><code>ConCache</code> is a worker child of the ChangeLog application.</li>
<li><code>/uploads</code> directory is setup to be served statically for dev only in <code>Endpoint</code>.</li>
<li><code>/lib/changelog/enums.ex</code> defines a custom column type for the podcast status. This could be used in multiple models.</li>
<li><code>/lib/changelog/factory.ex</code> is a collection of factories for the various models, using <code>ex_machina</code>.</li>
<li><code>/lib/changelog/hashid.ex</code> sets up the encode/decode for the hashed IDs. Salt also set here.</li>
<li>Commonly used regular expressions are defined in a <code>Regexp</code> module.</li>
<li><code>Scrivener</code> default page size is setup in <code>Repo</code>.</li>
<li>Page <code>&lt;title&gt;</code> set via <code>Meta.Title</code> and <code>Meta.AdminTitle</code>. Imported in <code>LayoutView</code>.</li>
<li>Other metadata (twitter, images, feeds, description) set by additional Meta modules.</li>
<li>craisin is the name of the application that handles Campaign Monitor access. Is based on HTTPoison with multiple resources as sub-modules. All mailing list alterations done over API, no local DB records for this.</li>
</ul>
<h3 id="models">Models</h3>
<ul>
<li>Channels are not currently exposed in the public interface. They are playlists for episodes from different podcasts.</li>
<li>Not all the models are <code>Ecto</code> backed. <code>Newsletter</code> for example.</li>
<li>Use of <code>Ecto.get_change</code> to delete records where the <code>delete</code> key has been set in a changeset. Pattern is shown in <code>Ecto.Changeset</code> <a href="https://hexdocs.pm/ecto/Ecto.Changeset.html#module-on-replace">documentation</a>. Uses a virtual delete field to remove nested records. Untested. (1)</li>
<li><code>Episode.with_numbered_slug</code> is used to scope episodes using a regex.</li>
<li>Episode <code>slug</code> is a string. Episodes with numeric-only slugs are “numbered episodes”. Episodes with any non-numeric characters are bonus content.</li>
<li><code>Episode.extract_duration_seconds</code> uses <a href="https://ffmpeg.org">FFMPEG</a> via <code>System.cmd</code> to get the episode duration.</li>
</ul>
<h3 id="controllers">Controllers</h3>
<ul>
<li>There is a lot of application logic in the controllers. (2)</li>
<li><code>scrub_params</code> is used to covert empty strings in changeset values to nil.</li>
<li>Slack integration for listing upcoming episodes with a countdown.</li>
</ul>
<h3 id="helpers">Helpers</h3>
<ul>
<li><code>ViewHelpers.no_widowed_words</code> prevents an overflowed string from only having a single word on the final line. Inserts a <code>&amp;nbsp;</code> between the last two words.</li>
<li><code>Viewhelpers.tweet_url</code> has multiple <code>defs</code> for <code>tweet_url</code>, with a default AND a guard. Header w. default, header w. guard, other headers as usual.</li>
</ul>
<h3 id="file-uploads">File Uploads</h3>
<ul>
<li>Uses <a href="https://github.com/stavro/arc">Arc</a>.</li>
<li>Uploadables defined in web/uploaders/</li>
<li>Arc provides a way to run transformations using system binaries, without the user invoking <code>System.run</code> directly.</li>
<li><code>cast_attachments</code> is used in model changesets to handle the attachment.</li>
</ul>
<h3 id="viewstemplates">Views/Templates</h3>
<ul>
<li>Nested models supported using <code>inputs_for</code>. Models handle these fields with <code>cast_assoc</code>.</li>
</ul>
<h3 id="javascript--css">Javascript &amp; CSS</h3>
<ul>
<li>Switched from Brunch to Webpack in <a href="https://github.com/thechangelog/changelog.com/commit/935b7c5">935b7c5</a>.</li>
<li><a href="https://github.com/turbolinks/turbolinks">Turbolinks</a> to do page transitions. Fast page loads, and additionally allows player to keep playing between page loads? (3)</li>
<li><a href="http://semantic-ui.com">Semantic UI</a> for admin area CSS framework.</li>
<li>Separate assets pipelines for main site and admin area.</li>
<li>CSS is <a href="https://sass-lang.com">SASS</a></li>
<li>Javascript is <a href="http://www.ecma-international.org/ecma-262/6.0/">ECMA 2015 (ES6.0)</a>.</li>
<li>Uses <a href="http://umbrellajs.com">Umbrella.js</a> for DOM/events/AJAX</li>
<li>ES6.0 JS classes are used to describe domain objects and encapsulate their functionality. Episode and Player for example.</li>
</ul>
<h3 id="search">Search</h3>
<ul>
<li>Admin-only at present.</li>
<li>Uses <code>LIKE</code> SQL queries.</li>
<li>Multiple or single result type (People, Episodes, etc)</li>
<li>Serviced via AJAX.</li>
</ul>
<h3 id="transactional-email">Transactional Email:</h3>
<ul>
<li>Uses Bamboo.</li>
<li>SMTP provider is Campaign Monitor.</li>
<li>Username and password configured in config as ENV variables.</li>
<li>Config picks up environment variables using e.g.: <code>password: {:system, "CM_SMTP_TOKEN”}</code>.</li>
<li><code>auth_view</code> used to generate URLs in emails, passing in the application endpoint instead of a current connection.</li>
</ul>
<h3 id="passwordless-logins">Passwordless Logins</h3>
<ul>
<li>User visits <code>/in</code> to enter their email and be sent a login email</li>
<li><code>GET /in</code> displays the form</li>
<li><code>POST /in</code> processes the form. Two function definitions, with one being pattern matched to look for <code>email</code> param.</li>
<li>Token and expiry set on user in controller action.</li>
<li>Error if user not found, user being shown a 404.</li>
<li>When in dev environment it gives a login link in the response page, and skips sending the email.</li>
<li>Response page checks for <code>@person</code> instance variable, shows confirmation message.</li>
<li>15 minute expiry. Checked after retrieving user, not part of query.</li>
<li>Auth token in URL is a Base16 encoded email and random token with a separator, <code>|</code>.</li>
<li><code>GET /in/:token</code> decodes the auth token, checks for user with email and matching random token.</li>
<li>One-time-use. Resets the token and expiry on successful login.</li>
</ul>
<p>Did I get something wrong? Get in touch with me <a href="https://twitter.com/douglasfshearer">on Twitter</a>.</p>
<h3 id="followup">Followup</h3>
<p>Added on 2016-11-16.</p>
<ol>
<li>I actually mistook this as something that was unused. I created a <a href="https://github.com/thechangelog/changelog.com/pull/128">pull request</a> doing so. <a href="https://twitter.com/jerodsanto/">Jerod Santo</a> kindly showed me where it was used.</li>
<li><a href="https://twitter.com/jerodsanto/status/796733708426510340">Jerod Santo asked what I meant by this</a>. See my replies there, and also my <a href="http://douglasfshearer.com/2016/02/29/how-i-put-rails-models-on-a-diet.html">How I Put Rails Models on a Diet</a> post.</li>
<li><a href="https://changelog.com/posts/why-we-chose-turbolinks">Confirmed</a>. I’m a big fan of Turbolinks.js, even the statically generated <a href="http://pincountpodcast.com">Pincount Podcast</a> site uses it.</li>
</ol>
Wed, 09 Nov 2016 16:01:32 +0000http://douglasfshearer.com/2016/11/09/a-look-through-the-changelog-dot-com-source-code.html
http://douglasfshearer.com/2016/11/09/a-look-through-the-changelog-dot-com-source-code.htmlelixir phoenixHow I Put Rails Models on a Diet<p><img src="/images/2016/green-railway.jpg" alt="Photo by Antoine Beauvillain on Unsplash" />
<small>Photo by <a href="https://unsplash.com/antoinebeauvillain">Antoine Beauvillain</a> on <a href="https://unsplash.com/photos/0Kw44ElHN3A">Unsplash</a>.</small></p>
<h4 id="improving-my-rails-applications-with-a-service-layer">Improving my Rails applications with a Service Layer.</h4>
<p>I first discovered <a href="http://rubyonrails.org/">Rails</a> in 2005. I’ve been developing web applications in Rails since 2006. It’s fair to say that the majority of my income since graduating from University has been Rails related work.</p>
<p>Slowly, Rails projects became less enjoyable. Multi-person greenfield projects would start off great, but would often end up as being difficult to understand, modify, and collaborate on. Brownfield projects were worse: fat controllers; fat models; and tests that were minimal, brittle, and frustratingly slow.</p>
<p><a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model">Jamis Buck’s Skinny Controller, Fat Model pa</a>ttern did a lot to improve things, though for the most part it was ignored in the projects I had the chance to work on. I liked that the application logic was in the model, where testing it didn’t involve the controller lifecycle. Still, fat models were now the burden, the logic being closely coupled to the persistence layer. Could the application logic go elsewhere?</p>
<p>The late <a href="http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html">James Golick had the solution I was looking for</a>: Services. Instead of packing all the logic into the models, it could exist in its own Ruby classes. Free from the shackles of persistence, services are just Plain Old Objects, where their purpose was clear, and testing fast due to their focussed purpose.</p>
<p>Time for an example! We have an e-commerce application, and wish to create an order from a request, calculate the tax on that order, and persist the order.</p>
<h3 id="the-controller">The Controller</h3>
<p>Below is an example of a controller action which would create an order:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">OrdersController</span>
<span class="k">def</span> <span class="nf">create</span>
<span class="n">service</span> <span class="o">=</span> <span class="no">OrderCreator</span><span class="o">.</span><span class="n">new</span>
<span class="n">status</span><span class="p">,</span> <span class="vi">@order</span> <span class="o">=</span> <span class="n">service</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:order</span><span class="o">]</span><span class="p">)</span>
<span class="k">if</span> <span class="n">status</span> <span class="o">==</span> <span class="ss">:succeeded</span>
<span class="n">redirect_to</span> <span class="vi">@order</span>
<span class="k">else</span>
<span class="n">render</span> <span class="ss">action</span><span class="p">:</span> <span class="s1">&#39;new&#39;</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></div>
<p>The controller only deals with passing the parameters to the service (or session information if required), and carrying out HTTP actions on the response. It makes no decisions on it’s own. The double-assignment isn’t the neatest (I’ll perhaps blog about different patterns I have tried for this in a future post), but it’s clear and understandable.</p>
<h3 id="a-service">A Service</h3>
<p>The <code>OrderCreator</code> service is what I would categorise as an Orchestrator; it mediates between the controller and the rest of the application. This, along with other services, would go in the <code>app/services</code> directory at the root of a Rails application.</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">OrderCreator</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">order_klass</span><span class="o">=</span><span class="no">Order</span><span class="p">,</span> <span class="n">tax_calculator_klass</span><span class="o">=</span><span class="no">TaxCalculator</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">order_params</span><span class="p">)</span>
<span class="n">order</span> <span class="o">=</span> <span class="n">order_klass</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">order_params</span><span class="p">)</span>
<span class="n">tax_service</span> <span class="o">=</span> <span class="n">tax_calculator_klass</span><span class="o">.</span><span class="n">new</span>
<span class="n">order</span> <span class="o">=</span> <span class="n">tax_service</span><span class="o">.</span><span class="n">calculate</span><span class="p">(</span><span class="n">order</span><span class="p">)</span>
<span class="k">if</span> <span class="n">order</span><span class="o">.</span><span class="n">save</span>
<span class="o">[</span><span class="ss">:succeeded</span><span class="p">,</span> <span class="n">order</span><span class="o">]</span>
<span class="k">else</span>
<span class="o">[</span><span class="ss">:failed</span><span class="p">,</span> <span class="n">order</span><span class="o">]</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></div>
<p>The first thing to note is the <code>initialize</code> method. Yes, that is dependency injection. No, you are not reading Java code from 1998. The use of <a href="https://en.wikipedia.org/wiki/Dependency_injection">DI</a> here allows us to swap out the classes for testing purposes, so we can isolate the service and test it on it’s own. If you’d like to see how this works in practice, check out the James Golick post linked above. Whilst passing arguments to <code>initialize</code> works well, I like to use <a href="https://github.com/bkeepers/morphine">Brandon Keepers’ Morphine gem</a>, which provides a nice API for doing simple dependency injection.</p>
<p>The second thing is that this service calls another service. Each service should be as simple as necessary, while allowing the services to be composed and re-used. It’s likely that sales tax would need to be re-calculated when editing an order, so extracting that functionality to a separate service makes sense.</p>
<p>Thirdly, we have the response. Depending on whether the order is persisted successfully, we pass back a symbol communicating the success state, and the order itself.</p>
<h3 id="the-auxiliary-service">The Auxiliary Service</h3>
<p>Lastly, we have a service for calculating tax on our order.</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">TaxCalculator</span>
<span class="no">TAX_RATE</span> <span class="o">=</span> <span class="mi">20</span><span class="o">.</span><span class="n">freeze</span> <span class="c1"># percent</span>
<span class="k">def</span> <span class="nf">calculate</span><span class="p">(</span><span class="n">order</span><span class="p">)</span>
<span class="n">order</span><span class="o">.</span><span class="n">tax</span> <span class="o">=</span> <span class="n">order</span><span class="o">.</span><span class="n">subtotal</span> <span class="o">*</span> <span class="p">(</span><span class="no">TAX_RATE</span> <span class="o">/</span> <span class="mi">100</span><span class="p">)</span>
<span class="n">order</span><span class="o">.</span><span class="n">total</span> <span class="o">=</span> <span class="n">order</span><span class="o">.</span><span class="n">subtotal</span> <span class="o">+</span> <span class="n">order</span><span class="o">.</span><span class="n">tax</span>
<span class="n">order</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></div>
<p>The <code>TaxCalculator</code> service calculates the sales tax, assigns the relevant attributes, and passes the order back. The order doesn’t even have to be an ActiveRecord instance, it could be any Struct or Object which responds to <code>subtotal</code>, <code>tax</code>, <code>tax=</code> and <code>total=</code>. Indeed, this is how this service would be tested, without instantiating a model, or even loading the Rails stack. The statelessness of this service removes side-effects, and allows for fast testing and easy debugging.</p>
<p>This is the sort of code that would previous have been encapsulated in a model callback or class method. Where the service becomes really adventagous is when tax gets more complicated, being related to geography, different rates of tax, etc. (Shout out to <a href="https://grabaperch.com/blog/archive/perch-shop-location-based-tax-and-vat-tools">VAT MOSS</a>!). The model no longer requires logic implementing these things, and mudding it’s persistence role.</p>
<h3 id="the-model">The Model</h3>
<p>So what is left in a model? Not much. The following is what I put in models:</p>
<ul>
<li>ActiveRecord Relationships</li>
<li>Validations</li>
<li>Class methods which wrap ActiveRecord API methods.</li>
<li>Instance methods which modify the state of the instance without any application logic.</li>
</ul>
<p>What is out?</p>
<ul>
<li>No public scopes. Wrap these.</li>
<li>No using ActiveRecord API methods outside of the model.</li>
<li>No display methods (example: a <code>full_name()</code> instance method which concatenates <code>first_name</code> and <code>last_name</code> attributes). This is a view concern. Either use a helper method, or use the <a href="https://github.com/drapergem/draper">Decorator/Presenter pattern</a> to wrap the model instance for display.</li>
<li>No callbacks. <a href="http://samuelmullen.com/2013/05/the-problem-with-rails-callbacks/">Samuel Mullen has written about this</a>, but prescribes a less aggressive callback prohibition than I follow.</li>
</ul>
<h3 id="the-result">The Result</h3>
<p>The core functionality of the the Distrify application is now easier to understand. No longer does debugging involve following chains of callbacks in the model, or other logic tied to the persistence. This speeds up the development of new features, as the risk of unintended regressions to existing features is much reduced.</p>
<p>Isolated testing of the services comprising the main logic of the application allows them to be run exceptionally fast, reducing developer downtime during development. Currently, the Distrify application has 34 controllers, 44 models (many of these are non-ActiveRecord models), and 130 services. There are 1869 Spec examples, which run in 1’58 seconds (plus 4’07 seconds for the test suite to load) on my late 2013 dual-core Macbook Pro. That test runtime is not a typo.</p>
<p>I should note that reduced test runtime was not a goal of using the service pattern. Increased understandability, reduced side-effects, and clearer ownership of functionality were the original goals. The tests running in single-digit seconds was just a nice side effect.</p>
<p>This started out as an experiment, but after more than a year productivity still seems to be high, and we are very pleased with how this has turned out.</p>
<h3 id="further-reading">Further Reading</h3>
<p>Not so much reading, but I can highly recommend <a href="https://twitter.com/garybernhardt">Gary Bernhardt</a>’s <a href="https://www.destroyallsoftware.com/screencasts/">Destroy All Software screencasts</a>. Season 4 covers a lot of what I’ve talked about in great detail, plus you’ll learn a bunch of other stuff too.</p>
<h3 id="tldr">TLDR</h3>
<p>Controllers do HTTP, models do persistence, decorators provide logic to views, services do everything else.</p>
<p><small>This post was also <a href="https://medium.com/@douglasfshearer/how-i-put-rails-models-on-a-diet-c33b9188bd0c">published on Medium</a>. Thanks to <a href="https://twitter.com/kieranmasterton">Kieran Masterton</a> for proofreading and guidance on the usage of Medium.</small></p>
Mon, 29 Feb 2016 19:41:32 +0000http://douglasfshearer.com/2016/02/29/how-i-put-rails-models-on-a-diet.html
http://douglasfshearer.com/2016/02/29/how-i-put-rails-models-on-a-diet.htmlrubyonrails rails rubyA New Distrify Player<p>Over the last six months I’ve been working on a complete rewrite of the <a href="https://distrify.com/">Distrify</a> player and backend infrastructure. Yesterday we migrated the first large batch of content-owners to this new player, so despite using it in production on a small scale for several months now, this was the first time it would be seen by a large number of people.</p>
<p><a href="https://new.distrify.com/videos/9ujBjS"><img src="/images/2015/new-distrify-player.jpg" alt="New Distrify Player" /></a></p>
<p>The player was conceived, designed, and built from the ground up with user experience in mind. The player has a fantastic design by <a href="http://mikekus.com/">Mike Kus</a>; the frontend in <a href="http://facebook.github.io/react/">React</a>, initially built by <a href="http://neilkinnish.com/">Neil Kinnish</a>; with a <a href="http://rubyonrails.org">Rails</a> backend by <a href="https://twitter.com/kieranmasterton">Kieran Masterton</a> and myself. The play and purchase experiences are fast, but we’re working hard to make them even faster and easier to use. I plan to write more about the technology choices that were made along the way.</p>
<p>The response so far has been fantastic, I’m incredibly proud of the whole Distrify team.</p>
Mon, 29 Jun 2015 19:49:35 +0100http://douglasfshearer.com/2015/06/29/a-new-distrify-player.html
http://douglasfshearer.com/2015/06/29/a-new-distrify-player.htmlSXC 2015 Round 2 - Dalbeattie<p><img src="/images/2015/2015-SXC-Round-2-Dalbeattie-Podium.jpg" alt="SXC 2015 Round 2 Dalbeattie - Podium" /></p>
<p>2015 is a year of change for me, at least as far as the bike goes. The little training I do manage (bother!) to do has been focused almost completely on Enduro. I’ve not ridden a road bike since last August, and sessions involve a lot more time doing weights, strength work, sprints, and skills sessions. I planned to enter the handful of <a href="https://sxc.org.uk/">Scottish Cross Country</a> rounds which did not clash with Enduro races, but there wasn’t enough to make a good go at the overall title, so the races were to be for training only. This more relaxed approach to XC seems to be working well, and despite the gleeful comments about my hairy legs on the start line, I pulled off a surprise third place in <a href="http://www.mylaps.com/en/classification/3402305?perClass=1">the first round of the SXC at Cathkin Braes</a>.</p>
<p>Round two was to be at Dalbeattie, and with a slightly shorter course to last year, which retained it’s technical character, I was sure I could equal the second place I had here in 2014. I started hard from the gun, making sure to get into the first tight section of boardwalk without getting involved in the melee of the big group. Jack Ravenscroft had a similar idea, cruising past me on the first forestry road. I tucked in behind, and when we got to the next section of forestry road I had a look to see who was around. <a href="https://instagram.com/rab_wardell/">Rab Wardell</a> and <a href="https://instagram.com/barloworld/">Andy Barlow</a> of <a href="http://dirtschool.co.uk">Dirt School</a> were both close at hand, and despite riding their trail bikes took good pace into the next section of climb. I knew I had to get 15-20 seconds on them for the descent, so followed Paul Carmichael as he pushed on towards the top of the climb. I got the gap I wanted, with Andy and Rab catching me up as things got flat again.</p>
<p>For the second lap I was mostly on my own, with a bit more of a gap at the bottom of the descent. Apart from dropping my chain towards the end of the gap due to a botched doubling-up of two rock sections, it was fairly uneventful. On lap three three I seemed to ease off a bit, and while I was going slower, Rab went fast and was right behind me going into the fourth lap. Paul Newnham told us that Jack had punctured and withdrawn, so we were now one and two. Again I knew I had to get a gap for the descent, so raised the pace going up the climb to the point where I wondered if I could maintain this pace for a lap and a half. I knocked 50 seconds off my previous lap time, definitely the negative split I would look for at this point in a race.</p>
<p>Going into lap five I knew there was a small gap back to Rab, but not big enough that I could ease off. I kept the pressure on, and apart from the stress of catching back markers in the tight sections, I matched my lap four split time. I crossed the line first, with Rab at 1m 16s, and James Fraser-Moodie at 3m 34s. I am of course very pleased to win, it’s only taken eleven seasons of podium appearances and other good results to get to the top of the box.</p>
<p>It was very interesting to see Rab run me so close on a trail bike that is nearly identical to the one I race Enduro on (29er, carbon, medium travel). While I wonder if he would have been closer with a change to proper XC tyres, it does drive home that modern trail bikes are fantastically versatile. I get the definite feeling that the “I don’t have an XC bike” excuse is redundant. Hopefully we can all convince a few of our Enduro-racing friends to come and give a Scottish XC a go.</p>
<p>Thanks to Emma (who was first in senior women, yah!) for doing my bottles, Steve at <a href="http://www.i-cycles.co.uk">I-Cycles</a> for the ongoing support, and all the other people who cheered me on and sent me their congratulations. And thanks to the Morven, Steve Brown, and all the other folks who make these races happen.</p>
<p>Finally, I found out a few hours after the race that my Aunt Dianne had passed away, so this race win is dedicated to her.</p>
<p>Related Stuff:</p>
<ul>
<li><a href="http://www.mylaps.com/en/classification/3447214?perClass=1">The race results on MyLaps</a>.</li>
<li><a href="https://medium.com/@Dirt_School/scottish-cross-country-series-round-2-with-rab-wardell-52612b780d9c">Rab’s report on the Dirt School blog</a>.</li>
<li><a href="http://derekshanks.blogspot.co.uk/2015/04/reminding-myself-why-i-do-this.html">Derek Shanks’ report</a>.</li>
<li><a href="http://novantae.zenfolio.com/p883198896">Photos by Alastair Ross</a>.</li>
<li><a href="https://www.strava.com/segments/9255684">The lap as a Strava segment</a>.</li>
</ul>
Wed, 29 Apr 2015 11:41:35 +0100http://douglasfshearer.com/2015/04/29/sxc-2015-round-2-dalbeattie.html
http://douglasfshearer.com/2015/04/29/sxc-2015-round-2-dalbeattie.htmlSXC 2014 Round 1 - Forfar<p>The first round of the <a href="http://sxc.org.uk/">Scottish Cross Country Series</a> was held just outside Forfar, with a course making fantastic use of woodland borders and disused quarry workings.</p>
<p>2013 had been a very wet and slow affair, making me a bit worried when it looked like the course was to be longer than last year. Thankfully conditions were dry and super fast, making for some nice fast sub-19 minute laps.</p>
<p>I got a decent start, following Rob Friel into the first singletrack. I let Gareth Montgomerie past on one of the early climbs, thinking there was no point in holding him up as he would likely ride away from me. I kept going steady, getting a good gap in the first lap to James Fraser Moodie. Sadly I tore a tyre on an embedded rock, so had to put a tube in. I was carrying my trusty pump, which although it is near-guaranteed to work better than an air canister, took several minutes to pump the tyre back up again. I lost about four and half minutes, dropping from third to sixth.</p>
<p>It took another lap to get back up to fourth, but I could see James about four minutes ahead on the twisty course, there was no chance of catching him in the remaining two laps. I rolled in for fourth, disappointed by the puncture but pleased with my early season form.</p>
<p>Thanks to the SXC and the chaps from Forfar for a great event. Full results can be found on <a href="http://www.mylaps.com/en/events/995556">MyLaps</a>.</p>
Mon, 28 Apr 2014 22:01:56 +0100http://douglasfshearer.com/2014/04/28/sxc-2014-round-1-forfar.html
http://douglasfshearer.com/2014/04/28/sxc-2014-round-1-forfar.htmlScottish Enduro Series 2014 Round 1 - Fort William<p>An Enduro, in February, in Scotland? I wondered about this, especially when standing in a rainy carpark knowing that my B&amp;B roommate had decided to spend another few weeks in Spain. Apparently it was warmer there.</p>
<p>The <a href="http://scottishenduroseries.co.uk">Scottish Enduro Series</a> is a new endevour brought by <a href="http://nofussevents.co.uk">No Fuss Events</a> and <a href="http://www.innerleithenmtbracing.com">Innerleithen MTB Racing</a>. Six rounds, some classic Scottish riding locations, and as on-trend as you like! I entered the whole series as soon as I could, sure that I could progress towards the upper echelons of mediocrity by the end of the season.</p>
<p>So back to the first round, in Fort William, where it was raining on practice day. Stage one used the top part of the XC World Championships course, twisting down with lots of pedalling and speed to be carried through tight hairpin bends. The straightforwardness made me slightly worried about the rest of the stages.</p>
<p>Not to worry, stage two went down Blue Crane, with some small detours, then into the Stump Alley descent as previously used on World Cups and <a href="http://www.sxc.org.uk">Scottish Cross Country</a> racing. Steep and with big holes, a few years of fallen trees meant that only the main line was rideable, leading to a luge-like trench appearing. After this there was some mixed rooty sections and a few steep faces, great fun.</p>
<p>Stage 3 was down the final sections before Nessie, including a bus stop and a line over the top of a rock, then a sprint from the foot of Nessie to the far reaches of the 10 Under the Ben course.</p>
<p>Stage 4 took in part of the World Cup Downhill track, then down the XC World Championships climb with some rough sections pedally sections in new growth trees.</p>
<p>On race day things went about as expected for me. Stage 1 was lots of sprinting, stage two rode easier than it did on race day, despite the mud. Due to a late start the top of stage 3 was barely recognisable, with a few fallen bodies to be negotiated. Stage 4 was cut short due to rainfall swelling a burn crossing to waist-deep. In the end I was 40th of 83 seniors, within my target of top-half for the first round, so I was reasonably happy, but I know I have work to do.</p>
<p>Next up, Innerleithen for some steeper stuff, and hopefully some more banter with a fantastic group of competitors and organisers!</p>
Tue, 25 Mar 2014 15:57:54 +0000http://douglasfshearer.com/2014/03/25/scottish-enduro-series-2014-round-1-fort-william.html
http://douglasfshearer.com/2014/03/25/scottish-enduro-series-2014-round-1-fort-william.htmlSXC 2012 Round 1 - Kirroughtree<p>The first round of the 2012&#160;<a href="http://sxc.org.uk">Scottish Cross Country</a> took place at Kirroughtree. The course used parts from previous races there, but was an all-new arrangement. The usual singletrack and fireroad to start, followed by a long natural climb, into an undulating rocky section, then some fast natural descending before a forestry road drag. A few pieces of natural singletrack dropped you into the finish of the Red route, back to the arena to start it all again.</p>
<p>There was a pretty good turnout for the first round, so the racing was going to be fast. On the first lap I tried not to go to wild chasing the elite guys, they were off to do their own battle. Weirdly on the first lap there were five elites up front, then the expert racers including myself fairly close together behind. It was at this point I realised I could have quite a good battle with Hamish Fletcher-Cooney, a fellow expert who I&#8217;d raced against a few times last year.</p>
<p>At the end of the first lap he had about 29 seconds on me, but I noticed as we did the first half of the lap that I could take him back fairly quickly on the climb and natural sections. I overtook on the natural climb, and pressed on a little bit to see what would happen. On the forestry road drag I lost some time, and we crossed the line at the end of the second lap with only a few seconds between us.</p>
<p>This status-quo continued on the next lap, with the gap over the line again being only a few seconds. For the last lap I knew I had to put a big effort in on the climbs to make sure he couldn&#8217;t close the gap on the flatter sections. I properly turned myself inside out on the first half of the course, then did my best to keep the speed up for the rest of the lap, resisting the temptation to look behind.</p>
<p>I crossed the line in sixth position, first expert, and held Hamish off my 18s. The course really is well suited to differing types of rider, I&#8217;m really looking forward to racing Kirroughtree again in June when the British XC Series finally comes back to Scotland.</p>
<p>Thanks to the SXC and the girls at The Breakpad for the awesome course.</p>
<p>Full results are available <a href="http://www.theclaymoreproject.com/uploads/associate/81/file/2012%20-%20SXC%201%20-%20Kirroughtree%20Results.pdf">on the SXC website</a>.</p>
Wed, 13 Jun 2012 22:48:22 +0100http://douglasfshearer.com/2012/06/13/sxc-2012-round-1-kirroughtree.html
http://douglasfshearer.com/2012/06/13/sxc-2012-round-1-kirroughtree.htmlBMBS 2012 Round 1 - Sherwood Pines<p>This year Sherwood had a mostly new course on offer for the first round of the British series. Unfortunately it was as flat as the old courses, with minimal technical sections to separate riders, resulting in some very close racing. After the race I was told there was caution arrows on some sections, I never saw these and still think my leg is being pulled.</p>
<p>I was gridded first, so was in the front row with a good chance of staying out of any trouble. Unfortunately there was a big crash in the row behind as the barriers narrowed in the start straight, ending many people&#8217;s races very early and resulting in Wheelbase&#8217;s Joe Richards having a helicopter ride to the hospital to be checked out (he&#8217;s OK, it was just precautionary).</p>
<p>The first section of singletrack came pretty quickly, by which time I was probably down in about 20th due to my lack of power for the start. I sat steady, laughing at riders being too keen to make a pass in inappropriate places, and at those who were now regretting their seriously-slick tyre choices. Once the second lap came round I started to move up places, blasting past people on the one short-step climb on the course, and drafting riders on the forestry roads before sprinting past into the singletrack.</p>
<p>On the last lap I was still making up places, but struggling with some of the elite back markers who were riding some sections a lot slower than I wanted to, especially frustrating with the narrow tree-lined singletrack that makes up the majority of the Sherwood course. The run-in to the finish was tight, with some rooty singletrack and right-angles to negotiate. I managed to pass some more elites on get on the back of a group of three other expert riders but I was outgunned in the sprint by all of them and came in for a rather pleasing tenth place. Not bad for a course that&#8217;s not supposed to suit me.</p>
Sat, 12 May 2012 15:44:28 +0100http://douglasfshearer.com/2012/05/12/bmbs-2012-round-1-sherwood-pines.html
http://douglasfshearer.com/2012/05/12/bmbs-2012-round-1-sherwood-pines.htmlScottish Cyclocross Season Roundup<p><img src="/images/tumblr_lzbx7qOpzO1r4bwz6.jpg"/></p>
<p><a href="http://www.scottishcyclocross.org.uk/">Scottish Cyclocross</a> is now bigger than ever, some of the rounds had almost 300 people turning up. Great courses and great people, I look forward to more of this in 2012.</p>
<h3><span class="caps">SCX</span> Round 1 – Irvine</h3>
<p>I’d never ridden the cyclocross course at Irvine before, despite it appearing twice in the past, once as an <span class="caps">SCX</span> round in 2009, and as the delayed 2010 champs in January 2011. Famous for it’s sand-trap, a long stretch in the dunes at the back of the beach, I was keen to see this was a gimmick or something worth using as part of the course.</p>
<p>The course has two runups, one short up a set of steps, the other a 25m steep grass section. The sand-trap is about 100m long, and is an awesome addition to the course. It takes more grunt than skill to ride it, though things are much easier when you realise you can skirt along the margins out of the really soft stuff, though you risk getting stuck in some of the larger ruts.</p>
<p>As for the racing, I had a horrible first couple of laps, going backwards after the initial fast start it took me a while to get into the swing of things. For the second half of the race I had Rapha’s James McCallum in sight, allowing me to keep my lap times sensible even though I was feeling a bit horrible. I put in a big effort on the last few laps, but didn’t make the catch by the end.</p>
<p>13th place, an improvement on my first CX race last year. Full results on the <a href="http://www.scottishcyclocross.org.uk/2011/10/irvine-quick-results.html"><span class="caps">SCX</span> website</a>.</p>
<h3><span class="caps">SCX</span> Round 2 – Plean</h3>
<p>A bit of a classic course remixed for 2011. Gone was the large climb at the back of the course, replaced with two new grassy sections. Loads of people complained about this course, but despite only having the one bike I had little issue with the bike clogging up. Definitely a course for the more powerful and skilled riders.</p>
<p>I got into fourth wheel into the first singletrack, then had to stop because my brake hanger cable was rubbing on the tyre. Almost as soon as I remounted I jammed my too-short chain on the big-ring, big-sprocket combination. My fault, fixed that then remounted. I was down in about 30th at this point. My disaster lap continues as I go some course tape caught in my cassette.</p>
<p>After this things started to come together, I was making up lots of places by riding sections others with running, and taking risks on some of the fast muddy descents. I did manage to drop my bike about halfway through as I hopped the hurdles, resulting in twisted bars and a bent <span class="caps">STI</span>. With no tools I just had to ride it like that.</p>
<p>I ended up 10th, which I was very pleased with given the troubles I had. Full results on the <a href="http://www.scottishcyclocross.org.uk/2011/10/quickie-update-round-2-plean-park.html"><span class="caps">SCX</span> website</a>.</p>
<h3><span class="caps">SCX</span> Round 3 – Mugdock</h3>
<p>Another classic course, this time without the crazy start of last year which resulted in lots of pileups. I got a good start and started to pick people off quite early. On the third lap I thought I felt my front tubeless tyre burping some air when I slammed it into some off-camber ruts. The next lap at the same point I was sure it was happening, and now had a very low front tyre. I kept riding it, hoping to make the pits where I could borrow a pump. Alas it burped the remaining air on a fast gravel corner, I luckily managed to step off the bike as it happened, with no injury. I ran round to the start-finish area to see what the time was like, then posted my <span class="caps">DNF</span>, my first of the year. Spectating was great fun after this, though I think some people were a little shocked by my enthusiastic heckling.</p>
<h3>Hallocross</h3>
<p>Organised by the <a href="http://www.thetricentre.com/">Tri Centre</a> on the same course as the Edinburgh 48 races, Hallocross was night-time cyclocross, in fancy dress, with cider. 60 people turned out for it, very impressive for a midweek event that required lights. Even knowing the course very well it was pretty frightening at times keeping the ’cross bike upright on the gravel corners and going over some of the blind drops. The racing was close and fast, with lots of mini battles to be had. Results were only down to third, due to the technical difficulties of reading numbers in the dark. I think I was seventh or eighth, but had fantastic fun whatever the result.</p>
<h3><span class="caps">SCX</span> Round 4 – Ballater</h3>
<p>Ballater isn’t exactly close, but some nice weather meant the drive north of the Lecht was one to remember. The Ballater course was very non-traditional ’cross, with some fast rocky tracks interspersed with forestry road. A few technical sections, punchy climbs and a horrible set of steps made it a tough one though. My only issue with the course was a rocky burn crossing which was very hard to cross without kissing the rims against the rocks.</p>
<p>I had a good start here too, and picked off places as people dropped chains and punctured due to the rocks. The climbs were really suiting me, with the descents allowing some recovery time.</p>
<p>I ended up 6th, my best result so far. Full results on the <a href="http://www.scottishcyclocross.org.uk/2011/11/round-4-ballater-results-quickie.html"><span class="caps">SCX</span> website</a>.</p>
<h3><span class="caps">SCX</span> Round 5 – Strathclyde Country Park</h3>
<p>One of my favourite courses from last year, it had been changed slightly with one of the grassy climbs replaced with hardpack, and one of the descents on a gravel path. It was super muddy, with the field section being calve-deep mud. More than 30 people sheared their rear mechs off due to the small stones on the descent getting dragged into the mech by the mud. I joined them on the third lap, my mech sheared clean through, with the hanger being left completely perfect. Bah. The chain needed replaced too, only having been on for three races prior.</p>
<h3><span class="caps">SCX</span> Round 6 – Meadowmill</h3>
<p>Another redesigned course for 2011, with lots of flat out fast sections, the usual awesome singletrack, and some horrible headwind sections due to gale force winds all day. It was nice to race on a dry mud-free course after the previous round. The course wasn’t really for me, there was no rest on it so I had to ride at a pace I could sustain for the whole hour, rather than just a few minutes at a time.</p>
<p>Despite this I really enjoyed the high pace all the way round, and the interesting technical sections. I had a near-miss with a spectator on the course, unfortunately Davie Lines didn’t manage to swerve and was on the ground and out of the race instantly.</p>
<p>I ended up 15th. Full results on the <a href="http://www.scottishcyclocross.org.uk/2011/11/round-6-meadowmill-results.html"><span class="caps">SCX</span> website</a>.</p>
<h3>Scottish Cyclo-Cross Championships</h3>
<p>Made it to within a mile of this race when I crashed the car due to slippy conditions. No race! I don’t think I’ve actually ridden the champs since I won them as a junior in 2002. Will have to make it next year for the 10th anniversary.</p>
<h3>Callendar Christmas Cross</h3>
<p>A new event for 2011, run by Franco Porco and Davie lines around the spectacular venue of Callendar House and the <a href="http://en.wikipedia.org/wiki/Antonine_Wall">Antonine Wall</a>. The course was fantastic, with steep run-ups, long off-camber sections and slippy climbs. Those who were technically inclined had no running at all. It rained for almost the entire day before the race, meaning there was lots of sloppy mud to be had once the grass was chewed up. This was good, certainly better than the sticky mud we had seen at other venues.</p>
<p>The start was along a fast section of tarmac. I hung back so as to avoid those weaving side-to-side as they tested their VO2 maxes. On the first steep run-up and muddy corner I went from 25th to 10th by virtue of riding it then going round the inside near the apex. I then stormed up the first climb to find myself in fourth position for the rest of the lap. Myself and the other mountainbikers were definitely at a big advantage on this course. I couldn’t hold my position on the flat sections of the following lap, and was riding round with Paul McInally and junior rider Calum Foster. I only made two mistakes the whole race, one being losing my line on a long off-camber section and losing about 20s, and the other crashing on an off-camber descent where I lost very little time.</p>
<p>Calum started to make some mistakes about halfway through, and I took full-advantage of him losing his line on the section I lost time on, by putting the hammer down as soon as I saw him veer. Paul was yo-yoing back and forth, making lots of time up on the fast sections round the house and one muddy section that had become a run, and losing it as I took risks on the back-side of the course. On the last two laps I put in the big efforts on the climbs and the running section, eeking out enough of a gap to take 7th overall and 5th senior. Paul was 8th a few seconds back, and Calum finished an impressive ride to take 9th,</p>
<p>A great result to end the year, especially after a few weeks of zone-2 riding and a complete lack of intervals in December.</p>
<p>Full results on the <a href="http://www.scottishcyclocross.org.uk/2011/12/callendar-christmas-cross.html"><span class="caps">SCX</span> website</a>.</p>
Sat, 31 Dec 2011 15:35:00 +0000http://douglasfshearer.com/2011/12/31/scottish-cyclocross-season-roundup.html
http://douglasfshearer.com/2011/12/31/scottish-cyclocross-season-roundup.htmlNo Fuss Tour De Ben Nevis 2011 <p>I missed out on this event in 2010, a lack of enthusiasm at the end of the season meant I just wasn’t up for it though. <a href="http://www.innerleithenmtbracing.com/">Innerleithen <span class="caps">MTB</span> Racing</a> was well represented though, with <a href="http://i-cycles.co.uk/">I-Cycles</a> own Steve Deas just missing out on the podium, and taking No Fuss’ Best All-Round Mountainbiker prize. Steve raved about the event, adamant that I should do it in 2011.</p>
<p>This year I was much more enthusiastic. Not racing for most of June and July, and not training properly in months meant I was mentally fresh and ready for this big day out in the hills.</p>
<p>On the Friday night I stayed in a bunkhouse with Marty, Niall, Dave, Dave and Colin. Only some beer was drunk by me, and we were in bed by midnight. Up early, and at the start venue for 8:30, we had to wait for a while to dib in. While we were waiting in the road to start there were heavy showers which only lasted a few minutes at a time. This did not bode well for four plus hours in the big hills. We were led by a police car to the High Street, where the official start was to be. Pipers piped us down the high street at slow march, with us riding along very slowly behind (no dabs!). Once onto the main road we were off.</p>
<p>Up the first climb I was in the first couple of riders, looking for James Shirley and Rauri Watt who had performed well last year. A couple of roadie-looking guys were pushing good pace, but I wasn’t wanting to blow myself up in the first couple of miles as I had no idea how fit I really was. Nic Smith slowly pulled away towards the top, and left a small group of us wondering whether to chase him or not on the rolling roads. I worked with James, Rauri and a few other guys on the road, then it was every man for himself on the first part of the West Highland Way.</p>
<p>Rauri pulled out a little gap here, again I was content not to chase. After about ten miles we got to the Kinlochleven descent. Saddle down, dibbed in, and off! After only a few hundred yards a rock struck me on the shin, the pain rolling up and down my leg as I tried to keep the speed up. I didn’t know the descent to start with, and the pain wasn’t helping things. I got down in one piece, no punctures on the huge drainage channels due to following Steve’s advice and jumping them. I was 17th on this, the first, special stage.</p>
<p>Up the climb to Mamore lodge I was caught Ben Arnott. He’d managed to land on his face during the descent, and had broken a finger. I rolled on, hoping to get a good result in this special stage. I almost caught James Shirley who had got a gap on me before the previous descent, and was struggling a little with his single ring. I was 4th on the climb.</p>
<p>Next up we rolled along the side of some lochs, splashing through huge deep puddles. I made good pace along here, aware that there were a few people with me as we had bunched up after the previous special stage. Right before the river crossing I dismounted to avoid what looked like an especially deep puddle, only to discover the water at the sides was also waist deep. After that the river crossing was an anti-climax, being only knee deep at it’s deepest.</p>
<p>At the other side the path was punctuated by ditches every few meters, so it was quicker to run. After about half a kilometer I met James coming the other way: we’d both missed the dibber after the river crossing. Rubbish. I ditched my bike and bag and joined James on the run back along the riverbank to the dibber, then back to the bikes. This special stage was pretty much a 40 minute walk/carry up a steep mountain path. We made good time up here, eventually dropping other riders who looked like they were making good pace on the bottom section. The end dibber was at the Lairig bothy. I was 6th on that stage.</p>
<p>As I had been descending the section to the bothy I noticed that there was a rattling coming from the backend of the bike. I suspected it was loose bushings as I’d had them apart a few days before to clean them. After getting my allen keys out of my bag I realised it was the rear wheel that was loose in the frame, rattling in the dropouts. The skewer-nut was seized onto it’s thread, so there was no way for me to tighten it up. I left it out, confident that the wheel wouldn’t be pulled out even under heavy load.</p>
<p>The descent down towards Spean Bridge is a cracker, full-on 30-40mph snaking forestry road for several miles. At the bottom James had a puncture, so I kept on trucking, hoping I might catch some of the four guys up ahead. I passed the 20km to go sign, feeling my legs weakening on the rather dull trails and forestry road we were faced with at this section. I necked my remaining gels, all three of them, within about 15 minutes. Shortly after I felt better again, knowing I just had to keep it rolling. As soon as I recognised some of the trails and knew where I was, it was like someone turning on the nitrous. I took a gulp from the bottle on my frame, then emptied the rest onto the ground as I knew I wouldn’t be needing it. I was flying up the final climb, knowing it well from many races in the past.</p>
<p>The final descent was the Blue Crane down to the top of Nessie. I knew this descent well, so was confident of doing well. In my tiredness I made a mistake and lowered my saddle. This was great for the first section, but the middle section is a fast forestry road where you need to pedal all the time. Having the saddle down here was not good for my tired legs, but I wasn’t convinced I would make any more time up by stopping and raising the saddle. It was great fun anyway, especially as I hadn’t ridden these trails this year. I was 16th on this stage.</p>
<p>The final little tarmac section to the start-finish was over pretty quickly, taking turns in front with James who had caught me up at the bottom of Nessie. My legs collapsed within sight of the end, and I finished in 6th, a few seconds down on James.</p>
<p>Oddly at this event, the positions rather than time on the special stages were what mattered. This meant I was 4th, which was a pretty pleasing result all things considered. I am already looking forward to next year.</p>
<p>Full results are available from <a href="http://www.sportident.co.uk/results/2011/TourDeBenNevis/TourDeBenResults2011.pdf">Sportident</a>.</p>
Thu, 13 Oct 2011 18:43:30 +0100http://douglasfshearer.com/2011/10/13/no-fuss-tour-de-ben-nevis-2011.html
http://douglasfshearer.com/2011/10/13/no-fuss-tour-de-ben-nevis-2011.html