<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[aron / philipp]]></title>
  <link href="http://apdevblog.com/atom.xml" rel="self"/>
  <link href="http://apdevblog.com/"/>
  <updated>2016-05-21T11:10:36+02:00</updated>
  <id>http://apdevblog.com/</id>
  <author>
    <name><![CDATA[Aron Woost and Philipp Kyeck]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Building a node web app at scale with AWS Lambda]]></title>
    <link href="http://apdevblog.com/aws-lambda-learnings/"/>
    <updated>2016-05-20T13:00:00+02:00</updated>
    <id>http://apdevblog.com/aws-lambda-learnings</id>
    <content type="html"><![CDATA[<p>Recently I built a web app for the <a href="http://www.eurovision.tv">Eurovision Song Contest</a>. Here are the things I would like to have known 6 week ago.</p>

<p>Some days into the project <a href="https://aws.amazon.com/de/about-aws/whats-new/2016/04/aws-lambda-supports-node-js-4-3/">Lambda added Node 4.3 support</a>. Previously the only supported Node version was 0.10 and the lack of generators made the code super messy. Without Node 4.3 this blogpost probably would look kinda different.</p>

<h2>Lambda is not a web server!</h2>

<p>The signature of a <a href="http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html">Lambda function</a>:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="nx">exports</span><span class="p">.</span><span class="nx">handler</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">,</span> <span class="nx">context</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Lambda functions can be triggered from different sources (<a href="http://docs.aws.amazon.com/lambda/latest/dg/with-s3.html">S3</a>, <a href="http://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html">DynamoDB</a>, <a href="http://docs.aws.amazon.com/lambda/latest/dg/with-scheduled-events.html">Scheduled Events</a> and more). For processing HTTP requests another service is needed: <a href="https://aws.amazon.com/de/api-gateway/">API Gateway</a>.</p>

<p>API Gateway is not tied to Lambda and can be used for various kinds of backends. Bottom line: The API Gateway/Lambda stack has very little to do with something like Express. On the contrary, mastering API Gateway is hard and I underestimated that. As a matter of fact, most debugging and cursing happened on API Gateway side and not because of Lambda.</p>

<h2>How to get requests into Lambda</h2>

<p>API Gateway provides a graphical interface where you (basically) setup an endpoint and method and map it to a Lambda function.</p>

<p><img src="http://apdevblog.com/images/2016/api-gateway-method-endpoint.png" alt="api-gateway-method-endpoint" /></p>

<p>By default API Gateway only passes the request payload (in case of a <code>POST</code>/<code>PUT</code>) to the <code>event</code> parameter (see above) of a Lambda function. And for a <code>GET</code> request nothing is passed!</p>

<h3>Input mapping</h3>

<p>In order to get all the request meta data into Lambda (like path, method, ip, headers etc.) an input &ldquo;Body Mapping Templates&rdquo; must be set up. This is the mapping I used:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="p">{</span>
</span><span class='line'>  <span class="s2">&quot;method&quot;</span><span class="o">:</span> <span class="s2">&quot;$context.httpMethod&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="s2">&quot;body&quot;</span> <span class="o">:</span> <span class="nx">$input</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="s1">&#39;$&#39;</span><span class="p">),</span>
</span><span class='line'>  <span class="s2">&quot;path&quot;</span><span class="o">:</span> <span class="s2">&quot;$context.resourcePath&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="s2">&quot;ip&quot;</span><span class="o">:</span> <span class="s2">&quot;$context.identity.sourceIp&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="s2">&quot;headers&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="err">#</span><span class="nx">foreach</span><span class="p">(</span><span class="nx">$param</span> <span class="k">in</span> <span class="nx">$input</span><span class="p">.</span><span class="nx">params</span><span class="p">().</span><span class="nx">header</span><span class="p">.</span><span class="nx">keySet</span><span class="p">())</span>
</span><span class='line'>      <span class="s2">&quot;$param.toLowerCase()&quot;</span><span class="o">:</span> <span class="s2">&quot;$util.escapeJavaScript($input.params().header.get($param))&quot;</span> <span class="err">#</span><span class="k">if</span><span class="p">(</span><span class="nx">$foreach</span><span class="p">.</span><span class="nx">hasNext</span><span class="p">),</span><span class="err">#</span><span class="nx">end</span>
</span><span class='line'>    <span class="err">#</span><span class="nx">end</span>
</span><span class='line'>  <span class="p">},</span>
</span><span class='line'>  <span class="s2">&quot;queryParams&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="err">#</span><span class="nx">foreach</span><span class="p">(</span><span class="nx">$param</span> <span class="k">in</span> <span class="nx">$input</span><span class="p">.</span><span class="nx">params</span><span class="p">().</span><span class="nx">querystring</span><span class="p">.</span><span class="nx">keySet</span><span class="p">())</span>
</span><span class='line'>      <span class="s2">&quot;$param&quot;</span><span class="o">:</span> <span class="s2">&quot;$util.escapeJavaScript($input.params().querystring.get($param))&quot;</span> <span class="err">#</span><span class="k">if</span><span class="p">(</span><span class="nx">$foreach</span><span class="p">.</span><span class="nx">hasNext</span><span class="p">),</span><span class="err">#</span><span class="nx">end</span>
</span><span class='line'>    <span class="err">#</span><span class="nx">end</span>
</span><span class='line'>  <span class="p">},</span>
</span><span class='line'>  <span class="s2">&quot;pathParams&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="err">#</span><span class="nx">foreach</span><span class="p">(</span><span class="nx">$param</span> <span class="k">in</span> <span class="nx">$input</span><span class="p">.</span><span class="nx">params</span><span class="p">().</span><span class="nx">path</span><span class="p">.</span><span class="nx">keySet</span><span class="p">())</span>
</span><span class='line'>      <span class="s2">&quot;$param&quot;</span><span class="o">:</span> <span class="s2">&quot;$util.escapeJavaScript($input.params().path.get($param))&quot;</span> <span class="err">#</span><span class="k">if</span><span class="p">(</span><span class="nx">$foreach</span><span class="p">.</span><span class="nx">hasNext</span><span class="p">),</span><span class="err">#</span><span class="nx">end</span>
</span><span class='line'>    <span class="err">#</span><span class="nx">end</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>These values are then available in the <code>event</code> parameter of the Lambda handler function.</p>

<h2>How to get responses out of Lambda</h2>

<p>For a success response it&rsquo;s quite easy. From Lambda do:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="nx">exports</span><span class="p">.</span><span class="nx">handler</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">,</span> <span class="nx">context</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// do the work here</span>
</span><span class='line'>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">responseData</span> <span class="o">=</span> <span class="p">{};</span>
</span><span class='line'>  <span class="nx">callback</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="nx">responseData</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>response</code> is what your user will receive as the response payload.</p>

<h3>How to setup non-default (200) responses</h3>

<p>Probably the most annoying thing with API Gateway (and the thing that produces the most Lambda related issue in the API Gateway forums) is how to setup custom HTTP response codes. It works by regex&#8217;ing over the error reponse from Lambda.</p>

<p>Suppose you want to respond with a 404. From Lambda you call:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="nx">exports</span><span class="p">.</span><span class="nx">handler</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">,</span> <span class="nx">context</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// do the work here</span>
</span><span class='line'>
</span><span class='line'>  <span class="nx">callback</span><span class="p">(</span><span class="s2">&quot;404 happened&quot;</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Since the first parameter of the callback function is used, API Gateway understands it as an error. In API Gateway you setup a &ldquo;Lambda Error Regex&rdquo; with regex <code>.*404.*</code> and the user will receive status code 404.</p>

<p>So how to not only set the correct status code but also pass a response to the client? You can pass the status code and response body as separate properties:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="nx">exports</span><span class="p">.</span><span class="nx">handler</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">,</span> <span class="nx">context</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// do the work here</span>
</span><span class='line'>
</span><span class='line'>  <span class="nx">callback</span><span class="p">({</span>
</span><span class='line'>    <span class="nx">status</span><span class="o">:</span> <span class="mi">403</span><span class="p">,</span>
</span><span class='line'>    <span class="nx">body</span><span class="o">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span>
</span><span class='line'>      <span class="nx">message</span><span class="o">:</span> <span class="s2">&quot;you&#39;re not allowed to do that&quot;</span>
</span><span class='line'>    <span class="p">})</span>
</span><span class='line'>  <span class="p">});</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>In API Gateway I then setup a &ldquo;Lambda Error Regex&rdquo; where the regex is <code>.*\"status\":403.*</code> and the following output &ldquo;Body Mapping Template&rdquo; (&ldquo;Body Mapping Template&rdquo; maps the reponse from Lambda to the response body that is passed to the user from API Gateway):</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="err">#</span><span class="nx">set</span> <span class="p">(</span><span class="nx">$errorMessageObj</span> <span class="o">=</span> <span class="nx">$util</span><span class="p">.</span><span class="nx">parseJson</span><span class="p">(</span><span class="nx">$input</span><span class="p">.</span><span class="nx">path</span><span class="p">(</span><span class="s1">&#39;$.errorMessage&#39;</span><span class="p">)))</span>
</span><span class='line'><span class="nx">$errorMessageObj</span><span class="p">.</span><span class="nx">body</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>$.errorMessage</code> is the error data coming from Lambda.</p>

<p>This needs be done for all response codes. You&rsquo;ll end up with something like this:</p>

<p><img src="http://apdevblog.com/images/2016/api-gateway-error-codes.png" alt="api-gateway-error-codes" /></p>

<h3>How to set response headers</h3>

<p>Suppose you want to set a set-cookie or a location header. How to do that? You can set &ldquo;Header Mappings&rdquo; where you map a property from the Lambda response object to a header set in API Gateway.</p>

<p><img src="http://apdevblog.com/images/2016/api-gateway-header-mapping-set-cookie.png" alt="api-gateway-header-mapping-set-cookie" /></p>

<p>Here is the catch: that does only work with the default response (see 200 from above) but not with the &ldquo;Lambda Error Regex&rdquo; workaround since the &ldquo;Header Mapping&rdquo; can not access the <code>$.errorMessage</code> property.</p>

<p>That means that you can currently only have one response code (the one that you defined as default) to set headers. I&rsquo;m sure AWS is currently working on that. Because this is bad.</p>

<h2>Get used to swagger config file early</h2>

<p>While the graphical user interface of API Gateway is fine at the beginning when starting to work with and understanding API Gateway, it gets super annoying down the road because you are ending up copy-pasting stuff all the time. For instance you will likely want to have the same &ldquo;Lambda Error Regex&rdquo; for most of your endpoints. And you definitely need to have the input &ldquo;Body Mapping Templates&rdquo; from above on every endpoint.</p>

<p>I resisted way too long against using a swagger config file, since I was unfamiliar with it and thought I already need to learn enough new stuff. However, it&rsquo;s easy to understand since it provides the same settings as the graphical user interface. I suggest you set up your basic settings with the GUI and then export it via &ldquo;Stages&rdquo; &ndash;> &ldquo;Your stage name&rdquo; &ndash;> &ldquo;Export&rdquo; &ndash;> &ldquo;Export as Swagger + API Gateway Extensions&rdquo;.</p>

<p>Then you can edit the swagger config file locally and push it with:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="nx">aws</span> <span class="nx">apigateway</span> <span class="nx">put</span><span class="o">-</span><span class="nx">rest</span><span class="o">-</span><span class="nx">api</span> <span class="o">--</span><span class="nx">rest</span><span class="o">-</span><span class="nx">api</span><span class="o">-</span><span class="nx">id</span> <span class="o">&lt;</span><span class="nx">your_api_id</span><span class="o">&gt;</span> <span class="o">--</span><span class="nx">mode</span> <span class="nx">overwrite</span> <span class="o">--</span><span class="nx">body</span> <span class="nx">file</span><span class="o">:</span><span class="c1">//&lt;path_to_swagger_file.json&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<h2>Serving static files</h2>

<p>Most parts of the site were dynamic (the admin interface and the API), but there were also static ones (the homepage). API Gateway can be used to serve static content by using the &ldquo;AWS Service Proxy&rdquo; Integration type. With that static files can be proxied from S3.</p>

<p>But it&rsquo;s slow! And requests count against the API Gateway throttling limits. Instead you should use subdomains for requests that should be handled by API Gateway Lambda (i.e. api.your_domain.com and admin.your_domain.com) and map the homepage (your_domain.com) to CloudFront. This is specially true since API Gateway <em>can not</em> server binary files (i.e. images).</p>

<p>Note: I did not test the API Gateway cache.</p>

<h2>How to run Lambda functions locally?</h2>

<p>I didn&rsquo;t do it. Being able to run the tests locally was enough for me. Final tests I did on production. I understand that this would not be suitable for larger and more complex projects.</p>

<h2>What to use for deployment?</h2>

<p>I went with <a href="https://github.com/motdotla/node-lambda">node-lambda</a>. I haven&rsquo;t really tested the alternatives. node-lambda does what I needed (deploy the function). Their ENV variables handling is nice.</p>

<h2>At scale</h2>

<p>If you&rsquo;re expecting a lot of traffic you might want to contact AWS support to increase API Gateway and Lambda limits.</p>

<p>Lambda is smart in reusing the spawned Lambda nodes. So if your Lambda function executes relatively fast (~300ms, typical for API requests) and the traffic is moderate the default Lambda limit (100 concurent Lambdas) should be fine.</p>

<h2>No gzip</h2>

<p>:(</p>

<h2>Conclusion</h2>

<p>While Lambda is easy to get used to, having to proxy requests through API Gateway makes everything hard that is usually super-easy in plain node (with the help of <a href="http://expressjs.com/de">express</a> or friends). Now that I went through all the pain, everything seems obvious. But it was a hard process.</p>

<p>Still, serverless is the new cool kid on the block and I enjoyed not needing to deal with provisioning servers (and make them scale). AWS is the leader and they are actively improving Lambda and API Gateway.</p>

<p>I&rsquo;m also very excited about <a href="https://zeit.co/">zeit.co</a>. With them you can just deploy your express app. Need to look into that soon.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[read later #8]]></title>
    <link href="http://apdevblog.com/read-later-8/"/>
    <updated>2016-01-17T12:00:00+01:00</updated>
    <id>http://apdevblog.com/read-later-8</id>
    <content type="html"><![CDATA[<ul>
<li><p><a href="http://www.bitsnbites.eu/?p=221">A tidy, linear Git history</a><br/>
It&rsquo;s challenging the keep the commit history clean when the team grows. This is one solution, Aron uses it at <a href="https://mixedzone.io/">MixedZone</a> and is very happy with it.</p></li>
<li><p><a href="https://github.com/edx/edx-platform/wiki/How-to-Rebase-a-Pull-Request">How to Rebase a Pull Request</a><br/>
Learn how to rebase (and how to <a href="https://github.com/edx/edx-platform/wiki/How-to-Rebase-a-Pull-Request#squash-your-changes">squash</a>) commits when following the workflow described above.</p></li>
<li><p>Modern JavaScript:<br/>
<img src="http://apdevblog.com/images/2016/modern-javascript-annotated.png" alt="modern-javascript-annotated" /></p></li>
<li><p><a href="http://flexboxfroggy.com/">Flexbox Froggy</a><br/>
Learn Flexbox. The fun way.</p></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[read later #7]]></title>
    <link href="http://apdevblog.com/read-later-7/"/>
    <updated>2016-01-03T12:00:00+01:00</updated>
    <id>http://apdevblog.com/read-later-7</id>
    <content type="html"><![CDATA[<p>Welcome back!</p>

<ul>
<li><p><a href="http://blog.jenkster.com/2015/12/what-is-functional-programming.html">What Is Functional Programming?</a><br/>
Must read.</p></li>
<li><p><a href="http://blog.jenkster.com/2015/12/which-programming-languages-are-functional.html">Which Programming Languages Are Functional?</a><br/>
Must read (continued).</p></li>
<li><p><a href="https://changelog.com/187/">the changelog #187</a><br/>
With Dan Abramov, creator of redux.</p></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[read later #6]]></title>
    <link href="http://apdevblog.com/read-later-6/"/>
    <updated>2014-04-28T12:00:00+02:00</updated>
    <id>http://apdevblog.com/read-later-6</id>
    <content type="html"><![CDATA[<ul>
<li><p><a href="https://github.com/tiimgreen/github-cheat-sheet">GitHub Cheat Sheet</a><br/>
While top 5 Ejmojis on GitHub are most important. :smile:</p></li>
<li><p><a href="http://www.regexr.com/">RegExr v2.0</a><br/>
Very nice rework by Grant Skinner and his team.</p></li>
<li><p><a href="https://github.com/joyent/node/wiki/node-core-vs-userland">node core vs userland</a><br/>
IMO the most important node paradigm. Only things that are very hard to implement for a module or almost everyone needs belong to node core. Everything else is userland.</p></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[read later #5]]></title>
    <link href="http://apdevblog.com/read-later-5/"/>
    <updated>2014-03-31T23:00:00+02:00</updated>
    <id>http://apdevblog.com/read-later-5</id>
    <content type="html"><![CDATA[<p>Our &ldquo;weekly digest&rdquo; is now &ldquo;read later&rdquo; &ndash; because it wasn&rsquo;t really weekly and
most of the links were older anyway.<br/>
So, without further ado, here are the links:</p>

<ul>
<li><a href="http://edgeconf.com/2014-london">Edge conference videos are online</a><br/>
&ldquo;Relive every second of Edge with complete video coverage&rdquo;. Will do.</li>
<li><a href="http://mdo.github.io/code-guide/">Code Guide by @mdo</a><br/>
We are also planning to create Code Conventions apdev edition</li>
<li><a href="http://en.wikipedia.org/wiki/Heisenbug">What is a Heisenbug</a><br/>
Conquer the Heisenbug, one of the most satisfying things in programming.</li>
<li><a href="http://blog.intechnica.co.uk/2014/03/07/how-much-damage-does-third-party-content-really-do-to-ecommerce-sites-report/">How much damage does third party content really do to e-commerce sites?</a><br/>
Some additional info to our <a href="http://apdevblog.com/mobile-performance-and-how-it-suffers-from-3rd-party-content/">own post about 3rd party content</a></li>
<li><a href="http://www.nomachetejuggling.com/2014/02/05/top-10-career-changing-programming-books/">10 career-changing programming books</a><br/>
If you still need to read some more about programming &hellip; maybe there&rsquo;s a book
on this list, that you haven&rsquo;t read already.</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[gruntjs and its plugins]]></title>
    <link href="http://apdevblog.com/gruntjs-and-its-plugins/"/>
    <updated>2014-03-21T11:00:00+01:00</updated>
    <id>http://apdevblog.com/gruntjs-and-its-plugins</id>
    <content type="html"><![CDATA[<p>&hellip; or even more suitable: &ldquo;plugin mess&rdquo;. But I&rsquo;ll start at the beginning.</p>

<p>I wanted to take look at <a href="http://gruntjs.com/">Grunt</a> for a while now. Finally, a couple of weeks
ago, I ran into a problem that I couldn&rsquo;t overcome with <a href="https://www.npmjs.org/">NPM</a> and its <code>scripts</code>
section alone. The right time to check out Grunt, I thought. Has been too long
on my TODO-list.</p>

<p>The problem I had to handle was: deployment of a node project to my dev server.
And I didn&rsquo;t want to rely on git hooks or CI for this. I wanted a script I
could call that uploaded everything to my dev server via ssh, ran <code>npm install</code>
and restarted the application (more on this in a seperate post later).</p>

<p>After googling for &ldquo;deploy grunt&rdquo; and reading through the gazillion results, it
became quickly clear to me, that I wasn&rsquo;t the first person that wanted to try
and do this with the help of the boar (or whatever <a href="http://gruntjs.com/img/grunt-logo.png">this thing</a> is).
I installed about every grunt plugin I could find but none fit my needs exactly
&hellip; so what now? Fork another plugin and adjust it to comply with my
requirements? Write my own plugin from scratch?</p>

<p>No, no, no. I might want to reuse this later on with a different server (no ssh)
or deploy a frontend-project (not a node one) and would have to update/extend
the plugin to accomodate this. Most authors of the aforementioned plugins have
done exactly that &ndash; they wrote a plugin that did what they needed 100%. But
because we as developers are very biased and only want to do it &ldquo;our way&rdquo;, these
plugins are all the same but at the same time different. And, of course, I didn&rsquo;t
like a single one of them :).</p>

<p>Ok, so no plugin. What then. I decided to write a simple Grunt task that used
other node modules (not Grunt plugins) to accomplish everything I needed. In
this, I could reuse code that others had written &ndash; maybe not for this use-case
but as a standalone module that was so granular that it &ldquo;only&rdquo; solved <em>one</em>
problem but that perfectly.</p>

<p><strong>Don&rsquo;t write plugins that try to solve too many problems at once.</strong></p>

<p>Write small modules that run in all kind of environments and solve exactly one
problem. This way, others can use your code &ndash; even in scenarios which you
haven&rsquo;t been thinking about while writing the code &hellip; this is a good thing.</p>

<p>Funny enough, a couple of days after my first stint with Grunt, I listened to
the <a href="http://javascriptjabber.com/097-jsj-gulp-js-with-eric-schoffstall/">JavaScript Jabber podcast</a> and they were talking about <a href="http://gulpjs.com/">Gulp</a> (and
Grunt, of course) with <a href="https://twitter.com/eschoff">Eric Schoffstall</a> &ndash; one of the developers behind &ldquo;the
streaming build system&rdquo;.
It was really interesting and you should definitely go and listen to it (after
you&rsquo;ve finished reading the post).</p>

<p>I don&rsquo;t want to talk about performance or other important reasons why A is
better than B, but there are two things &ndash; in my eyes &ndash; Gulp (or the Gulp
community) does better than Grunt:</p>

<p>1) There is a curated <a href="http://gulpjs.com/plugins/">plugin list</a></p>

<blockquote><p>So, we’re curating it. We don’t just come out and say, “Oh this plugin sucks. We think it’s stupid. You’re not allowed on our website.” But we do say, “This plugin doesn’t work,” or, “It has too many bugs. It just shouldn’t be a plugin. And those are our requirements.”</p><footer><strong>Eric Schoffstall</strong> <cite>Episode 97 of the JavaScript Jabber</cite></footer></blockquote>


<p>So there shouldn&rsquo;t be duplicate plugins or plugins that wrap some functionality
you could use directly via its node module</p>

<p>2) <a href="https://github.com/gulpjs/gulp/tree/master/docs/recipes">Recipes</a> instead of too many plugins</p>

<blockquote><p>[&#8230;] we actually have a section of docs called recipes. And these are just, “Hey, I used Mocha with Gulp. Here is a task that I used it in and let me explain why I did a couple of things.” So that way, people can just say, “Okay, I want to use Mocha. Let me go look at the recipes and go find one for Mocha.” And it’ll explain in-depth why they did what they did, how to use it [&#8230;]</p><footer><strong>Eric Schoffstall</strong> <cite>Episode 97 of the JavaScript Jabber</cite></footer></blockquote>


<p>If there is a problem you solved by using already available node modules, share
it with other devs that might get stuck at the same point. But don&rsquo;t do it
writing your own plugin &ndash; write a small HOWTO instead.</p>

<p><strong>Don&rsquo;t write code that can&rsquo;t be used outside of Grunt/Gulp</strong></p>

<p>Cheers.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[weekly digest #4]]></title>
    <link href="http://apdevblog.com/weekly-digest-4/"/>
    <updated>2014-03-03T11:00:00+01:00</updated>
    <id>http://apdevblog.com/weekly-digest-4</id>
    <content type="html"><![CDATA[<p>This issue of our weekly digest has a bit of everything: performance, tools,
security &ndash; audio, video, text &hellip; Happy reading, watching, listening.<!-- more --></p>

<ul>
<li><a href="http://www.jonathanklein.net/2014/02/revisiting-cookieless-domain.html">Cookieless Domain vs. DNS lookups</a><br/>
Finally some stats regarding that issue.</li>
<li><a href="http://javascriptjabber.com/097-jsj-gulp-js-with-eric-schoffstall/">Gulp vs. Grunt</a><br/>
The guys from the JavaScript Jabber podcast talk to <a href="https://twitter.com/eschoff">Eric Schoffstall</a>, one of the creators behind <a href="http://gulpjs.com/">gulp.js</a>. Really interesting background info why they created a new build-system (instead of just using grunt).</li>
<li><a href="https://www.youtube.com/watch?v=_P9HqHVPeik">Stephen Wolfram&rsquo;s Introduction to the Wolfram Language</a><br/>
Well, we should have a look at it, right?</li>
<li><a href="http://atom.io/">ATOM</a><br/>
New &ldquo;hackable&rdquo; text editor from the people we love @ Github. Can&rsquo;t wait to get our hands on this one.</li>
<li><a href="https://crackstation.net/hashing-security.htm">Salted Password Hashing &ndash; Doing it Right</a><br/>
Some more input on securely saving passwords &ndash; as an addition to the article from last week: <a href="http://apdevblog.com/salt-n-pepper-or-how-to-store-passwords-securely/">salt&#8217;n&#8217;pepper &ndash; or how to store passwords securely</a></li>
</ul>


<p>Fun fact: You can check who added what link by <a href="https://github.com/apdev/apdevblog.com/commits/master">checking the commits</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[salt'n'pepper - or how to store passwords securely]]></title>
    <link href="http://apdevblog.com/salt-n-pepper-or-how-to-store-passwords-securely/"/>
    <updated>2014-02-17T12:00:00+01:00</updated>
    <id>http://apdevblog.com/salt-n-pepper-or-how-to-store-passwords-securely</id>
    <content type="html"><![CDATA[<p>Just yesterday I received an email from <a href="https://www.kickstarter.com/">kickstarter</a> telling me that their database
servers were hacked and that someone was able to access all my account data:</p>

<blockquote><p>Accessed information included usernames, email addresses, mailing addresses, phone numbers, and encrypted passwords. Actual passwords were not revealed, [&hellip;]</p></blockquote>

<p>Good to know, that they didn&rsquo;t store the password in plain text but encrypted the
most sensitive data.</p>

<p>You see, in today&rsquo;s world it is very important to make sure that an attacker,
after aquiring your user data, isn&rsquo;t able to just know you password and:</p>

<p>1) log into your account<br/>
2) more importantly &ndash; use the password for other services with which he/she
could really &ldquo;harm&rdquo; you</p>

<blockquote><p>[&hellip;] however it is possible for a malicious person with enough computing power to guess and crack an encrypted password, particularly a weak or obvious one.</p></blockquote>

<p>That&rsquo;s true &ndash; even when using encryption. So, we as developers have to make sure
that this process of &ldquo;computing&rdquo; or &ldquo;guessing&rdquo; takes as long as possible (and has to be repeated for every password).</p>

<p>I can remember my first attemps at server-side user-management with PHP 13-14 years
ago. I just stored the user&rsquo;s data like this:</p>

<pre>
----------------------
| user | password    |
----------------------
| phil | topsecret   |
| aron | notsosecret |
----------------------
</pre>


<p>The user sent the data via <code>POST</code> to the server where I would compare the password he/she just entered with the password stored inside my database like this:</p>

<pre>
// don't do this
if ($_POST["password"] === $row["password"]) {
  // grant access ...
}
</pre>


<p>If the user ever forgot his/her password, I could just get it from the DB and resend it via email :( &ndash; a really bad idea! But back in the days I didn&rsquo;t know better.</p>

<p><strong>Rule #1</strong><br/>
Never save passwords in plain text</p>

<p>Use a <a href="http://en.wikipedia.org/wiki/Hash_function">hash function</a> to store the password securely. Hash functions are one-way
algorithms that turn your plain text password into a totally unrecognizable number
and letter combination. Plus they produce results that differ completely even if
the input is almost the same.</p>

<p><em>The following snippets are JavaScript because our last project was a node.js
project &ndash; but these functions are available in other programming languages as
well.</em></p>

<div><a href='https://gist.github.com/pkyeck/9048021#sha256.js' target='_blank' data-json-url='https://gist.github.com/pkyeck/9048021.json' data-file='sha256.js'>View code on gist.github.com</a></code>
<noscript><pre><code>var crypto = require(&quot;crypto&quot;);

var hash1 = crypto.createHash(&quot;sha256&quot;);
hash1.update(&quot;topsecret&quot;, &quot;utf8&quot;);
console.log(hash1.digest(&quot;base64&quot;));  // UzNqZ2xkwTllU7K3yS84EmdognyTtk2RQgacEO2npyE=

var hash2 = crypto.createHash(&quot;sha256&quot;);
hash2.update(&quot;topSecret&quot;, &quot;utf8&quot;);
console.log(hash2.digest(&quot;base64&quot;));  // mFuUzsUvYbRLt21+34+fwP6IpapAhqBLsbycLnGfQIc=</code></pre></noscript></div>


<p>You get the idea &hellip;</p>

<p>But this is not enough. If we save the once hashed password into the DB, hackers
can use methods like &ldquo;<a href="http://en.wikipedia.org/wiki/Dictionary_attack">dictionary</a>&rdquo;&ndash;, &ldquo;<a href="http://en.wikipedia.org/wiki/Brute-force_attack">brute force</a>&rdquo;-attacks, <a href="http://en.wikipedia.org/wiki/Lookup_table">lookup</a>&ndash; or
<a href="http://en.wikipedia.org/wiki/Rainbow_table">rainbow-tables</a> to &ldquo;guess&rdquo; your password. It&rsquo;ll take some time, but they will get
there &ndash; and even faster if the original password only contains letters from a-z.</p>

<p><strong>Rule #2</strong><br/>
Salt your password before saving it</p>

<p>Hashing function always produce the same result if the input is the same. You
can run the code above as many times as you like, the result won&rsquo;t change.<br/>
Two users with the same password would get the same hash, which makes it easier
for an attacker because he/she can use lookup- or rainbow-tables to &ldquo;decrypt&rdquo;
your password.</p>

<p>Appending a <strong>random</strong> string (a <a href="http://en.wikipedia.org/wiki/Salt_(cryptography)">salt</a>) to the password makes this impossible.</p>

<p><em>I don&rsquo;t know if they still do, but in a previous version wordpress used the
hashed <code>SITE_URL</code> as a salt for every password &ndash; not good. You can do better
than that.</em></p>

<p>Create a new salt everytime you store a password in the database &ndash; never reuse
an old one. And don&rsquo;t make the salt to short. It gets harder to hack the longer
the salt is.</p>

<pre>
var crypto = require("crypto");
var salt = crypto.randomBytes(64);
</pre>


<p>If you don&rsquo;t use the same salt for every password, you have to store the salt in
the database as well. Either in a seperate column or add it to the password as
&ldquo;metadata&rdquo;.</p>

<pre>
------------------------------------------
| user | password                        |
------------------------------------------
| phil | &lt;hashed password + salt&gt;:&lt;salt&gt; |
| aron | &lt;hashed password + salt&gt;:&lt;salt&gt; |
------------------------------------------
</pre>


<p>Salting the passwords prevents hackers from using lookup-tables to gain access
to your passwords <strong>quickly</strong> but with up-to-date hardware they can use &ldquo;brute force&rdquo;
to generate billions of hashes a second and compare them with the hashes you
stored in the database.</p>

<p><strong>Rule #3</strong><br/>
Use &ldquo;slow&rdquo; hash functions</p>

<p>This may sound strange but another method to make sure it&rsquo;ll take attackers more
time to hack the passwords is to use an encryption algorithm that takes it time
to produce a result. This reduces the number of hashes a hacker can generate to
a fraction of the number above.</p>

<p><a href="http://en.wikipedia.org/wiki/PBKDF2">PBKDF2</a> is a good choice for this.</p>

<p>Storing the encrypted password</p>

<div><a href='https://gist.github.com/pkyeck/9047805#storing.js' target='_blank' data-json-url='https://gist.github.com/pkyeck/9047805.json' data-file='storing.js'>View code on gist.github.com</a></code>
<noscript><pre><code>var crypto = require(&quot;crypto&quot;);

var pwd = &quot;topsecret&quot;;

// create random salt
var salt = crypto.randomBytes(64);

// encrypt+salt password
var encrypted = crypto.pbkdf2(pwd, salt, 10000, 64, function(err, key) {
  if (err) {
    return reject(err);
  }
  key.toString(&quot;base64&quot;));
});

// combine hash + salt
var toBeStored = encrypted + &quot;:&quot; + salt.toString(&quot;base64&quot;);

// store in DB in one column ...

</code></pre></noscript></div>


<p>Retrieving the encrypted password</p>

<div><a href='https://gist.github.com/pkyeck/9047805#retrieving.js' target='_blank' data-json-url='https://gist.github.com/pkyeck/9047805.json' data-file='retrieving.js'>View code on gist.github.com</a></code>
<noscript><pre><code>var crypto = require(&quot;crypto&quot;);

// user submitted form with email + pwd
var pwd = req.params.pwd;

// fetch result from DB ...

// retrieve hash from DB and compare to pwd
var result = &lt;RESULT&gt;;

var meta = fromStore.split(&quot;:&quot;);
var salt = new Buffer(meta[1], &quot;base64&quot;);
var hash = meta[0];

// encrypt+salt password
var encrypted = crypto.pbkdf2(pwd, salt, 10000, 64, function(err, key) {
  if (err) {
    return reject(err);
  }
  key.toString(&quot;base64&quot;));
});

// check if passwords match
if (hash !== encrypted) {
  throw new Error(&quot;credentials unknown&quot;);
}

// user is logged in ...</code></pre></noscript></div>


<p>Hope this summary helps and makes your next project a bit more secure (than it already is).</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[weekly digest #3]]></title>
    <link href="http://apdevblog.com/weekly-digest-3/"/>
    <updated>2014-02-10T11:00:00+01:00</updated>
    <id>http://apdevblog.com/weekly-digest-3</id>
    <content type="html"><![CDATA[<p>This week&rsquo;s digest is about optimization &ndash; how to get more out of mobile performance, Responsive Web Design, your tools, your daily working hours and JS in general. Happy reading.<!-- more --></p>

<ul>
<li><a href="http://programming.oreilly.com/2014/02/responsive-web-design-performance.html">Responsive Web Design Performance</a><br/>
Etsy&rsquo;s Lara Swanson talks &ldquo;mobile first&rdquo; (Design + Performance)</li>
<li><a href="http://oreillynet.com/pub/e/2999">Performance for Responsive Web Design</a><br/>
Free webcast presented by Maximiliano Firtman</li>
<li><a href="http://ariya.ofilabs.com/2014/02/javascript-array-slice-vs-splice.html">JavaScript Array: slice vs splice</a><br/>
Convention of how to distinguish between modifying and non-modifying object method calls.</li>
<li><a href="http://www.paulgraham.com/makersschedule.html">Maker&rsquo;s Schedule, Manager&rsquo;s Schedule</a><br/>
Paul Graham&rsquo;s piece about &ldquo;flow&rdquo; is already 5 years old but still true today.</li>
<li><a href="https://vimeo.com/36579366">Inventing on Principle</a><br/>
Great talk by Bret Victor about how our tools (or programming in general) should work nowadays.</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[To use jQuery or not to use jQuery, that is the question]]></title>
    <link href="http://apdevblog.com/to-use-jquery-or-not-to-use-jquery/"/>
    <updated>2014-02-07T11:00:00+01:00</updated>
    <id>http://apdevblog.com/to-use-jquery-or-not-to-use-jquery</id>
    <content type="html"><![CDATA[<p>There was a lot of buzz around <a href="http://youmightnotneedjquery.com/">YOU MIGHT NOT NEED JQUERY</a> the last couple
of days &ndash; we also mentioned it in our <a href="http://apdevblog.com/weekly-digest-2/">weekly digest #2</a> from last monday.</p>

<p>YMNNJ is a collection of vanilla JavaScript snippets that can (more or less)
replace some often used jQuery built-in functions. <a href="https://twitter.com/zackbloom">Zack Bloom</a> and
<a href="https://twitter.com/adamfschwartz">Adam Schwartz</a>, the guys behind YMNNJ, want us to think twice before using a
(big) framework/library like jQuery:</p>

<blockquote><p>If you&rsquo;re developing a library on the other hand, please take a moment to consider if you actually need jQuery as a dependency. Maybe you can include a few lines of utility code, and forgo the requirement.</p></blockquote>

<p>I totally agree with this statement. You should pause and ponder before using
something like jQuery (or e.g. Modernizr), be it in a library or any other project you are doing. It comes with a cost that you definitely should be aware of.</p>

<p><a href="https://twitter.com/tkadlec/">Tim Kadlec</a> also wrote a <a href="http://timkadlec.com/2014/01/smart-defaults-on-libraries-and-frameworks/">post</a> and sums up the performance hits pretty well:</p>

<blockquote><p>This is concerning, but it’s not just download sizes that you should be worried about. In a presentation given at Velocity in 2011, Maximiliano Firtman pointed out that on some phones (older, but still popular, BlackBerry devices for example) can take up to 8 seconds just to parse jQuery. More recent research from Stoyan Stefanov revealed that even on iOS 5.1, it was taking as many as 200-300ms to parse jQuery.</p></blockquote>

<p>But jQuery isn&rsquo;t all bad, of course not. Fittingly, <a href="http://webplatformdaily.org/">Webplatform Daily</a> posted
a link to a <a href="https://gist.github.com/rwaldron/8720084#file-reasons-md">list</a> by <a href="https://twitter.com/rwaldron/">Rick Waldron</a> of all the quirks jQuery fixes for us developers that need to address as much devices/browsers as possible. Try fixing (or even finding them in the first place) them in vanilla JS by yourself.</p>

<p>So, if you&rsquo;re aware of the &ldquo;downsides&rdquo; of something like jQuery but decide to use
it anyway (because of all the &ldquo;upsides&rdquo; it includes), it&rsquo;s totally fine. But think
of ways to use it responsibly.</p>

<p>Again taken from Mr. Kadlec&rsquo;s post:</p>

<blockquote><p>[&hellip;] some of the devices we needed to test on couldn’t load the page at all if jQuery was present — it was just too much JavaScript for the device to handle.</p></blockquote>

<p>In one of our last mobile projects we decided to rely on jQuery for all the more
fancy things we wanted to implement. Features that the older phones couldn&rsquo;t or
shouldn&rsquo;t cope with because they would crash or suffer devastating performance
hits that would keep the users from navigating the site. Using
<a href="http://alistapart.com/article/understandingprogressiveenhancement">Progressive enhancement</a> and a technique like the BBC&rsquo;s
<a href="http://alistapart.com/article/understandingprogressiveenhancement">&ldquo;Cutting the mustard&rdquo;</a> allowed us to only deliver JavaScript (including
jQuery) to the more capable devices &ndash; avoiding to make your beloved
Blackberry explode :)<br/>
In addition, the user only had to download HTML, CSS and assets to view the
page, which saved about 40-50KB of transfered data and some JS parsing/execution
time.</p>

<p>Tim&rsquo;s closing paragraph:</p>

<blockquote><p>I’m not saying that we stop using libraries altogether—and neither were the people who created “You Might Not Need jQuery”. I’m suggesting we make that decision with a great deal of care.</p></blockquote>

<p>WORD!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[weekly digest #2]]></title>
    <link href="http://apdevblog.com/weekly-digest-2/"/>
    <updated>2014-02-03T11:00:00+01:00</updated>
    <id>http://apdevblog.com/weekly-digest-2</id>
    <content type="html"><![CDATA[<p>Second week of our link collection &ndash; some old, some new. But each one worth your time.
Of course. Happy reading.<!-- more --></p>

<ul>
<li><a href="http://devswag.com/collections/stickers">DevSwag</a><br/>
Get stickers from open source projects. Support and promote them.</li>
<li><a href="http://youmightnotneedjquery.com/">YOU MIGHT NOT NEED JQUERY</a><br/>
I love jQuery when I need it, but I don&rsquo;t always need it.</li>
<li><a href="http://www.sitepoint.com/sass-mixin-placeholder/">Sass: Mixin or Placeholder?</a><br/>
Understanding the difference.</li>
<li><a href="https://twitter.com/soffes/status/427516075391983616">Map caps locks to control</a><br/>
Do it! ++productivity</li>
<li><a href="http://paulgraham.com/ds.html">Do Things that Don&rsquo;t Scale</a><br/>
Great stories about famous startups and how they got to where they&rsquo;re now.</li>
<li><a href="http://aeon.co/magazine/living-together/james-somers-web-developer-money/">Are coders worth it?</a><br/>
&ldquo;In today’s world, web developers have it all: money, perks, freedom, respect. But is there value in what we do?&rdquo;</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Mobile performance and how it suffers from 3rd party content]]></title>
    <link href="http://apdevblog.com/mobile-performance-and-how-it-suffers-from-3rd-party-content"/>
    <updated>2014-01-30T11:00:00+01:00</updated>
    <id>http://apdevblog.com/mobile-performance-and-how-it-suffers-from-3rd-party-content</id>
    <content type="html"><![CDATA[<p>Nowadays, when you talk to a web developer about building a (mobile) website,
performance is one of the topics that comes up quickly. This is absolutely great
and we have to say &lsquo;thank you&rsquo; to a bunch of <a href="https://twitter.com/Souders">people</a> <a href="https://twitter.com/stoyanstefanov">never</a>
<a href="https://twitter.com/jaffathecake">stopping</a> <a href="https://twitter.com/guypod">to</a> <a href="https://twitter.com/aerotwist">bother</a> <a href="https://twitter.com/jonathanklein">us</a> with these things.</p>

<p>So we developers take care of:</p>

<ul>
<li>gzipping</li>
<li>minify/uglify + concat HTML, JS and CSS</li>
<li>all blocking JS at the bottom of the page</li>
<li>caching/CDN</li>
<li>fewer requests</li>
<li>and a lot more &hellip;</li>
</ul>


<p>Each of the points above will improve your site&rsquo;s performance &ndash; sometimes more
sometimes less, depending &ndash; of course &ndash; on your site and its structure/content.</p>

<p>In one of our last projects we took this to heart and wanted to deliver a
beautiful and fast mobile website (m-dot-domain, so it was really only mobile).
We&rsquo;re not talking about the &ldquo;under 1 second to screen&rdquo; scenario, but rather a
smooth surfing experience using a 3G network. Let&rsquo;s say something like 1-2
seconds until &ldquo;start to render&rdquo; and ~10 seconds for &ldquo;loaded&rdquo;.</p>

<p>Starting with HTML templates/components that &ndash; if needed &ndash; get progressively
enhanced was a good foundation for this. With dummy content filling the HTML,
this is a breakdown of the filesizes (gzipped) by type:</p>

<ul>
<li>HTML ~15KB</li>
<li>CSS ~15KB</li>
<li>JS ~50KB (biggest part being jQuery)</li>
<li>images ~500KB</li>
<li>fonts ~45KB</li>
</ul>


<p>Putting JS at the bottom of the <code>&lt;body&gt;</code> made the browser only wait for the HTML
and CSS before beginning to render the page. Sadly, because we&rsquo;re using
webfonts, it begins rendering the content without displaying the text. Only
after the webfont is downloaded, does the text become visible &ndash; in my eyes
webfonts&#8217; <em>biggest</em> disadvantage.</p>

<p><a href="http://apdevblog.com/images/2014/01/waterfall-templates-complete.png"><img src="http://apdevblog.com/images/2014/01/waterfall-templates-complete.png" width="462" height="513" title="'waterfall for our templates'" ></a></p>

<p><em>I used my slow vServer to get this waterfall, so an optimized setup with an
additional CDN (like the live-server) should be even faster.</em></p>

<p>As we can see in the waterfall above &ndash; &ldquo;start render&rdquo; lies somewhere between 2-3
seconds and the webfonts are loaded a second later. This way the user should be
able to start reading after 4 seconds. So far, so good.</p>

<p>This is also the moment the JS is loaded (async) and executes. Some of the
components &ldquo;change&rdquo; a bit because of the progressive enhancement kicking in but
nothing so serious that it&rsquo;ll annoy the user too much.<br/>
And this is only for the user&rsquo;s first visit &ndash; the second time the cache will be
filled with our CSS, JS, images and fonts and the page load time should be even
faster.</p>

<p>Because our client uses a big fancy CMS, this is the point where we bundled up
our assets and handed them over to them. Their developers had to integrate our
templates and scripts into the colossus that is their CMS <em>and</em> most of all,
they also had to add things like:</p>

<ol>
<li>tracking and</li>
<li>advertising</li>
<li>real content</li>
</ol>


<p>From here on out it got messy &hellip;</p>

<p>Tracking shouldn&rsquo;t have been a big problem, you might think. No, it usually isn&rsquo;t.
But they had to include more than one tracking-script and not at the bottom of
the document but the head (I think we could check and move them to the bottom,
though). These are mostly small scripts that load fast but every request hurts
on mobile.<br/>
There are services like <a href="https://segment.io/">Segment.io</a> but they are US-based
and we Germans don&rsquo;t like to send our data across the pond ;) Besides, I don&rsquo;t
think they support german newspapers&#8217; most important tracking-provider IVW.<br/>
Nonetheless are these kind of services a great tool for improving your website&rsquo;s
performance: one call instead of two or three every time you want to track
something = big win!</p>

<p>On to advertisement. Because our client offers all its content free of charge,
ads are the only source of revenue they got. So these are important and we have
to make sure they are loaded and displayed correctly.<br/>
The setup gets even more complicated because the client uses a &ldquo;middleware&rdquo; that
is based on DoubleClick as the technical foundation &ndash; leading to more and more
scripts that have to be fetched from different servers (double <em>ouch</em>).<br/>
DoubleClick offers an <code>&lt;iframe&gt;</code>-based &ldquo;async&rdquo; version of the <a href="https://support.google.com/dfp_sb/answer/1649768?hl=en">GPT</a> but we
can&rsquo;t use it because the different ad-formats won&rsquo;t play well with iframes =
even more synchronous JavaScript stopping the page from being rendered :(</p>

<p>Most of the time the first ad is located on the top of every page, so the aforementioned
scripts have to be loaded before this (in the <code>&lt;head&gt;</code>). Not 1, not 2,
but 4 (in words: FOUR) scripts are loaded before the custom initialisation is
run and the first ad can be displayed on page &hellip; ah no, I forgot to mention
that every ad loads one or more JS files to get the content they
need. <nobr>S-Y-N-C-H-R-O-N-O-U-S-L-Y.</nobr></p>

<p>All these tracking and ad scripts push the &ldquo;start render&rdquo; back 4.5 seconds which
leads to readers looking at a blank screen for 8 seconds. If you&rsquo;re
lucky, they haven&rsquo;t left the site when the content starts trickling in.<br/>
I&rsquo;d also like to point to the fact that the webfonts are loaded long after the
&ldquo;start render&rdquo; at around 13 seconds which means that even though the browser
starts to render the page, <a href="https://twitter.com/jaffathecake/status/425629232543186944">copy isn&rsquo;t displayed</a> until they are downloaded
completely.</p>

<p>Let&rsquo;s take a look at how this changes the page&rsquo;s waterfall:</p>

<p><a href="http://apdevblog.com/images/2014/01/waterfall-live-time-to-render.png"><img src="http://apdevblog.com/images/2014/01/waterfall-live-time-to-render.png" width="462" height="179" title="'waterfall for live site (time to render)'" ></a></p>

<p>Concluding points 1 and 2 we have to realize that many 3rd party content
providers just don&rsquo;t care about performance &ndash; at least not as much as we
developers would like them to.</p>

<p>The last thing I want to talk about is point 3: real content.<br/>
In our case we built templates for a home-, overview- and article-page using
dummy copy and images that nearly reflected real-life content (at least the
amount we thought made sense considering our <a href="http://timkadlec.com/2013/01/setting-a-performance-budget/">performance budget</a>).<br/>
The client was in too much of a &ldquo;desktop-mode&rdquo; and filled the landingpage with
everything that came to mind. So we ended up with around 50 article-teasers and
multiple more complex gallery widgets etc.</p>

<p><a href="http://apdevblog.com/images/2014/01/waterfall-live-complete.png"><img src="http://apdevblog.com/images/2014/01/waterfall-live-complete.png" width="462" height="791" title="'waterfall for live site (complete)'" ></a></p>

<p>I don&rsquo;t say that it was there mistake (alone), we should have sat down with them
more often and talked about how <a href="http://bradfrostweb.com/blog/post/performance-as-design/">peformance is a feature</a> and how it
(sometimes) is more important than an abundance of different content elements
put on the same page.</p>

<p>Points 1 and 2 destroyed our <em>time to first render</em> and 3 let the page get
way too heavy for a &ldquo;smooth surfing experience using a 3G network&rdquo;.</p>

<p>Let&rsquo;s not bury our heads in the sand and instead learn from a project like this.
But sometimes complaining again and again is enough to make all those shitty 3rd
party widgets/scripts improve. At least I hope so.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[weekly digest #1]]></title>
    <link href="http://apdevblog.com/weekly-digest-1/"/>
    <updated>2014-01-27T10:30:00+01:00</updated>
    <id>http://apdevblog.com/weekly-digest-1</id>
    <content type="html"><![CDATA[<p>Today we are starting a new type of post: a weekly digest of articles, videos
or just links we stumbled upon during the last week. They don&rsquo;t have to be
brand-new, hyped or hot on Hacker News to get onto our list (link #1 is from 2012)
&hellip; instead being great, entertaining or just full of good stuff helps a lot :)<!-- more --></p>

<p>Maybe we got something you haven&rsquo;t read/watched before:</p>

<ul>
<li><a href="http://www.youtube.com/watch?v=qI_g07C_Q5I">Introduction to NoSQL by Martin Fowler</a><br/>
Great introductory video about NoSQL and its rivalry with other RDBMS.</li>
<li><a href="http://www.mongodb.com/webinar/intro_mongodb_jan14">Webinar: Getting Started with MongoDB &ndash; Back to Basics</a><br/>
If you want to dive into developing applications backed by MongoDB.</li>
<li><a href="http://www.youtube.com/watch?v=x7cQ3mrcKaY">Pete Hunt: React: Rethinking best practices &mdash; JSConf EU 2013</a><br/>
Very tempting their approach of always be able to render the current state of the application. Their virtual DOM sounds like voodoo. We need to do a real life test.</li>
<li><a href="http://www.joelonsoftware.com/items/2007/06/07.html">A game of inches</a><br/>
Ever worked two days on an optimization of a feature? An optimization that most users probably won&rsquo;t notice and is &ndash; in manager terms &ndash; very minor? You worked on it because it makes a difference. Because software development is a &ldquo;game of inches&rdquo;.</li>
<li><a href="http://www.theverge.com/2013/11/5/5039216/everpix-life-and-death-inside-the-worlds-best-photo-startup">Everpix &ndash; the story</a><br/>
&ldquo;why the world&rsquo;s best photo startup is going out of business&rdquo;</li>
<li><a href="https://github.com/everpix/Everpix-Intelligence">Everpix intelligence</a><br/>
Uncensored Everpix metrics, financials and business data for your perusing</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Octopress/Jekyll + S3 + CloudFront + gzip]]></title>
    <link href="http://apdevblog.com/octopress-jekyll-s3-cloudfront-gzip"/>
    <updated>2014-01-08T11:12:29+01:00</updated>
    <id>http://apdevblog.com/octopress-slash-jekyll-plus-s3-plus-cloudfront-plus-gzip</id>
    <content type="html"><![CDATA[<p>We recently moved this blog from WordPress to Octopress. Static pages FTW! And because we have static pages we want to use every <a href="http://de.wikipedia.org/wiki/Content_Delivery_Network">CDN</a> power we can get. We choose to use <a href="http://aws.amazon.com/cloudfront/">AWS CloudFront</a>. There are some fine tutorials on the interweb how to make all this work:</p>

<p><a href="http://www.maxmasnick.com/2012/01/21/jekyll_s3_cloudfront/">Blogging with Jekyll + S3 + CloudFront</a><br/>
<a href="http://www.jerome-bernard.com/blog/2011/08/20/quick-tip-for-easily-deploying-octopress-blog-on-amazon-cloudfront/">Quick Tip for Easily Deploying Octopress Blog on Amazon CloudFront</a></p>

<p>One open thing was to enable gzipped content. Zipping is certainly the most obvious performance optimization. Usually the webserver takes care of it, but we don&rsquo;t have a webserver here. We have a CDN and like every CDN CloudFront doesn&rsquo;t &ldquo;process&rdquo; request, it just serves files. Meaning that by definition it does not compress files when requested by a client. However that only means that we need to create zipped files when we deploy the Octopress files. A very good starting point:</p>

<p><a href="http://www.furida.mu/blog/2012/02/29/gzip-your-octopress/">gzip your Octopress</a></p>

<p>I made some modifications regarding CloudFront. Also <code>rename</code> is not available on OSX so I changed it around a bit.</p>

<div><a href='https://gist.github.com/aronwoost/8298023#gziped_sync.sh' target='_blank' data-json-url='https://gist.github.com/aronwoost/8298023.json' data-file='gziped_sync.sh'>View code on gist.github.com</a></code>
<noscript><pre><code>#!/bin/bash

# compress the files if they aren&#39;t
find public/ -iname &#39;*.html&#39; -exec ./gzip_if_not_gzipped.sh {} \;
find public/ -iname &#39;*.js&#39; -exec ./gzip_if_not_gzipped.sh {} \;
find public/ -iname &#39;*.css&#39; -exec ./gzip_if_not_gzipped.sh {} \;

# change their name back
find public -iname &#39;*.gz&#39; -exec bash -c &#39;mv $0 ${0/.gz/}&#39; {} \;
echo &quot;gzipping successful&quot;

echo &quot;syncing gzipped files&quot;
s3cmd sync --progress --acl-public --reduced-redundancy --cf-invalidate --verbose --cf-invalidate-default-index --add-header &#39;Content-Encoding:gzip&#39; public/* s3://$1/ --exclude &#39;*.*&#39; --include &#39;*.html&#39; --include &#39;*.js&#39; --include &#39;*.css&#39;
echo &quot;syncing gzipped files complete&quot;

echo &quot;syncing non-gzipped files&quot;
s3cmd sync --progress --acl-public --reduced-redundancy --cf-invalidate --verbose public/* s3://$1/ --exclude &#39;*.html&#39; --exclude &#39;*.js&#39; --exclude &#39;*.css&#39;
echo &quot;syncing non-gzipped files complete&quot;</code></pre></noscript></div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[How to save the positions of annotations in epubs (or html)]]></title>
    <link href="http://apdevblog.com/how-to-save-the-positions-of-annotations-in-epubs-or-html/"/>
    <updated>2012-07-30T01:00:00+02:00</updated>
    <id>http://apdevblog.com/how-to-save-the-positions-of-annotations-in-epubs-or-html</id>
    <content type="html"><![CDATA[<p>The biggest advantage of ebooks over printed ones is &#8211; of course &#8211; that there are no more fixed pages. Every device can decide how it wants to display the content. With digital books it is possible to have the same excellent user experience on a small smartphone screen as well as on a huge desktop monitor.</p>

<p>But with all these advantages there also come challenges. One is positioning inside the book. So what to do, when you want to save the user&#8217;s current reading position, so that he can continue on the same &#8220;page&#8221; when he comes back? Since the user can read the text on different devices with different screen resolutions and different text settings (e.g. font size settings) you can not simply save &#8220;Page 36&#8243;.</p>

<!--more-->


<h3>Last read page (rough position)</h3>

<p>If you just want the rough position (i.e. no certain text) the plain percentage is a way to go. The <a href="https://github.com/joseph/Monocle" target="_blank">monocle framework</a> (that we use at <a href="http://nim.bi/" target="_blank">Nimbi</a> and <a href="http://paperc.com/" target="_blank">PaperC</a>) <a href="https://github.com/joseph/Monocle/issues/116" target="_blank">does exactly that</a>. It saves the reading position by dividing the current page number by the total number of pages (base on the rendering on the current device). On a smartphone the user might be on page 50 of 100 pages total where he is on page 6 of 12 pages total on his desktop browser. Both lead to roughly the same position.</p>

<p>We must be aware that there is a certain cognitive load for the user to find his actual last sentence on a page when changing devices or even changing from portait to landscape mode on his tablet. We have no chance to save the actual last words the user read.</p>

<h3>Certain text</h3>

<p>When it comes to saving annotations we need something more accurate. The user wants to highlight a certain sentence. We need a way to save the exact text so we can allow the user to navigate to that text directly and visually highlight the text.</p>

<h3>Text and offset (or index)</h3>

<p>Just saving the selected string as text and do a full text search when trying to find the string won&#8217;t do the job, since finding the actual word is impossible when the user selected a word like e.g. &#8220;and&#8221;. So together with the actual string we need <em>at least</em> an offset from the beginning of the text (which is usually a chapter in ebooks, also called component).</p>

<p>This is a typical way to deal with search results. Search engines usually deliver results based on full text search. When they index a HTML document they strip away all markup leaving the text only. So what they can give you is the found string and an offset (numbers of characters) from the beginning of the document. A result for a term like &#8220;and&#8221; (which is usually a <a href="http://en.wikipedia.org/wiki/Stop_words" target="_blank">stop word</a> in search engines) will contain different offsets for the same term.</p>

<p>The bad news is, that in order for the offset to be accurate, you need to make sure that your local text-online version of the HTML document is <em>exactly the same</em> as the one your search engine indexed. You need to know exactly how/if the search engine converts linebreaks to spaces and how the search engine deals with spaces/linebreaks between DOM nodes (and other things). And then you have to apply these rules to the implementations on your client.</p>

<p>To avoid the &#8220;offset-hell&#8221; you could tell your search engine to deliver an index of the appearance in the document instead of an offset. Assuming you have three appearances of the term &#8220;and&#8221; the search engine could deliver the index 0, 1 or 2 to a result dataset. With that you are always certain which term you are dealing with.</p>

<p>Either way it&#8217;s not trivial to first convert the markup to text-only and then, after you found the term, find this term back in your DOM tree. Also note, that operations on huge amounts of text can be CPU-intensive, especially on mobile devices.</p>

<h3>DOM based</h3>

<p>Back to the case where a user wants to highlight a portion of text. Why not letting the DOM help us a little. We can make use of the <a href="https://developer.mozilla.org/en/DOM/range" target="_blank">Range obj</a>.</p>

<p>&#8220;The Range object represents a fragment of a document that can contain nodes and parts of text nodes in a given document.&#8221;</p>

<p>That seems like the perfect fit for our use case. And it is! While it&#8217;s not supported by every browser environment it is well supported on engines that are typically used for displaying ebook (epub) content. For our &#8220;and&#8221; example it would look like this (jQuery pseuso code):</p>

<div><a href='https://gist.github.com/3209510#example1.js' target='_blank' data-json-url='https://gist.github.com/3209510.json' data-file='example1.js'>View code on gist.github.com</a></code>
<noscript><pre><code>/* 
Looking for &quot;and&quot;. 
Markup is the following:

&lt;body&gt;
  &lt;div id=&quot;text&quot;&gt;Pride and Prejudice&lt;/div&gt;
&lt;/body&gt;
*/

var range = {
  startContainer:$(&quot;text&quot;), 
  startOffset:6, 
  endContainer:$(&quot;text&quot;), 
  endOffset:9
}</code></pre></noscript></div>


<p>The range of a more complex text (that runs over more that one DOM node) could look like this (user selected &#8220;Prejudice is a book&#8221;):</p>

<div><a href='https://gist.github.com/aronwoost/3209510#example2.js' target='_blank' data-json-url='https://gist.github.com/aronwoost/3209510.json' data-file='example2.js'>View code on gist.github.com</a></code>
<noscript><pre><code>/* 
Looking for &quot;Prejudice is a book&quot;. 
Markup is the following:

&lt;body&gt;
  &lt;div id=&quot;headline&quot;&gt;Pride and Prejudice&lt;/div&gt;
  &lt;div id=&quot;text&quot;&gt;is a book, that is very popular&lt;/div&gt;
&lt;/body&gt;
*/

var range = {
  startContainer:$(&quot;headline&quot;), 
  startOffset:10, 
  endContainer:$(&quot;text&quot;), 
  endOffset:9
}</code></pre></noscript></div>


<p>This is a good starting point for describing fragments of text embedded in html. If you want to locate the visual position of this range you can call</p>

<div><a href='https://gist.github.com/aronwoost/3209510#example3.js' target='_blank' data-json-url='https://gist.github.com/aronwoost/3209510.json' data-file='example3.js'>View code on gist.github.com</a></code>
<noscript><pre><code>range.getBoundingClientRect();
</code></pre></noscript></div>


<h3>How to save it in the backend</h3>

<p>For saving the dataset to the backend we need a string representation of the DOM elements. Different approaches are possible. As XPath it could look something like this:</p>

<div><a href='https://gist.github.com/aronwoost/3209510#example4.js' target='_blank' data-json-url='https://gist.github.com/aronwoost/3209510.json' data-file='example4.js'>View code on gist.github.com</a></code>
<noscript><pre><code>/* 
Looking for &quot;and&quot;. 
Markup is the following:

&lt;body&gt;
  &lt;div id=&quot;text&quot;&gt;Pride and Prejudice&lt;/div&gt;
&lt;/body&gt;
*/

var highlight = {
  text:&quot;and&quot;, 
  startContainer:&quot;//*[@id=&#39;text&#39;]&quot;, 
  startOffset:6, 
  endContainer:&quot;//*[@id=&#39;text&#39;]&quot;, 
  endOffset:9
}</code></pre></noscript></div>


<p>Or, if you don&#8217;t want to rely on elements to have id&#8217;s or classes:</p>

<div><a href='https://gist.github.com/aronwoost/3209510#example5.js' target='_blank' data-json-url='https://gist.github.com/aronwoost/3209510.json' data-file='example5.js'>View code on gist.github.com</a></code>
<noscript><pre><code>/* 
Looking for &quot;and&quot;. 
Markup is the following:

&lt;body&gt;
  &lt;div id=&quot;text&quot;&gt;Pride and Prejudice&lt;/div&gt;
&lt;/body&gt;
*/

var highlight = {
  text:&quot;and&quot;, 
  startContainer:&quot;/html/body/node()[1]&quot;, 
  startOffset:6, 
  endContainer:&quot;/html/body/node()[1]&quot;, 
  endOffset:9
}</code></pre></noscript></div>


<p>Yap, in Xpath the index starts with 1.</p>

<p>However, for Nimbi and PaperC we tried to keep the data smaller (it&#8217;s potentially big data, right?) and query language agnostic:</p>

<div><a href='https://gist.github.com/aronwoost/3209510#example6.js' target='_blank' data-json-url='https://gist.github.com/aronwoost/3209510.json' data-file='example6.js'>View code on gist.github.com</a></code>
<noscript><pre><code>/* 
Looking for &quot;and&quot;. 
Markup is the following:

&lt;body&gt;
  &lt;div id=&quot;text&quot;&gt;Pride and Prejudice&lt;/div&gt;
&lt;/body&gt;
*/

var highlight = {
  text:&quot;and&quot;, 
  startContainer:&quot;0/0&quot;, 
  startOffset:6, 
  endContainer:&quot;0/0&quot;, 
  endOffset:9
}</code></pre></noscript></div>


<p>startContainer and endContainer are the first textnode in the frist childNode of body (i.e. body.childNodes[0].childNodes[0]). The dataset is smaller (less to send over the wire and to save in the db) and &#8211; more importantly &#8211; it does not rely on XPath. While XPath might not be the bottleneck in this kind of application, it is known for not necessarily being fast, especially on mobile devices. And there is no XPath on Android &lt; 2.4.</p>

<p>Here is an example of a more complex DOM tree:</p>

<div><a href='https://gist.github.com/aronwoost/3209510#example7.js' target='_blank' data-json-url='https://gist.github.com/aronwoost/3209510.json' data-file='example7.js'>View code on gist.github.com</a></code>
<noscript><pre><code>/* 
Looking for &quot;Prejudice&quot;. 
Markup is the following: 

&lt;body&gt;
  &lt;div&gt;
    &lt;div&gt;A classic:&lt;/div&gt;
    &lt;div&gt;Pride &lt;span&gt;and&lt;/span&gt; Prejudice&lt;/div&gt;
  &lt;/div&gt;
&lt;/body&gt;
*/

var highlight = {
  text:&quot;Prejudice&quot;,
  startContainer:&quot;0/1/2&quot;, 
  startOffset:1, 
  endContainer:&quot;0/1/2&quot;, 
  endOffset:10
}</code></pre></noscript></div>


<p>This pretty much solved the position issue for us.</p>

<h3>Pros</h3>

<p>The position is 100% unique. You&#8217;ll find the fragment no matter the screen size or font settings. You can even save the position of a single character.</p>

<p>It makes use of the most basic HTML. The DOM tree was there since Day 1 of the HTML standard. So even the oldest and most outdated html parser should be able to find the fragment.</p>

<p>It&#8217;s query agnostic. You don&#8217;t need XPath or other stuff.</p>

<p>Very small data footprint.</p>

<h3>Cons</h3>

<p>Since the position is described in a DOM tree, the backend needs a XML/HTML parser to &#8220;understand&#8221; the position. Asume that you want the backend to create a &#8220;heatmap&#8221; of the book, where the users set highlights, where they stopped reading and so on. The backend will have to convert the position to it&#8217;s own positioning format (if it is not using the DOM tree in the first place).</p>

<p>Also the implementation relies on a valid DOM. Epub components (a html chapter of an epub) are typically XHTML. If they are not served with mimetype application/xhtml the browser will fall into quirks mode. When you now have DOM elements that are only valid according to XHTML they will not correctly hook into the DOM tree. Since we are using a numeric tree representation (e.g. 12/0/4/0) one invalid DOM element can mess up the whole tree and we won&#8217;t be able to find the elements. While this may sound scary, setting the correct mimetype will fix the issue entirely.</p>

<h3>Last reading position 2.0</h3>

<p>Eventually we decided to also use this approach to save the user&#8217;s last reading position. We now save the first character on a page as the current reading position. We thought back and forth if we might should take a character somewhere from the middle of the page. However with the first character approach we can be sure, that the user always sees something that he has already read, so that he can easily find the last read sentence.</p>

<h3>What&#8217;s next?</h3>

<p>Well, the hard part is &#8211; of course &#8211; to let the user select text (hint: window.getSelection()) and to visually highlight text fragments. But this is something for another blog post&#8230; :)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Do not &#8220;learn&#8221; jQuery, learn backbone.js and require.js]]></title>
    <link href="http://apdevblog.com/learn-backbone-and-require/"/>
    <updated>2012-07-02T01:00:00+02:00</updated>
    <id>http://apdevblog.com/learn-backbone-and-require</id>
    <content type="html"><![CDATA[<p>So you wanna join the little HTML5 party some of us are having? Great! It&#8217;s not that packed yet, quite comfortable actually. And the main gigs haven’t even played yet. So no, you&#8217;re not late. Quite one time actually.</p>

<!--more-->


<p><strong>Dear JavaScript pros</strong>: This is a blog post for beginners. So don&#8217;t shoot me for simplifying things.</p>

<p>Many developers (especially from the Flash / ActionScript community, but not only them) are currently wanting to &#8220;learn&#8221; HTML5. Some want to focus on websites, others want to build (single-page-) apps. Some want to do shiny advertising&#8217;ish projects, others want to focus more on &#8220;products&#8221;. And many say, that therefore they need to learn jQuery now.</p>

<h3>Do not learn jQuery, just use it!</h3>

<p>When someone asks you if you&#8217;re into jQuery, just confirm. Chances are, that clients/employers won’t mind to ask, since they assume that you do.</p>

<p>My prediction is, that you&#8217;ll use 10 jQuery methods max in the beginning. When starting with jQuery, using jQuery is mostly understanding HTML and CSS (you understand HTML and CSS, right?). Anyway, <a href="http://api.jquery.com/" target="_blank">api.jquery.com</a> is your friend. In a year from now, you&#8217;ll probably laugh about your jQuery code, but that&#8217;s ok for now, you have bigger problems.</p>

<h3>How to produce clean code in JavaScript</h3>

<p>The more important question is, how to build clean, manageable, understandable and maintainable JavaScript code. And unlike two years ago there are two industry standards now: <a href="http://documentcloud.github.com/backbone/" target="_blank">backbone.js</a> and <a href="http://requirejs.org/" target="_blank">require.js</a>.</p>

<p>Backbone enables you to write a class-like code structure, that fulfills all the aforementioned criteria. And &#8211; most importantly &#8211; since it so widespread many people already know it. For me, working with backbone made working in big teams possible (you want to work in big teams, right?). There are many other frameworks out there, lots of them with a strong feature set. However, what I like about Backbone that it <em>does not</em> have a big feature set.</p>

<p>Require on the other side, not only encourages you to write loosely coupled and granular parts of code but also helps you to structure <em>the JS files</em> that hold your code. It makes you create JavaScript modules in a standardized way, so that you can easily plug them together. I know first hand, how hard it is in the beginning to decide how to organize the JavaScript files/folders and how to get them into the application. Require.js is there for exactly that.</p>

<h3>So how to learn them then?</h3>

<p>While understanding require is quite straight forward (the documentation will do), the learning curve of backbone is harder. Try to avoid the famous <a href="http://backbonejs.org/examples/todos/index.html" target="_blank">ToDo list example</a>. It gives the impression that backbone applications are always big and/or complicated. The truth is backbone can be extremely simple. Start with something <a href="http://thomasdavis.github.com/2011/02/01/backbone-introduction.html" target="_blank">waaaaay simpler</a>.</p>

<p>Unlike other coding communities, the JavaScript community is almost entirely based on open source. It basically lives on <a href="https://github.com/" target="_blank">github</a>. This includes <a href="https://github.com/documentcloud/backbone" target="_blank">backbone</a> and <a href="https://github.com/jrburke/requirejs" target="_blank">require</a>. So if you don&#8217;t already have a github account &#8211; go and get one. And hang out there for a while.</p>

<p>Other things you might want to check on github:<br/>
* <a href="https://github.com/h5bp/html5-boilerplate" target="_blank">HTML5 Boilerplate</a> (especially the <a href="https://github.com/h5bp/html5-boilerplate/blob/master/.htaccess" target="_blank">.htaccess file</a> is very interesting)<br/>
* <a href="https://github.com/twitter/bootstrap/issues" target="_blank">Twitter Bootstrap issues</a><br/>
* <a href="https://github.com/Modernizr/Modernizr" target="_blank">Modernizr</a><br/>
* <a href="https://github.com/darcyclarke/Front-end-Developer-Interview-Questions" target="_blank">Good list of things to look into</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Optimizing &#8220;overflow:scroll&#8221; on iOS5 [UPDATE]]]></title>
    <link href="http://apdevblog.com/optimizing-webkit-overflow-scrolling/"/>
    <updated>2012-06-07T01:00:00+02:00</updated>
    <id>http://apdevblog.com/optimizing-webkit-overflow-scrolling</id>
    <content type="html"><![CDATA[<p><img src="http://apdevblog.com/images/2012/06/native-ios-scroll.png" title="&#34;native-ios-scroll&#34;" alt="&#34;native-ios-scroll&#34;"></p>

<p>With the never webkit builds on iOS5 and Android ICS finally overflow:scroll works as expected. On iOS you also have the &#8220;native&#8221; scroll bounce, which is huge IMHO (with bounce I mean the ease when the scrolled viewport reaches the top/end). The app finally feels native, a big step for web apps.</p>

<!--more-->


<p>It wasn&#8217;t the sluggishness and weak performance of <a href="http://cubiq.org/iscroll" target="_blank">iScroll</a> and <a href="http://joehewitt.github.com/scrollability/" target="_blank">scrollability</a> I was mad about, it was the wrong implementation of the bounce physics. I was always able the tell the difference of a native and a phonegap app by checking scroll views.</p>

<h3>Get it working</h3>

<p><a href="http://aronwoost.github.com/optimize-webkit-overflow-scrolling/1.html" target="_blank">Check example 1</a> (with iOS5+ device or simulator)</p>

<p>The magic happens with <a href="https://github.com/aronwoost/optimize-webkit-overflow-scrolling/blob/gh-pages/1.html#L23" target="_blank">this line</a>.</p>

<div><a href='https://gist.github.com/2887542' target='_blank' data-json-url='https://gist.github.com/2887542.json' data-file=''>View code on gist.github.com</a></code>
<noscript><pre><code>-webkit-overflow-scrolling: touch;</code></pre></noscript></div>


<p>Without you would still have a touchmove scroll with one finger, however the &#8220;native&#8221; bounce comes with -webkit-overflow-scrolling:touch. I notices however one thing that differs from the native scroll. When the content to scroll is on top (e.g. you can&#8217;t scroll any further up) and try it anyways, the touchmove bubbles up. <a href="http://aronwoost.github.com/optimize-webkit-overflow-scrolling/1.html" target="_blank">Try example 1 again</a> (with iOS5+ device or simulator) In this case it bubble to the top window object which then will scroll the browser chrome. While this might be an expected behavior, it still feels not very native. Let&#8217;s fix this.</p>

<h3>Make it even more native</h3>

<p><a href="http://aronwoost.github.com/optimize-webkit-overflow-scrolling/2.html" target="_blank">Check example 2</a> (with iOS5+ device or simulator)</p>

<p>When you try the same here, you&#8217;ll notice that you can drag the scrolled content with the expected physics. This feels, well, simply amazing. I have been working with all the JavaScript scroll implementations for such a long time now and now it&#8217;s finally native. Yeah!</p>

<p>So how is it done?</p>

<div><a href='https://gist.github.com/2887552' target='_blank' data-json-url='https://gist.github.com/2887552.json' data-file=''>View code on gist.github.com</a></code>
<noscript><pre><code>&lt;html&gt;
    &lt;head&gt;
        &lt;style&gt;
            ...
            #subcontainer {
                height:100%;
                overflow-y: auto;
            }
            ...
        &lt;/style&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id=&quot;container&quot;&gt;
            &lt;div id=&quot;subcontainer&quot;&gt;
                &lt;ul&gt;&lt;/ul&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre></noscript></div>


<p>Well, the magic is to include the scroll content <a href="https://github.com/aronwoost/optimize-webkit-overflow-scrolling/blob/gh-pages/2.html#L57" target="_blank">into another container div</a>, that has to have the <a href="https://github.com/aronwoost/optimize-webkit-overflow-scrolling/blob/gh-pages/2.html#L26" target="_blank">same height as the parent scroll container and also the correct overflow settings set</a> (-webkit-overflow-scrolling doesn&#8217;t seem to be needed). Note that the subcontainer has to have the exact same height, so no margin or padding or stuff allowed. Also note, that no JavaScript is required. This is pure css.</p>

<h4>Here a video to illustrate the difference between example 1 and 2:</h4>

<iframe src="http://www.youtube.com/embed/Yjch40sp4Po" frameborder="0" width="420" height="315"></iframe>


<h3>One last thing</h3>

<p>Notice that in the previous example you can still scroll the browser chrome? Try it by dragging (touch moving) the grey background. If you have a web app (instead of a website) you might want to disable the scrolling of the window viewport entirely.</p>

<p><a href="http://aronwoost.github.com/optimize-webkit-overflow-scrolling/3.html" target="_blank">Check example 3</a> (with iOS5+ device or simulator)</p>

<p>Easy.</p>

<div><a href='https://gist.github.com/2887578' target='_blank' data-json-url='https://gist.github.com/2887578.json' data-file=''>View code on gist.github.com</a></code>
<noscript><pre><code>&lt;script type=&quot;text/javascript&quot;&gt;
    document.addEventListener(&quot;touchmove&quot;, function(evt){
        evt.preventDefault();
    }, false);
            
                
    var container = document.getElementById(&quot;container&quot;);
    container.addEventListener(&quot;touchmove&quot;, function(evt){
        evt.stopPropagation();
    }, false);
&lt;/script&gt;
</code></pre></noscript></div>


<p><a href="https://github.com/aronwoost/optimize-webkit-overflow-scrolling/blob/gh-pages/3.html#L40" target="_blank">Listen to the touchmove event on document level and prevent the default behavior</a>. With this setting alone scrolling would be disabled globally, also in the overflow div. That why you need to set <a href="https://github.com/aronwoost/optimize-webkit-overflow-scrolling/blob/gh-pages/3.html#L57" target="_blank">another touchmove listener to the scroll div</a> and stop the bubbling. This event then will never reach the document object.</p>

<h3>UPDATE &#8211; Arg, found an edge case</h3>

<p>Or, actually not so edge. <a href="http://aronwoost.github.com/optimize-webkit-overflow-scrolling/3a.html" target="_blank">Check a variation of the previous example</a> (with iOS5+ device or simulator) where there are not enough item for the div to be scrollable. When you try to scroll you&#8217;ll notice, that the browser chrome scrolls. Not good.</p>

<p><a href="http://aronwoost.github.com/optimize-webkit-overflow-scrolling/4.html" target="_blank">Here corrected example 4</a> (with iOS5+ device or simulator)</p>

<p>However, it adds more markup and more JavaScript. I would appreciate a smarter solution.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Don&#8217;t comment your code!]]></title>
    <link href="http://apdevblog.com/comments-in-code/"/>
    <updated>2011-05-02T01:00:00+02:00</updated>
    <id>http://apdevblog.com/comments-in-code</id>
    <content type="html"><![CDATA[<p><img src="http://apdevblog.com/images/img/worst-comment.gif" alt="" /><br/>
<a style="font-size: 10px;" href="http://stackoverflow.com/questions/184618/what-is-the-best-comment-in-source-code-you-have-ever-encountered/389723#389723" target="_blank">stackoverflow.com</a></p>

<p>&#8230; unless you know what you do.</p>

<!--more-->


<h3>The problem</h3>

<p>Since I started programming I have always been fighting with &#8220;strange&#8221; code comments. Everyone told me I had to document my code. Also I wanted to be a good citizen and make it as easy as possible for my co-workers to understand my code. On the other hand there was always so little time. And writing good comments takes time. Also I understood that when the codes changes I also have to update the comments (even more time consuming). Plus I was not sure what to comment. Every line? Every method? Or a summary of the class in the header?</p>

<p>When I got better at coding I noticed that no one is writing comments. At least not many. However I found out that comments can be very useful when there is a codeline that needs clarification. So I still felt guilty for not commenting my code.</p>

<p><a href="http://www.amazon.de/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/" target="_blank"><img style="float: left; margin-right: 8px; margin-bottom: 10px;" src="http://apdevblog.com/images/img/clean-code.jpg" alt="" width="140" height="186" /></a> Last year I got my hands on <a href="http://www.amazon.de/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/" target="_blank">Clean Code</a> by <a href="http://twitter.com/unclebobmartin" target="_blank">Robert C. Martin</a>. <strong>Dear programmers out there, you need to read this book</strong>.</p>

<p>The chapter on Code Comments made me totally change my mind about writing comments and feeling guilty for not writing them. Bottom line: The code itself is the best comment, only write comments when it&#8217;s really needed, don&#8217;t leave commented code lines.</p>

<h3 style="clear: both;">
  Example 1 &#8211; Annoying
</h3>


<p>Recently I came into a project with some other programmers. I found the following comment styles (different coding styles aside, that is another story). Which do you find best?</p>

<pre class="brush: as3; title: ; wrap-lines: false; notranslate" title="">public class AbstractModuleMediator extends Mediator implements IMediator {
    /* public variables and consts */

    /* protected and private variables and consts */

    private static const log : Logger = LogContext.getLogger(AbstractModuleMediator);

    /* public functions */

    /**
     * Constructor for the AbstractModuleMediator class
     */
    public function AbstractModuleMediator(name : String, viewComponent : IModule) {
        super(name, viewComponent);
    }
}
</pre>




<pre class="brush: as3; title: ; wrap-lines: false; notranslate" title="">public class SomeModule extends AbstractInteractionModule {
    private static const log : Logger = LogContext.getLogger(SomeModule);

    // ✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖
    // VARIABLES
    // ✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖
    private var _vo : SomeVo;

    // ✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖
    // CONSTRUCTOR
    // ✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖
    public function SomeModule() : void {

    };

    // ✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖
    // PUBLIC FUNCTIONS
    // ✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖✖
    override public function create() : void {
    }
}
</pre>




<pre class="brush: as3; title: ; wrap-lines: false; notranslate" title="">public class SomeMediator extends AbstractModuleMediator implements IMediator {

    // _____________________________________________________________________________________________________________
    // C O N S T A N T
    // =============================================================================================================

    public static const NAME : String = "SomeMediator/";

    private static const log : Logger = LogContext.getLogger(SomeMediator);

    // _____________________________________________________________________________________________________________
    // V A R I A B L E S
    // =============================================================================================================

    // _____________________________________________________________________________________________________________
    // I N I T I A L I Z E
    // =============================================================================================================

    public function SomeMediator(name : String, module : IModule) {
        super(name, module);
        log.debug("VideoPlayerMediator()");
    }

    // _____________________________________________________________________________________________________________
    // P R I V A T E - F U N C T I O N S
    // =============================================================================================================
}
</pre>




<pre class="brush: as3; title: ; wrap-lines: false; notranslate" title="">public class SomeMediator extends AbstractModuleMediator
{
    public static const NAME:String = "SomeMediator/";

    private static const log:Logger = LogContext.getLogger(SomeMediator);

    public function SomeMediator(viewComponent : IModule)
    {
        super(NAME, viewComponent);
    }

    override public function onRegister() : void
    {
    }
}
</pre>


<p>So what&#8217;s the best commented code? It&#8217;s #4. Why? Think about when you first enter a class to make some adjustments. You need to understand the code. You need to <em>read</em> the code. Everything thats distracts you from <em>reading code</em> is useless and should be avoided.</p>

<p>We all know (at least we should) the order of static field, public field, private field, constructor, public methods, private methods. No need to repeat that.</p>

<h3>Example 2 &#8211; Bad</h3>

<p>The other thing Martin points out is the danger of uncommented code lines. This one is harder to achieve since it needs you to make a quick code review on all edited classes before you commit something. Take this example:</p>

<pre class="brush: as3; title: ; wrap-lines: false; notranslate" title="">facade.registerProxy(new ChannelProxy());
facade.registerProxy(new SoundProxy());
//facade.registerProxy(new SoundAssetProxy());
facade.registerProxy(new SubtitleStateProxy());

facade.registerProxy(new SocialShareProxy());

facade.registerProxy(new TrackProxy());
</pre>


<p>I had a ticket where I needed to fix a sound issue. I came across this command and the uncommented line left nothing but questions for me. Is this work in progress? Or a permanent removal? Does this Proxy even exist any longer? And most importantly: Does this has something to do with my issue?</p>

<p>Of course someone just forgot to remove this line/comment. However, it cost me some minutes to figure that out.</p>

<h3>Example 3 &#8211; EVIL</h3>

<pre class="brush: as3; title: ; wrap-lines: false; notranslate" title="">/**
 * Always returns true.
 */
public boolean isAvailable() {
    return false;
}
</pre>


<p>Do I need to say more? This &#8211; of course &#8211; is the worst thing that can happen with code comments. The code changes but the comment stays the same. When you change a commented line/function/method and you can &#8211; for whatever reason &#8211; not change the comment, <em>delete the comment</em>.</p>

<h3>Conclusion</h3>

<p>As you see from the examples it&#8217;s way harder to write good comments then no comments at all. So instead of making it all worse just don&#8217;t do it. If you consider yourself as someone who writes exceptable code, that should explain enough for you and your co-workers.</p>

<h3>Thats all folks. Oh wait&#8230;</h3>

<pre class="brush: as3; title: ; wrap-lines: false; notranslate" title="">//
// Dear maintainer:
//
// Once you are done trying to 'optimize' this routine,
// and have realized what a terrible mistake that was,
// please increment the following counter as a warning
// to the next guy:
//
// total_hours_wasted_here = 39
//
</pre>


<p><a href="http://stackoverflow.com/questions/184618/what-is-the-best-comment-in-source-code-you-have-ever-encountered/482129#482129">source</a></p>

<pre class="brush: as3; title: ; wrap-lines: false; notranslate" title="">stop(); // Hammertime!
</pre>


<p><a href="http://stackoverflow.com/questions/184618/what-is-the-best-comment-in-source-code-you-have-ever-encountered/186309#186309">source</a></p>

<pre class="brush: as3; title: ; wrap-lines: false; notranslate" title="">// I dedicate all this code, all my work, to my wife, Darlene, who will
// have to support me and our three children and the dog once it gets
// released into the public.
</pre>


<p><a href="http://stackoverflow.com/questions/184618/what-is-the-best-comment-in-source-code-you-have-ever-encountered/186967#186967">source</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[7 things to do first when starting with FDT4]]></title>
    <link href="http://apdevblog.com/top-7-things-fdt4-beginner/"/>
    <updated>2010-09-23T01:00:00+02:00</updated>
    <id>http://apdevblog.com/top-7-things-fdt4-beginner</id>
    <content type="html"><![CDATA[<p>Recently I upgraded to FDT4. Well, not exactly upgraded since I downloaded a fresh Eclipse version (for using FDT as plug-in) and created a new workspace. Here 7 things essential for me to start working with FDT4.</p>

<!--more-->


<p><strong>Note:</strong> Some of the points will maybe auto set, when using the final FDT4 Standalone version.</p>

<h3>1. Always launch previous application</h3>

<p>When hitting F11 by default Eclipse is launching the currently active class (the one you&#8217;re currently editing). Of course you want to launch the previous build. Got to Preferences &ndash;> Run/Debug &ndash;> Launching and set the &#8220;Launch Operation&#8221; to &#8220;Always launch the previous launched application&#8221;.</p>

<p><img src="http://apdevblog.com/images/img/top-7-fdt/top-01.gif" alt="" /></p>

<h3>2. Disable Autobuild</h3>

<p>When working with big project FDT4 has a hell of work to do when (re-)building the project. This can be seriously annoying. So instead of letting FDT autobuild the project when changes happen, you want to do this manually (ctrl/cmd + B). Goto Project and disable &#8220;Build automatically&#8221;.</p>

<p><img src="http://apdevblog.com/images/img/top-7-fdt/top-02.gif" alt="" /></p>

<h3>3. Set class comment header</h3>

<p>With that you not only have your copyright in the class (not so useful) but also will the svn tag be updated. Go to Preferences &ndash;> FDT &ndash;> Code Style &ndash;> Code Templates &ndash;> &#8220;typecomment&#8221; &ndash;> &#8220;Edit&#8230;&#8221; and insert your custom class comment.</p>

<p><img src="http://apdevblog.com/images/img/top-7-fdt/top-03.gif" alt="" /></p>

<h3>4. Set the braces correctly</h3>

<p>It drives me nuts, when the braces formating style is not correct ;). Go to Preferences &ndash;> FDT &ndash;> Code Style &ndash;> ActionScript Formatter &ndash;> Braces Tab &ndash;> &#8220;Wrap All&#8221;. Clean that mess with ctrl/cmd + shift + F.</p>

<p><img src="http://apdevblog.com/images/img/top-7-fdt/top-04.gif" alt="" /></p>

<h3>5. Make the code assist work more responsive</h3>

<p>You want to code assistant to appear when typing and not only when typing . or hitting ctrl/cmd + SPACE. Go to Preferences &ndash;> FDT &ndash;> Editor &ndash;> Code Assist. Set the delay in the &#8220;Auto activation Delay:&#8221; to 50. In the field &#8220;Auto activation triggers for AS:&#8221; you insert &#8220;abcdefghijklmnopqrstuvwxyz_.:&#8221;.</p>

<p><img src="http://apdevblog.com/images/img/top-7-fdt/top-05.gif" alt="" /></p>

<h3>6. Show line numbers</h3>

<p>You need then, don&#8217;t you? Right click on the grey vertical bar, left to the code editing window and select &#8220;Show line numbers&#8221;.</p>

<p><img src="http://apdevblog.com/images/img/top-7-fdt/top-06.gif" alt="" /></p>

<h3>7. View classes hierachical in the Flash Explorer</h3>

<p>No need to share the space with all classes.</p>

<p><img src="http://apdevblog.com/images/img/top-7-fdt/top-07.gif" alt="" /></p>

<h3>Wait! Are there more?</h3>

<p>So these are my 7 steps to get FDT4 up and running. Take it or leave it&#8230; :)</p>

<p>Do you have some FDT essentials you can&#8217;t live without? <strong>Please write a comment!</strong></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Dear iOS developer, please collect the garbage (the other one)]]></title>
    <link href="http://apdevblog.com/ios-the-other-garbage-collection/"/>
    <updated>2010-08-20T01:00:00+02:00</updated>
    <id>http://apdevblog.com/ios-the-other-garbage-collection</id>
    <content type="html"><![CDATA[<p><img src="http://farm1.static.flickr.com/36/84970969_68529bd4f1.jpg" alt="" /><br/>
<a style="font-size: 10px;" href="http://www.flickr.com/photos/oskaline/" target="_blank">Paper basket image by oskaline via Flickr</a></p>

<p>Some time ago I wrote a blog <a href="http://apdevblog.com/slow-itunes-sync-backup/">post about fixing slow iTunes backup</a>. The article became the by far most visited one on this blog and we are still getting tons of visitors from google every day. When reading the comments you can see that lots of users seem to have the same/similar problem.</p>

<!--more-->


<p>The issue results from the fact, that some apps <strong>save lots of files to the file system</strong> of the iPod Touch, iPhone or iPad. While &#8211; of course &#8211; nothing speaks against using the file system to save data, the <strong>problem starts when files getting to many</strong>. iTunes then needs ages to copy them to the computer when doing a sync (the backup before the sync in particular).</p>

<p>If using a reasonable number of files you will never have a problem. However, if your app syncs data from the web (for instance a news or rss reader) make sure that you delete the read / no longer needed files. Please :)</p>

<p>The usual customer otherwise might have the feeling the iPhone &#8220;doesn&#8217;t work&#8221; anymore. Or even worse, the customer reads <a href="http://apdevblog.com/slow-itunes-sync-backup/">our article</a> and blames your app in <a href="http://apdevblog.com/slow-itunes-sync-backup/#comments">the comments</a>&#8230; :)</p>

<p>PS: Personally, I haven&#8217;t worked on apps that make extensive use of the file system. If you have, feel free to share your experiences.</p>
]]></content>
  </entry>
  
</feed>
