<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Soliloquy]]></title><description><![CDATA[Father, husband, cosmopolitan geek and music lover. Learning how to build beautiful things.]]></description><link>https://iampeterbanjo.com</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 09:21:06 GMT</lastBuildDate><atom:link href="https://iampeterbanjo.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Re-re-re-re-factor]]></title><description><![CDATA[Being able to safely refactor code is one of the greatest benefits of writing tests. The goal of refactoring is to improve code design e.g. style, design patterns, performance.
Let's take an example from the previous route handler which has already b...]]></description><link>https://iampeterbanjo.com/re-re-re-re-factor</link><guid isPermaLink="true">https://iampeterbanjo.com/re-re-re-re-factor</guid><category><![CDATA[THW Web Apps]]></category><category><![CDATA[Testing]]></category><category><![CDATA[TDD (Test-driven development)]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Wed, 27 Apr 2022 20:39:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/Klby0nxseY8/upload/v1651091879761/KeRA6UKV2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Being able to safely refactor code is one of the greatest benefits of writing tests. The goal of refactoring is to improve code design e.g. style, design patterns, performance.</p>
<p>Let's take an example from the previous route handler which has already been refactored.</p>
<pre><code class="lang-Typescript"><span class="hljs-keyword">import</span> fastify <span class="hljs-keyword">from</span> <span class="hljs-string">'fastify'</span>;

<span class="hljs-comment">// for testing</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">start</span>(<span class="hljs-params">options: { port: <span class="hljs-built_in">number</span> }</span>) </span>{
  <span class="hljs-keyword">const</span> server = fastify({ logger: <span class="hljs-literal">true</span> });

  <span class="hljs-comment">// basic health response can be anything</span>
  server.get(<span class="hljs-string">'/health'</span>, <span class="hljs-keyword">async</span> () =&gt; <span class="hljs-string">'OK'</span>);
  <span class="hljs-keyword">const</span> { port } = options;

  <span class="hljs-keyword">await</span> server.listen(port);

  <span class="hljs-keyword">return</span> server;
}

<span class="hljs-comment">// so we can node ./build/index.js to start the server</span>
(<span class="hljs-keyword">async</span> () =&gt; start({ port: <span class="hljs-built_in">Number</span>(process.env.PORT) }))();
</code></pre>
<p>The minimum required to pass the test is</p>
<pre><code class="lang-Typescript">(<span class="hljs-keyword">async</span> () =&gt; { 
  <span class="hljs-keyword">const</span> server = fastify();

  <span class="hljs-keyword">await</span> server.get(<span class="hljs-string">'/health'</span>, <span class="hljs-keyword">async</span> () =&gt; <span class="hljs-string">'OK'</span>)
  <span class="hljs-keyword">await</span> server.listen(<span class="hljs-number">4000</span>);

  <span class="hljs-keyword">return</span> server;
 })();
</code></pre>
<p>But the issue with this version of the code is this error:</p>
<pre><code class="lang-Typescript"> FAIL  src/index.test.ts (<span class="hljs-number">7.241</span> s)
  Given start
    ✕ Server is listening (<span class="hljs-number">5002</span> ms)

  ● Given start › Server is listening

    connect ECONNREFUSED <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">4000</span>



  ● Given start › Server is listening

    thrown: <span class="hljs-string">"Exceeded timeout of 5000 ms for a test.
    Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."</span>
</code></pre>
<p>By using a fixed port number the server under test has trouble being started and stopped. </p>
<p>The solution to this would be to randomise the port number. The issue is the test runner would need to know where the server is listening. We solve this by using <a target="_blank" href="https://en.wikipedia.org/wiki/Inversion_of_control">Inversion of Control</a> and giving the server under test the port to listen on. 👂🏽</p>
<pre><code class="lang-Typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">start</span>(<span class="hljs-params">options: { port: <span class="hljs-built_in">number</span> }</span>) </span>{
  <span class="hljs-keyword">const</span> server = fastify();
  <span class="hljs-keyword">const</span> { port } = options;

  <span class="hljs-keyword">await</span> server.get(<span class="hljs-string">'/health'</span>, <span class="hljs-keyword">async</span> () =&gt; <span class="hljs-string">'OK'</span>)
  <span class="hljs-keyword">await</span> server.listen(port);


  <span class="hljs-keyword">return</span> server;
}
</code></pre>
<p>It's a good habit to refactor tests and the code under tests separately. By writing our tests first, we can be more confident when we make changes.</p>
<p>Martin Fowler goes into a lot more detail in his book on <a target="_blank" href="https://martinfowler.com/books/refactoring.html">Refactoring</a>. 🔧</p>
]]></content:encoded></item><item><title><![CDATA[Applying marginal gains theory to tech debt]]></title><description><![CDATA[Marginal gains ⚙️
The 2020 Summer Olympic Games were held in Tokyo between 23 July and 8 August. The enduring motto of the Olympics is “Faster, Higher, Stronger”.
From 1908 to 2003 the British cycling team won only 1 gold medal at the Olympics. This ...]]></description><link>https://iampeterbanjo.com/applying-marginal-gains-theory-to-tech-debt</link><guid isPermaLink="true">https://iampeterbanjo.com/applying-marginal-gains-theory-to-tech-debt</guid><category><![CDATA[2Articles1Week]]></category><category><![CDATA[performance]]></category><category><![CDATA[personal]]></category><category><![CDATA[technology]]></category><category><![CDATA[habits]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Mon, 25 Apr 2022 22:42:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/WUehAgqO5hE/upload/v1650925955746/szPN04Ew1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-marginal-gains">Marginal gains ⚙️</h2>
<p>The 2020 Summer Olympic Games were held in Tokyo between 23 July and 8 August. The enduring motto of the Olympics is “Faster, Higher, Stronger”.</p>
<p>From 1908 to 2003 the British cycling team won only 1 gold medal at the Olympics. This changed when Sir Dave Brailsford became the head of British Cycling in 2002. During the ten-year span from 2007 to 2017, British cyclists won 178 world championships and 66 Olympic or Paralympic gold medals and captured 5 Tour de France victories in what is widely regarded as <a target="_blank" href="https://jamesclear.com/marginal-gains">the most successful run in cycling history</a>.</p>
<p>Sir Dave Brailsford credits this remarkable achievement to 3 pillars: 🏛</p>
<ol>
<li>Strategy - what it takes to win a specific event by analysing the best performers.</li>
<li>Human performance - how to get the best out of each athlete.</li>
<li>Continuous Improvement in all areas where cumulative improvements are as small as 1% aka. "marginal gains" can make a big difference in total.</li>
</ol>
<h2 id="heading-tech-debt">Tech debt 💸</h2>
<blockquote>
<p>One organization defined it as the negative impact of technology on the business, particularly as manifested in rising operational and technology costs, slower time to market, and reduced flexibility</p>
<p>-- Mckinsey - <a target="_blank" href="https://www.mckinsey.com/business-functions/mckinsey-digital/our-insights/tech-debt-reclaiming-tech-equity">Tech debt: Reclaiming tech equity</a> (October 2020. Accessed June, 2021)</p>
</blockquote>
<p>I find the idea of marginal gains a compelling way to understand tech debt. Marginal gains describe a solution for how we can make future successes more achievable by taking small steps today. And addressing tech debt is the small steps we can take to improve our software.</p>
<p>We end up with tech with good intentions because we prioritise tasks <strong>directly</strong> related to customer needs. Those tasks are easy to prioritise because we don’t want to disappoint our customers or colleagues. By only prioritising those tasks, we neglect to make improvements that make the next task easier. The end result is that doing work in the future takes more time and effort. We break our budgets eating out 🍕 because we don't have time to cook at home. 🍲</p>
<p>Mckinsey in Tech debt: Reclaiming tech equity outline 7 steps to address tech debt:</p>
<ol>
<li>Start with a shared definition of tech debt.</li>
<li>Treat tech debt as a business issue, not a technology problem.</li>
<li>Create transparency to value the debt position.</li>
<li>Formalize the decision-making process.</li>
<li>Dedicate resources to tackling tech debt.</li>
<li>Avoid a big-bang approach to writing down all debt.</li>
<li>Determine which areas are “bankrupt” and explore shifting them to a greenfield stack.</li>
</ol>
<p>This will sound familiar if you already know about Continuous Improvement a.k.a. Kaizen. And it's interesting to see how a small idea made such a huge impact in competitive sports. It shows how small gains like improved build times, code refactoring, updated documentation and other small improvements add up. </p>
]]></content:encoded></item><item><title><![CDATA[Troubleshooting CORS errors]]></title><description><![CDATA[This is a scenario that took some time to understand. I could see errors in the browser but everything on the server seemed to be configured correctly.
The problem was, that when a request was made by the browser, the server handling the request made...]]></description><link>https://iampeterbanjo.com/troubleshooting-cors-errors</link><guid isPermaLink="true">https://iampeterbanjo.com/troubleshooting-cors-errors</guid><category><![CDATA[Web Development]]></category><category><![CDATA[THW Web Apps]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Bugs and Errors]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Sun, 24 Apr 2022 09:24:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/6ZO3rE6OLew/upload/v1650792166005/0JyFutpW0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a scenario that took some time to understand. I could see errors in the browser but everything on the server seemed to be configured correctly.</p>
<p>The problem was, that when a request was made by the browser, the server handling the request made a request to another API and that request returned an error. When the browser received the error message it reported the issue as a CORS issue.</p>
<p>CORS only applies to requests made by the browser, <strong>if the request is made by a server then it cannot be CORS</strong>. If the requests made by cURL, Postman or non-browser client are working then it’s not CORS.</p>
<h2 id="heading-troubleshooting">Troubleshooting</h2>
<ul>
<li>Is the domain the browser is communicating from allowed by the server’s CORS settings?</li>
<li>Are the API servers able to communicate with each other?</li>
<li>Are there any API limits that could be causing the error?</li>
<li>Are there network configurations that should be checked? e.g. Cloudflare, proxies, DNS, etc.</li>
</ul>
<h2 id="heading-references">References</h2>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">Cross-Origin Resource Sharing - MDN</a></li>
<li><a target="_blank" href="https://httptoolkit.tech/blog/how-to-debug-cors-errors/">How to debug CORS errors</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[7 Lean principles to supercharge your productivity]]></title><description><![CDATA[Florida and Minnesota each developed their Statewide Automated Child Welfare Information System (SACWIS). In Florida, system development started in 1990 and was estimated to take 8 years and cost $32 million. As Johnson spoke in 2002, Florida had spe...]]></description><link>https://iampeterbanjo.com/7-lean-principles-to-supercharge-your-productivity</link><guid isPermaLink="true">https://iampeterbanjo.com/7-lean-principles-to-supercharge-your-productivity</guid><category><![CDATA[Productivity]]></category><category><![CDATA[Soft Skills]]></category><category><![CDATA[learning]]></category><category><![CDATA[books]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Sat, 23 Apr 2022 16:09:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/CUJjR4J_BlM/upload/v1650726021329/Us0Ig2JK0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Florida and Minnesota each developed their Statewide Automated Child Welfare Information System (SACWIS). In Florida, system development started in 1990 and was estimated to take 8 years and cost $32 million. As Johnson spoke in 2002, Florida had spent $170 million and the system was estimated to be completed in 2005 at the cost of $230 million. Meanwhile, Minnesota began developing essentially the same system in 1999 and completed it in early 2000 at the cost of $1.1 million. That’s a productivity difference of over 200:1. Johnson credited Minnesota’s success to a standardized infrastructure, minimized requirements, and a team of eight capable people. - (Poppendieck, 2003)</p>
</blockquote>
<p>When I started as a web developer I was obsessed with productivity. When I read the Toyota Way by Jefferey Liker it changed my understanding of productivity. Toyota was founded in 1933 by a family-owned, textile manufacturing company that transformed itself into a world-leading automaker. The collection of principles and practices that led to their success is known as the Toyota Production System or Lean and is so effective that they have been copied by its competitors.</p>
<p>Software developers and automakers share a similar problem - when we develop products for our customers we make complex design decisions that balance variation, costs and time.</p>
<p>Mary and Tom Poppendieck translate Lean principles to agile practices in their excellent book - <a target="_blank" href="https://play.google.com/store/books/details/Mary_Poppendieck_Lean_Software_Development">Lean Software Development</a>. </p>
<p>This is a summary of their ideas.</p>
<ol>
<li>Stop waste</li>
</ol>
<p>Waste is anything that does not add value to a product, where value is perceived by the customer. There are two questions here - who is the customer and what is valuable? This is the most important and the hardest part of lean because an organization or team has to agree on the answer.</p>
<p>For example, the customer of the Infrastructure team is the Development team, the customer for marketing could be the sales team etc.</p>
<p>Hot tip: A frequent, large waste in software development is waiting for things to happen.</p>
<ol>
<li>Amplifying learning</li>
</ol>
<p>Our customers come to us with unique problems which we often know little about. We have to explore different solutions, measure the impact and learn from them. This way, we iterate to more optimal solutions. But does it scale? Scaling is another set of challenges and the same mindset should apply - plan, do, learn, repeat.</p>
<p>Hot tip: Write code in a way that is easy to change. For example, abstractions, DRY principles, feature flags, configurable behaviour etc.</p>
<ol>
<li>Decide as late as possible</li>
</ol>
<blockquote>
<p>Plans are worthless but planning is everything</p>
<p>-- D. Eisenhower</p>
</blockquote>
<p>This is the hardest lesson for me - I'm constantly reading crystal balls, they're so shiny! 🔮👀</p>
<p>When project managers, sales teams and clients want costs and time estimates to fill up their timesheets, it's hard to say, "It will be OK, I haven't made up my mind yet about that". 😬</p>
<p>The idea here is that change is inevitable so delaying commitments allows us to make decisions when we have the most information. For example, how much RAM will this API need? Any estimate will change based on actual usage metrics.</p>
<p>Hot tip: The best ideas often occur towards the end of a project because that's when we know the most.</p>
<ol>
<li>Deliver as fast as possible</li>
</ol>
<p>As an industry, this is where most of our productivity dollars are spent. Every new framework has to have a benchmark with it at the top to have clout. 📈 But we should consider softer issues like faster decision making, access to information etc. Toyota is famously slow to adopt new technologies because they want to avoid optimising some production steps at the cost of the whole system. Don't take risks to speed ahead, just to get stuck at a traffic light. 🚦</p>
<p>Hot tip: Speed is measured by the time it takes for the customer to receive the value.</p>
<ol>
<li>Empower the team</li>
</ol>
<p>There is an amusing story about a pig and chicken wanting to open a Bed and Breakfast restaurant. They argued over the menu design when the chicken suggested "Bacon and eggs". Who should decide? The pig should decide since the decision would impact them more. </p>
<p>Hot tip: Treat work colleagues like volunteers. Trust and respect are vital.</p>
<ol>
<li>Build integrity in</li>
</ol>
<p>As software developers, we have to make trade-offs between competing goals. Business realities, competing standards, and deadlines leave their mark on the product. These compromises can leak into the customer experience in the form of performance issues (slow network requests, API quotas, large file sizes etc.), uninformed designs (missing localisation, clunky legally required information etc.) and other questionable choices.</p>
<p>It would be much better if we can build products with a single vision. Domain-driven design can help us get on the same page as our customers.</p>
<p>Hot tip: Get close to the customer and focus on what matters to them.</p>
<ol>
<li>See the whole</li>
</ol>
<p>This is why I became a full-stack developer. My personal preference is to be able to understand problems from start to finish and use that information when writing software. Our industry has chosen particular knowledge boundaries like "Front-end", "Backend", "Site Reliability" and others.</p>
<p>Optimisations in one step of a product can make it harder to be productive in others. For example, front-end frameworks can hurt user experience through large file sizes and heavy workloads. Serverless solutions have to contend with cold starts. Database choice can limit the type of application features like region distribution, data governance etc.</p>
<p>Hot tip: Be careful how we measure success and set rewards. We win when we all win.</p>
<p>That's it - only 7 things 😂. Please let me know what you think.</p>
<p>And some parting words that keep me humble -</p>
<blockquote>
<p>If today’s problems come from yesterday’s solutions, then tomorrow’s problems will come from today’s solutions. Avoid creating a pendulum that swings from high ceremony to low ceremony and back; look for the balance point of the lean principles.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Starting with a health route]]></title><description><![CDATA[Why a health route?
I want to begin with the end in mind. When our API is deployed in a cloud environment, the automated, cloud deployment will need a way to know that our API has started correctly. That is the purpose of the GET /health route.
I hav...]]></description><link>https://iampeterbanjo.com/starting-with-health-route</link><guid isPermaLink="true">https://iampeterbanjo.com/starting-with-health-route</guid><category><![CDATA[Testing]]></category><category><![CDATA[TDD (Test-driven development)]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[THW Web Apps]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Thu, 21 Apr 2022 22:04:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1650573300794/SlFlztoaX.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Why a health route?</p>
<p>I want to begin with the end in mind. When our API is deployed in a cloud environment, the automated, cloud deployment will need a way to know that our API has started correctly. That is the purpose of the GET <code>/health</code> route.</p>
<p>I have created a <a target="_blank" href="https://codesandbox.io/s/strange-tamas-s91etj?file=/src/index.test.ts">workspace on Codesandbox.io</a> with the completed code. And I'll walk you through the code.</p>
<p>If you want to skip ahead, you can take a look at the "Getting Started" section in the README.md.</p>
<h2 id="heading-acceptance-criteria">Acceptance criteria ✅</h2>
<p>Well written tests are like documentation that is always in sync with operational code. So I like to use a style called Acceptance criteria from <a target="_blank" href="https://en.wikipedia.org/wiki/Behavior-driven_development">Behaviour-Driven Design</a> (BDD) when describing my tests. This takes the format of -</p>
<ul>
<li><strong>Given</strong>: the initial context at the beginning of the scenario, in one or more clauses;</li>
<li><strong>When</strong>: the event that triggers the scenario;</li>
<li><strong>Then</strong>: the expected outcome, in one or more clauses.</li>
</ul>
<h2 id="heading-red">Red 🔴</h2>
<p>The entry point of our application is <code>index.ts</code>. A typical NodeJs server is started with a command like <code>node ./build/index.js</code>. I follow the pattern of placing test files next to the code they test. This makes them easier to find and shows which files are missing tests.</p>
<p><code>index.test.ts</code> contains -</p>
<pre><code class="lang-Typescript"><span class="hljs-comment">// helper method to make a HTTP request</span>
<span class="hljs-keyword">const</span> asyncGet = (
  options: http.RequestOptions
): <span class="hljs-built_in">Promise</span>&lt;http.IncomingMessage&gt; =&gt;
  <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
    http.get(options, <span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> resolve(res));
  });

describe(<span class="hljs-string">`Given start`</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> server: FastifyInstance;
  <span class="hljs-keyword">let</span> port: <span class="hljs-built_in">number</span>;

  <span class="hljs-comment">// SETUP</span>
  beforeAll(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// generate a random port number for the server</span>
    port = <span class="hljs-keyword">await</span> getPort({ port: getPort.makeRange(<span class="hljs-number">3000</span>, <span class="hljs-number">3100</span>) });
    <span class="hljs-comment">// configure the server from outside using params</span>
    <span class="hljs-comment">// this makes it much easier to test</span>
    server = <span class="hljs-keyword">await</span> start({ port });
  });

  <span class="hljs-comment">// CLEAN-UP</span>
  afterAll(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> server.close();
  });

  <span class="hljs-comment">// ACT</span>
  test(<span class="hljs-string">`Server is listening`</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// make a request to the server port</span>
    <span class="hljs-comment">// does it work?</span>
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> asyncGet({
      hostname: <span class="hljs-string">'localhost'</span>,
      port,
      path: <span class="hljs-string">'/health'</span>,
    });

    <span class="hljs-comment">// check the response code</span>
    expect(res.statusCode).toEqual(<span class="hljs-number">200</span>);
  });
});
</code></pre>
<h2 id="heading-green">Green 🟢</h2>
<p>The minimum viable code to get the tests to pass is a simple route to respond to GET '/health'</p>
<pre><code class="lang-Typescript"><span class="hljs-keyword">import</span> fastify <span class="hljs-keyword">from</span> <span class="hljs-string">'fastify'</span>;

<span class="hljs-comment">// for testing</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">start</span>(<span class="hljs-params">options: { port: <span class="hljs-built_in">number</span> }</span>) </span>{
  <span class="hljs-keyword">const</span> server = fastify({ logger: <span class="hljs-literal">true</span> });

  <span class="hljs-comment">// basic health response can be anything</span>
  server.get(<span class="hljs-string">'/health'</span>, <span class="hljs-keyword">async</span> () =&gt; <span class="hljs-string">'OK'</span>);
  <span class="hljs-keyword">const</span> { port } = options;

  <span class="hljs-keyword">await</span> server.listen(port);

  <span class="hljs-keyword">return</span> server;
}

<span class="hljs-comment">// so we can node ./build/index.js to start the server</span>
(<span class="hljs-keyword">async</span> () =&gt; start({ port: <span class="hljs-built_in">Number</span>(process.env.PORT) }))();
</code></pre>
<h2 id="heading-refactor">Refactor</h2>
<p>There isn't anything to do here since the code is so simple. But in the next post, 📮 we'll add more routes that will require some refactoring.</p>
]]></content:encoded></item><item><title><![CDATA[Setup Prettier, Typescript and Jest]]></title><description><![CDATA[The beginning is a very good place to start and we begin by setting up our project. You might want to skip this if you're familiar with setting up a project with Jest and Typescript.
I have opened a Github pull request that shows all the code changes...]]></description><link>https://iampeterbanjo.com/setup-prettier-typescript-and-jest</link><guid isPermaLink="true">https://iampeterbanjo.com/setup-prettier-typescript-and-jest</guid><category><![CDATA[THW Web Apps]]></category><category><![CDATA[TDD (Test-driven development)]]></category><category><![CDATA[Testing]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Wed, 20 Apr 2022 20:55:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1650484016194/dbtKpo8Uk.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The beginning is a very good place to start and we begin by setting up our project. You might want to skip this if you're familiar with setting up a project with Jest and Typescript.</p>
<p>I have opened a Github <a target="_blank" href="https://github.com/iampeterbanjo/get-tdd/pull/1">pull request</a> that shows all the code changes which I'll explain below.</p>
<h2 id="heading-formatting">Formatting 💅🏾</h2>
<p>I prefer to use <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> as my IDE and I recommend installing the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode">Prettier extension</a> to format code. This keeps our code tidy and easier to read. </p>
<ul>
<li>prettierignore</li>
<li>prettierrc</li>
</ul>
<h2 id="heading-typescript">Typescript 🦾</h2>
<p><a target="_blank" href="https://www.typescriptlang.org/">Typescript</a> supercharges JavaScript with type checking. Type checks limit bugs by looking over our shoulders for mistakes like strings instead of numbers. More advanced uses of types can even help document our code. If you've ever wanted to phone a friend for some help while coding in JavaScript then give Typescript a try.</p>
<ul>
<li>tsconfig.json</li>
</ul>
<h2 id="heading-jest">Jest 🤡</h2>
<p>Jesting is another way of saying joking. And another word for joking is mocking. I guess that's why this testing and mocking library is called <a target="_blank" href="https://jestjs.io/">Jesting</a>.</p>
<blockquote>
<p>No place like 127.0.0.1 🏡</p>
</blockquote>
<p>Jest doesn't yet work with Typescript so we'll need a plugin to help translate our Typescript code for Jest. I've used the <a target="_blank" href="https://jestjs.io/">ts-jest plugin</a> for this.</p>
<ul>
<li>jest.config.js</li>
</ul>
<p>To get started I need to install the packages and run the tests. So in a terminal window at the root of the project, I have run  -</p>
<ul>
<li><code>npm install</code></li>
<li><code>npm test</code></li>
</ul>
<p>Jest reports "No tests found, exiting with code 1". Don't panic. In Test-Driven Development we have to get used to our tests not working at first. When we add the correct code and the tests pass, we prove that the code works. Don't trust tests that can't fail.</p>
<h2 id="heading-next">Next ➡️</h2>
<p>That's all the setup steps. The next step is a code walkthrough.</p>
<p>If I've missed anything that you would like explained, please let me know in the comments</p>
]]></content:encoded></item><item><title><![CDATA[Get TDD]]></title><description><![CDATA[In a world where APIs rule the web and JavaScript is the language that reigns across all the stacks, one framework shall rise to challenge the Kingdom of Express.
Introduction
Fastify, first of its name, is a low overhead (aka. fast) server-side Java...]]></description><link>https://iampeterbanjo.com/get-tdd</link><guid isPermaLink="true">https://iampeterbanjo.com/get-tdd</guid><category><![CDATA[THW Web Apps]]></category><category><![CDATA[Web API]]></category><category><![CDATA[Testing]]></category><category><![CDATA[TDD (Test-driven development)]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Tue, 19 Apr 2022 21:08:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/ZhTYr3YKgqQ/upload/v1650398037943/CK8mNJmpa.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In a world where APIs rule the web and JavaScript is the language that reigns across all the stacks, one framework shall rise to challenge the Kingdom of Express.</p>
<h2 id="heading-introduction">Introduction</h2>
<p><a target="_blank" href="https://www.fastify.io/">Fastify</a>, first of its name, is a low overhead (aka. fast) server-side JavaScript framework. Express is currently the most popular NodeJs framework and Fastify is compatible with Express. What makes Fastify a beauty 🌸 is that it's <a target="_blank" href="https://www.fastify.io/benchmarks/">faster</a>, has more <a target="_blank" href="https://www.fastify.io/docs/latest/Guides/Getting-Started/">features</a> and makes <a target="_blank" href="https://www.fastify.io/docs/latest/Guides/Testing/">testing</a> our APIs so easy.</p>
<p>What we are going to build together is a simple Calculator API. When we POST an array of numbers e.g. <code>[2, 4]</code> our API will add them together and return <code>6</code>. It's simple so we keep our attention on testing.</p>
<h2 id="heading-test-driven">Test-Driven 🚘</h2>
<p>Test-Driven Development is a programming practice where:</p>
<ul>
<li>Tests are written before operational code</li>
<li>Operational Code is updated to make the tests pass</li>
<li>Code is refactored to improve its design</li>
</ul>
<p>This is commonly known as Red, Green and Refactor. It reminds me of traffic lights 🚥. </p>
<h2 id="heading-plan">Plan</h2>
<p>The plan is that I will create a blank repo and open PR's so you can see how we can evolve an API using tests.</p>
<p>If you would like to skip ahead, take a look at this <a target="_blank" href="https://github.com/iampeterbanjo/get-tdd">Github repository</a>.</p>
<p>See you soon. </p>
<p>✌️</p>
]]></content:encoded></item><item><title><![CDATA[Tasty Algorithms in a Data Structure dip]]></title><description><![CDATA[A useful way I like to remember what data structures are is the mnemonic “DORITOS”. Where D is Data, O is Operations, R(i) is Relationship, T(o) is time and S is space.
Let’s consider a recipe to make a tasty salsa dip for our Doritos. We can make a ...]]></description><link>https://iampeterbanjo.com/tasty-algorithms-in-a-data-structure-dip</link><guid isPermaLink="true">https://iampeterbanjo.com/tasty-algorithms-in-a-data-structure-dip</guid><category><![CDATA[algorithms]]></category><category><![CDATA[data structures]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Wed, 19 Jan 2022 23:31:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/0hyliwOpNH0/upload/v1650228796660/wF_bn7-6E.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A useful way I like to remember what data structures are is the mnemonic “DORITOS”. Where D is Data, O is Operations, R(i) is Relationship, T(o) is time and S is space.</p>
<p>Let’s consider a recipe to make a tasty salsa dip for our Doritos. We can make a tasty, tomato salsa sauce with a recipe that includes tomatoes, red onion, garlic, white wine vinegar, lime juice and coriander. We peel and chop the tomatoes, peel and dice the onions, peel and crush the garlic and slice the coriander. We add and stir all of these ingredients in a bowl with vinegar and lime juice. Now we have a salsa dip for our Doritos - yum! Notice how it’s not possible to peel the white wine vinegar? Or chop lime juice? Those operations are not allowed on those ingredients. </p>
<p>Computers process data to create results like in a recipe. We call these recipes algorithms and prefer them to take less time and memory. Like in our recipe, there is some preparation required before an algorithm can work. We prepare the data points by grouping them together in a meaningful way. Data points relate to each other like which one comes first or last. Or in what shapes like a list or a tree. We can also add, remove or move around data points in a group.</p>
<p>One more thing, so that the computer can keep track of all of the data, the data is saved as Binary Objects With a fixed Length (B.O.W.L.). Let’s picture this to let it sink in – Doritos dipped in a bowl of tasty, tomato salsa: algorithms and data structures have never tasted so good.</p>
<p>Putting this together – we create data structures by grouping data points into a structure (data structure) that defines the relationships and operations between each of them. Computers use algorithms to operate on data structures and this takes time and space (memory). We describe the time and space complexity of an algorithm using the Big O notation.</p>
]]></content:encoded></item><item><title><![CDATA[Big O state of mind]]></title><description><![CDATA[I grab the mic and jump up to the right as does my asymptote. I never run more than once, man, 'cause to quote,  “Overruns become the source of overflows”. Within the walls of operations, Big O is defined. 
We conceive of memory and time, in a Big O ...]]></description><link>https://iampeterbanjo.com/big-o-state-of-mind</link><guid isPermaLink="true">https://iampeterbanjo.com/big-o-state-of-mind</guid><category><![CDATA[algorithms]]></category><category><![CDATA[data structures]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Wed, 19 Jan 2022 22:41:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/QsDArmUhlAE/upload/v1650228869774/QdTSInAx4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I grab the mic and jump up to the right as does my <a target="_blank" href="https://en.wikipedia.org/wiki/Asymptote">asymptote</a>. I never run more than once, man, 'cause to quote,  “Overruns become the source of overflows”. Within the walls of operations, Big O is defined. </p>
<p>We conceive of memory and time, in a Big O state of mind.</p>
<p>Does the CPU have the juice? The clue is in the queue if you don’t want to lose out to <a target="_blank" href="https://en.wikipedia.org/wiki/Computational_complexity_theory">complexity</a> in a <a target="_blank" href="https://en.wikipedia.org/wiki/While_loop">while do { }</a>. Be in a Big O state of mind.</p>
<p>What more could you ask for? When I, operate in <a target="_blank" href="https://en.wikipedia.org/wiki/Time_complexity#Constant_time">constant time</a>? If you complain about how big the data is, this algo loves it though. It goes like a pro, "Go go gadget!", to the beat of the algo rhythm.</p>
<p>When I'm <a target="_blank" href="https://en.wikipedia.org/wiki/Recursion_(computer_science">recursing</a> I bless the <a target="_blank" href="https://en.wikipedia.org/wiki/Call_stack">call stacks</a> and <a target="_blank" href="https://en.wikipedia.org/wiki/Tail_call">tail call</a> optimise them. Sprinkle some holy water, cross the compiler, <a target="_blank" href="https://en.wikipedia.org/wiki/Logarithmic_growth">O(log)N</a> is my bridge over disordered waters. Double the input and it only needs one more step over.</p>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Time_complexity#Linear_time">Linear</a> takes one pass, but <a target="_blank" href="https://en.wikipedia.org/wiki/Time_complexity#Sub-quadratic_time">X squared</a> is to be feared. <a target="_blank" href="https://en.wikipedia.org/wiki/Time_complexity#Polynomial_time">Cubic? Exponential?</a> <a target="_blank" href="https://en.wikipedia.org/wiki/Deadlock">Deadlock</a> is what they deserve.</p>
<p>Yeah, yeah I'm checking the space-time.</p>
<p>And I'm gonna move the perf line.</p>
<p>Yea, yeah, I’m in a Big O state of mind.</p>
<p>Inspired by <a target="_blank" href="https://www.song-lyrics-generator.org.uk/">Song lyrics generator</a></p>
]]></content:encoded></item><item><title><![CDATA[Elixir methods]]></title><description><![CDATA[The best programming book I've read was Atomic Scala by Bruce Eckel and Dianne Marsh. It was so good that when I went for a job interview I chose to use Scala for the technical test because I was so in love with the language and its programming conce...]]></description><link>https://iampeterbanjo.com/elixir-methods</link><guid isPermaLink="true">https://iampeterbanjo.com/elixir-methods</guid><category><![CDATA[Elixir]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[THW Web Apps]]></category><category><![CDATA[learning]]></category><category><![CDATA[books]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Tue, 29 Oct 2019 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/WE_Kv_ZB1l0/upload/v1651144380658/eBuRwEkpL.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The best programming book I've read was <a target="_blank" href="https://gumroad.com/discover?query=Atomic%20scala#aoQY">Atomic Scala by Bruce Eckel and Dianne Marsh</a>. It was so good that when I went for a job interview I chose to use Scala for the technical test because I was so in love with the language and its programming concepts. Well, that was premature and I bombed the interview but I'm still fond of the book and Scala. </p>
<p>As I learn <a target="_blank" href="https://elixir-lang.org/">Elixir</a>, what I'm coming across in the literature doesn't fill me with the same excitement. I've read about Elixir structs, maps, lists and pattern matching too many times. I want to get to a working knowledge of the language and at this point, I don't really care if strings are UTF-8, how large a float is or the Erlang representation behind them. </p>
<p>When I'm on holiday in Spain, what I care about is how to say "Good morning, table for two please" not <a target="_blank" href="https://en.wikipedia.org/wiki/Spanish_language">"Spanish is a Romance language that originated in the Iberian Peninsula and today has over 450 million native speakers in Spain and the Americas. It is a global language and the world's second-most spoken native language, after Mandarin Chinese"</a>.</p>
<p>So this is an ode to Atomic scala.</p>
<h2 id="heading-methods-aka-function-aka-do-something">Methods aka. Function aka. Do something</h2>
<p>A method groups programming logic that can be executed by name or anonymously. Simple functions are used to build more complex logic structures. You don't need a <code>return</code> statement.</p>
<h3 id="heading-terms">Terms</h3>
<ul>
<li>Arity - the number of parameters to a method e.g <code>List.first/1</code> means one parameter required for the function named <code>first</code> in the <code>List</code> module.</li>
<li>Typedef - i.e. <code>@spec</code> - explicit type information used to find errors. <a target="_blank" href="https://elixirschool.com/en/lessons/specifics/debugging/">Learn more</a></li>
<li>Pattern matching - the method signature is determined by the type of arguments</li>
<li>Guard - a conditional that is checked before executing a function. <a target="_blank" href="https://elixirschool.com/en/lessons/basics/functions/#guards">Learn more</a></li>
<li>Default value - i.e. <code>argument \\ value</code></li>
</ul>
<h3 id="heading-explanation">Explanation</h3>
<p>Named functions exist in modules and have the structure:</p>
<pre><code class="lang-elixir">defmodule Module_name do
 # note the comma before 'do'
  def function_name (arg1), do
  ...
 end

 @spec function_name(type()) :: return_type()
 def function_name (arg1, arg2) when arg1 &gt; 0 do
  ...
 end
end
</code></pre>
<p>Private functions start with <code>defp</code> instead of <code>def</code>.</p>
<p>Anonymous functions have two structures with <code>fn</code> and <code>&amp;</code>. E.g.</p>
<pre><code class="lang-elixir">function_name = fn (arg1, arg2) -&gt; ... end
capture_function = &amp;(&amp;1 ... &amp;2)
</code></pre>
<p>A header function is a function with the same name as a named function and is used to set the default value for the named function's argument(s). E.g.</p>
<pre><code class="lang-elixir">defmodule Module_name do
  def function_name(arg1 \\ default_value)
 def function_name(arg1), do
    ...
  end
end
</code></pre>
<h3 id="heading-examples">Examples</h3>
<pre><code class="lang-elixir">@spec multiply(number()) :: number()
defmodule Calculator do
  def multiply(arg1, arg2) when arg1 &gt; 0 do
    arg1 * arg2
  end
end

Calculator.multiply(0, 2) # -&gt; (FunctionClauseError) no function clause matching in Calculator.named_multiply/2
Calculator.multiply(2, 3) # -&gt; 6

anon_multiply = fn (arg1, arg2) -&gt; arg1 * arg2 end
capture_multiply = &amp;(&amp;1 * &amp;2)

anon_multiply.(2, 4) # -&gt; 8
capture_multiply.(5, 4) # -&gt; 20

defmodule Greeter do
  def hello(name \\ "world")
  def hello(names) when is_list(names) do
    names
    |&gt; Enum.join(", ")
    |&gt; hello
  end

  def hello(name) when is_binary(name) do
    phrase() &lt;&gt; name
  end

  def phrase, do: "Hello, "
end

Greeter.hello() # -&gt; "Hello, world"
Greeter.hello("Mark") # -&gt; "Hello, Mark"
Greeter.hello(["Mark", "Luke"]) # -&gt; "Hello, Mark, Luke"
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Hapi error logging]]></title><description><![CDATA[It was so annoying - there I was trying to test my hapi server but it was broken and I had no idea why. Because I was using server.inject the errors where not being shown in the console. How do I log the error messages from a stub server?
suite('getB...]]></description><link>https://iampeterbanjo.com/hapi-error-logging</link><guid isPermaLink="true">https://iampeterbanjo.com/hapi-error-logging</guid><category><![CDATA[Web Development]]></category><category><![CDATA[logging]]></category><category><![CDATA[Bugs and Errors]]></category><category><![CDATA[HapiJS]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Tue, 23 Jul 2019 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/NEwe0UGsTfY/upload/v1651144699634/XMv6LZO9A.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It was so annoying - there I was trying to test my hapi server but it was broken and I had no idea why. Because I was using <code>server.inject</code> the errors where not being shown in the console. How do I log the error messages from a stub server?</p>
<pre><code class="lang-JavaScript">suite(<span class="hljs-string">'getBlogFiles'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">let</span> server;

    beforeEach(<span class="hljs-function">() =&gt;</span> {
        server = Hapi.Server();
        <span class="hljs-keyword">const</span> preResponse = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, h</span>) </span>{
            <span class="hljs-keyword">const</span> { response } = request;
            <span class="hljs-keyword">if</span> (!response.isBoom) {
                <span class="hljs-keyword">return</span> h.continue;
            }

            <span class="hljs-keyword">return</span> <span class="hljs-built_in">console</span>.warn(response);
        };

        server.ext(<span class="hljs-string">'onPreResponse'</span>, preResponse);
    });

    test(<span class="hljs-string">'get posts route gets markdown files'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> files = [<span class="hljs-string">'that.md'</span>, <span class="hljs-string">'this.md'</span>];
        <span class="hljs-keyword">const</span> getBlogFiles = sinon.stub().resolves(files);
        <span class="hljs-keyword">const</span> stubMethods = [{ <span class="hljs-attr">name</span>: <span class="hljs-string">'blog.getBlogFiles'</span>, <span class="hljs-attr">method</span>: getBlogFiles }];

        <span class="hljs-keyword">await</span> server.register({
            plugin,
            <span class="hljs-attr">options</span>: { <span class="hljs-attr">methods</span>: stubMethods },
        });

        <span class="hljs-keyword">const</span> { method, url } = routes.v1.get_blog_posts();
        <span class="hljs-comment">// errors disappearing between here...</span>
        <span class="hljs-keyword">const</span> { result } = <span class="hljs-keyword">await</span> server.inject({
            method,
            url,
        });
        <span class="hljs-comment">// ... and here</span>

        expect(result).to.equal(files);
    });
});
</code></pre>
<p>So I got on the <a target="_blank" href="https://hapihour.slack.com">Hapi hour slack channel</a> to ask this question and a in a few minutes <a target="_blank" href="https://medium.com/@eranhammer">Eran</a> told me</p>
<blockquote>
<p>@iampeterbanjo it's coming soon, but for now just throw server.events.on('request', console.log) to see what's up.</p>
</blockquote>
<p>Brilliant! It feels great to be using a framework from such a friendly and helpful community.</p>
]]></content:encoded></item><item><title><![CDATA[The hardest test to write]]></title><description><![CDATA[I like writing tests, because they prove the code will run like I expect. I like it even more when I can write tests before I write code. But often I can’t do that, because I'm still figuring out how the code will work. To put it another way, I'm not...]]></description><link>https://iampeterbanjo.com/the-hardest-test-to-write</link><guid isPermaLink="true">https://iampeterbanjo.com/the-hardest-test-to-write</guid><category><![CDATA[Testing]]></category><category><![CDATA[TDD (Test-driven development)]]></category><category><![CDATA[Jest]]></category><category><![CDATA[mocha]]></category><category><![CDATA[Cypress]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Sat, 13 Apr 2019 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/UNbiqyCAFrg/upload/v1651143837401/BpUn-rrGI.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I like writing tests, because they prove the code will run like I expect. I like it even more when I can write tests before I write code. But often I can’t do that, because I'm still figuring out how the code will work. To put it another way, I'm not sure where I'm going, so how can I ask for directions?</p>
<p>Another challenge is when there is already some code in place that was not written with tests in mind. The code is written like a black box, giving out no secrets, taking no prisoners or arguments. That's why tests should be written first.</p>
<p>Sometimes, I convince myself that it's quicker to do manual tests by refreshing the browser or repeating the commands in the terminal. Then I realise that I'm making slower progress, because automated tests are much faster and less error prone than manual tests. </p>
<p>Do I want to spend my time writing log statements and stepping through breakpoints? No, this sucks, but the alternative is that I'll have to set up <a target="_blank" href="https://jestjs.io">Jest</a>, <a target="_blank" href="https://mochajs.org/">Mocha</a> or <a target="_blank" href="https://www.cypress.io">Cypress</a>. </p>
<blockquote>
<p>Setting up tests is a one-off toll to get on the highway.</p>
</blockquote>
<p>Eventually, I find my way. Either the path becomes clear and I know what I'm aiming for, or I'm tired of repeating the same actions over and over - code, run, check, code, run, check. And I commit to writing the first test.</p>
<p>Code, check, repeat.</p>
]]></content:encoded></item><item><title><![CDATA[The OK Gatsby]]></title><description><![CDATA[I have been using Gatsby to build my personal portfolio site and after a few months, I don't think I would recommend it. I've had a few blogs and apps that I didn't maintain over the years and so having something easy to work with over time is a must...]]></description><link>https://iampeterbanjo.com/the-ok-gatsby</link><guid isPermaLink="true">https://iampeterbanjo.com/the-ok-gatsby</guid><category><![CDATA[Frontend Development]]></category><category><![CDATA[static]]></category><category><![CDATA[generators]]></category><category><![CDATA[Gatsby]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Mon, 08 Apr 2019 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/VvKdB1FYNZs/upload/v1651143681607/doY-Jr0Qq.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have been using Gatsby to build my personal portfolio site and after a few months, I don't think I would recommend it. I've had a few blogs and apps that I didn't maintain over the years and so having something easy to work with over time is a must have for this side project. <a target="_blank" href="https://gatsbyjs.org">GatsbyJS</a> uses the latest (as of writting) front-end tools to build static sites</p>
<h2 id="heading-features">Features</h2>
<ul>
<li>ReactJS (I know this one)</li>
<li>Webpack (I usually avoid this when I can)</li>
<li>Graph QL (Oooo... looks nice)</li>
<li>Service workers (Yikes)</li>
</ul>
<p>But the combination of all these together feels like riding a bicycle down hill with faulty brakes. I keep running into problems with no way out.</p>
<h2 id="heading-problems">Problems</h2>
<ul>
<li>What happened to my static pages? With Gatsby, although it looks like your generated "static" site has regular URLs it is <a target="_blank" href="https://github.com/gatsbyjs/gatsby/issues/962">TOTALLY dependent</a> on client-side JavaScript. I'm from the school of <a target="_blank" href="https://www.smashingmagazine.com/2009/04/progressive-enhancement-what-it-is-and-how-to-use-it/">Progressive Enhancement</a> and <a target="_blank" href="https://web.dev/installable/discover-installable">Progressive web apps</a> don't have to fall apart without JavaScript. The good news is that Google seems to be able to read my site fine. But there should be an "eject to HTML" button somewhere.</li>
<li>Cryptic webpack build errors. Lord deliver me from the evil of Webpack build errors! What does this error output mean?? 🤯</li>
</ul>
<pre><code class="lang-JavaScript">⢀ Building <span class="hljs-keyword">static</span> HTML <span class="hljs-keyword">for</span> pages/korin/profiles/Khalid/My-Bad

error Building <span class="hljs-keyword">static</span> HTML failed <span class="hljs-keyword">for</span> path <span class="hljs-string">"/korin/profiles/The-Black-Keys/LOHI"</span>

See our docs page on debugging HTML builds <span class="hljs-keyword">for</span> help https:<span class="hljs-comment">//gatsby.dev/debug-html</span>

   <span class="hljs-number">8</span> |  <span class="hljs-keyword">else</span>
   <span class="hljs-number">9</span> |          root[<span class="hljs-string">"lib"</span>] = factory(root[<span class="hljs-string">"@reach/router"</span>], root[<span class="hljs-string">"core-js/modules/es6.array.iterator"</span>], root[<span class="hljs-string">"core-js/modules/es6.array.sort"</span>], root[<span class="hljs-string">"core-js/modules/es6.function.name"</span>], root[<span class="hljs-string">"core-js/modules/es6.map"</span>], root[<span class="hljs-string">"core-js/modules/es6.object.assign"</span>], root[<span class="hljs-string">"core-js/modules/es6.object.to-string"</span>], root[<span class="hljs-string">"core-js/modules/es6.regexp.constructor"</span>], root[<span class="hljs-string">"core-js/modules/es6.regexp.replace"</span>], root[<span class="hljs-string">"core-js/modules/es6.regexp.split"</span>], root[<span class="hljs-string">"core-js/modules/es6.regexp.to-string"</span>], root[<span class="hljs-string">"core-js/modules/es6.string.ends-with"</span>], root[<span class="hljs-string">"core-js/modules/es6.string.fixed"</span>], root[<span class="hljs-string">"core-js/modules/es6.string.iterator"</span>], root[<span class="hljs-string">"core-js/modules/web.dom.iterable"</span>], root[<span class="hljs-string">"crypto"</span>], root[<span class="hljs-string">"fs"</span>], root[<span class="hljs-string">"lodash"</span>], root[<span class="hljs-string">"path"</span>], root[<span class="hljs-string">"react"</span>], root[<span class="hljs-string">"react-dom/server"</span>], root[<span class="hljs-string">"react-helmet"</span>]);
&gt; <span class="hljs-number">10</span> | })(<span class="hljs-built_in">this</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">__WEBPACK_EXTERNAL_MODULE__reach_router__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_array_iterator__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_array_sort__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_function_name__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_map__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_object_assign__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_object_to_string__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_regexp_constructor__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_regexp_replace__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_regexp_split__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_regexp_to_string__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_string_ends_with__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_string_fixed__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_es6_string_iterator__, __WEBPACK_EXTERNAL_MODULE_core_js_modules_web_dom_iterable__, __WEBPACK_EXTERNAL_MODULE_crypto__, __WEBPACK_EXTERNAL_MODULE_fs__, __WEBPACK_EXTERNAL_MODULE_lodash__, __WEBPACK_EXTERNAL_MODULE_path__, __WEBPACK_EXTERNAL_MODULE_react__, __WEBPACK_EXTERNAL_MODULE_react_dom_server__, __WEBPACK_EXTERNAL_MODULE_react_helmet__</span>) </span>{
     |  ^
  <span class="hljs-number">11</span> | <span class="hljs-keyword">return</span>


  WebpackError: Invariant Violation: Minified React error #<span class="hljs-number">130</span>; visit https:<span class="hljs-comment">//reactjs.org/docs/error-decoder.html?invariant=130&amp;args[]=object&amp;args[]= for the full message or   use the non-minified dev environment for full errors and additional helpful warnings.</span>

  - universalModuleDefinition:<span class="hljs-number">10</span> ba
    lib/webpack/universalModuleDefinition:<span class="hljs-number">10</span>:<span class="hljs-number">2</span>

<span class="hljs-comment">// truncated for brevity</span>
</code></pre>
<p>In this case the error is at the url buried in the terminal.</p>
<blockquote>
<p>Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.</p>
</blockquote>
<p>Usually with <a target="_blank" href="https://github.com/facebook/create-react-app#npm-start-or-yarn-start">create-react-app</a> you get error details nicely laid out in the terminal. So this Webpack format is not a step in the right direction.</p>
<ul>
<li>Service worker weirdness. This feature is meant to cache your site so that it works great with less than ideal internet connections - even offline. In practice it means that I have to keep refreshing my site to see what's on it. In development, my end-to-end tests sometimes fail on the first pass. Or second and third pass until I delete browser data or the Cypress binary... and re-install. Below is a screenshot of the same page on first load and then after refresh.</li>
</ul>
<p><img src="/images/Screenshot_2019-04-08-service-workers-no-tracks-found.png" alt="on first load" /></p>
<blockquote>
<p>Fig 1: On first load</p>
</blockquote>
<p><img src="/images/Screenshot_2019-04-08-service-workers-top-40-tracks-found.png" alt="after refresh" /></p>
<blockquote>
<p>Fig 2: After refresh</p>
</blockquote>
<p>Create react app has disabled their service workers as a default setting for <a target="_blank" href="https://twitter.com/dan_abramov/status/954146978564395008">similar reasons</a>.</p>
<p>So what's a developer to do? Keep it simple - Gatbsy was fun to test drive but I see too many maintenance issues ahead for my side project(s).</p>
]]></content:encoded></item><item><title><![CDATA[Qwik labs makes learning Machine learning easy]]></title><description><![CDATA[There are a number of ways to learn machine learning on Google Cloud Platform (GCP) but my favourite is using Qwik labs. What's great about it is that each lab has a preconfigured environment that includes all the essential permissions and setup.
I'v...]]></description><link>https://iampeterbanjo.com/qwik-labs-makes-learning-machine-learning-easy</link><guid isPermaLink="true">https://iampeterbanjo.com/qwik-labs-makes-learning-machine-learning-easy</guid><category><![CDATA[THW Cloud Computing]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[AWS]]></category><category><![CDATA[GCP]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Mon, 25 Feb 2019 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/2EJCSULRwC8/upload/v1651144877241/YZWiKaBI8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There are a number of ways to learn machine learning on <a target="_blank" href="https://cloud.google.com/">Google Cloud Platform (GCP)</a> but my favourite is using <a target="_blank" href="https://www.qwiklabs.com">Qwik labs</a>. What's great about it is that each lab has a preconfigured environment that includes all the essential permissions and setup.</p>
<p>I've tried using the cloud services of <a target="_blank" href="https://www.openshift.com/products">Open Shift</a>, <a target="_blank" href="https://aws.amazon.com/">Amazon Web Services (AWS)</a>, <a target="_blank" href="https://azure.microsoft.com">Microsoft Azure</a>, <a target="_blank" href="https://www.ibm.com/watson">IBM Watson</a> and <a target="_blank" href="https://cloud.google.com/">GCP</a> and I found that getting the service working with the right options is a major source of confusion. I found AWS to be the hardest - their fine granularity of rules and privileges felt tedious. </p>
<p>Qwik labs include a user login with the right permissions to complete the lab <a target="_blank" href="https://support.google.com/qwiklabs/answer/9115366?hl=en&amp;ref_topic=9114857">step-by-step</a>. This means that I could realistically commit an hour to learning the basics of Machine learning.</p>
<p>Qwik labs have <a target="_blank" href="https://support.google.com/qwiklabs/answer/9120902?hl=en&amp;ref_topic=9120803">pay-as-you-go or subscription pricing</a> but I managed to get some free credits for attending the <a target="_blank" href="https://devfest.withgoogle.com/events/devfestonair18">Google Devfest on air 2018</a>. And I'm glad I did.</p>
]]></content:encoded></item><item><title><![CDATA[GraphQL eats REST]]></title><description><![CDATA[As a web developer it's a strange curse to be afflicted with. The curse that every year at least some new technology in the JavaScript ecosystem gets replaced by something new, sometimes better and this time it was not even a framework - it was a spe...]]></description><link>https://iampeterbanjo.com/graphql-eats-rest</link><guid isPermaLink="true">https://iampeterbanjo.com/graphql-eats-rest</guid><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Tue, 19 Feb 2019 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>As a web developer it's a strange curse to be afflicted with. The curse that every year at least some new technology in the JavaScript ecosystem gets replaced by something new, sometimes better and this time it was not even a framework - it was a specification. And look how <a target="_blank" href="https://developer.github.com/v4/">the mighty have fallen</a>.</p>
<p>There is no REST for APIs except to buried in documentation because NOW the glorious age of GraphQL is upon us! Here are my top 3 reasons why GraphQL eats REST:</p>
<ol>
<li><a target="_blank" href="https://graphql.org/learn/best-practices/#versioning">No more API versions</a></li>
<li><a target="_blank" href="https://facebook.github.io/graphql/June2018/#sec-Type-System">A type system</a></li>
<li><a target="_blank" href="https://graphql.github.io/learn/queries/#fields">More economical with data</a></li>
</ol>
<p>I know if you're like me, you might already be <a target="_blank" href="https://www.goodreads.com/work/quotes/245494-the-great-gatsby">pursuing, busy and tired</a> of the constant change in our technology choices. I didn't set out to adopt GraphQL. All I wanted to do was create a new blog and then <a target="_blank" href="https://www.gatsbyjs.org/">Gatsby</a> showed up using GraphQL as a data handling layer. Anyway, it's fun learning new things but what I really want is mastery.</p>
]]></content:encoded></item><item><title><![CDATA[I like JSONata and so can you]]></title><description><![CDATA[JSON (JavaScript Object Notation) is a popular way to send and receive objects as strings. The syntax is terse and yet flexible enough to describe a lot of common objects - even deeply nested ones. That's nice, but what happens when you want to trans...]]></description><link>https://iampeterbanjo.com/i-like-jsonata-and-so-can-you</link><guid isPermaLink="true">https://iampeterbanjo.com/i-like-jsonata-and-so-can-you</guid><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Thu, 24 Jan 2019 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>JSON (JavaScript Object Notation) is a popular way to send and receive objects as strings. The syntax is terse and yet flexible enough to describe a lot of common objects - even deeply nested ones. That's nice, but what happens when you want to transform JSON from one shape to another? How about if you wanted to add comments to describe the transformation?</p>
<p>For example, I invite you to take a look at this JSON object describing the order history of a user.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"Account"</span>: {
    <span class="hljs-attr">"Order"</span>: [
      {
        <span class="hljs-attr">"OrderID"</span>: <span class="hljs-string">"order103"</span>,
        <span class="hljs-attr">"Product"</span>: [
          {
            <span class="hljs-attr">"Product Name"</span>: <span class="hljs-string">"Bowler Hat"</span>,
            <span class="hljs-attr">"ProductID"</span>: <span class="hljs-string">"858383"</span>,
            <span class="hljs-attr">"SKU"</span>: <span class="hljs-string">"0406654608"</span>,
            <span class="hljs-attr">"Price"</span>: <span class="hljs-string">"34.45"</span>,
            <span class="hljs-attr">"Quantity"</span>: <span class="hljs-string">"2"</span>
          },
          {
            <span class="hljs-attr">"Product Name"</span>: <span class="hljs-string">"Trilby hat"</span>,
            <span class="hljs-attr">"ProductID"</span>: <span class="hljs-string">"858236"</span>,
            <span class="hljs-attr">"SKU"</span>: <span class="hljs-string">"0406634348"</span>,
            <span class="hljs-attr">"Price"</span>: <span class="hljs-string">"21.67"</span>,
            <span class="hljs-attr">"Quantity"</span>: <span class="hljs-string">"1"</span>
          }
        ]
      },
      {
        <span class="hljs-attr">"OrderID"</span>: <span class="hljs-string">"order104"</span>,
        <span class="hljs-attr">"Product"</span>: [
          {
            <span class="hljs-attr">"Product Name"</span>: <span class="hljs-string">"Bowler Hat"</span>,
            <span class="hljs-attr">"ProductID"</span>: <span class="hljs-string">"858383"</span>,
            <span class="hljs-attr">"SKU"</span>: <span class="hljs-string">"040657863"</span>,
            <span class="hljs-attr">"Description"</span>: {
              <span class="hljs-attr">"Colour"</span>: <span class="hljs-string">"Purple"</span>,
              <span class="hljs-attr">"Width"</span>: <span class="hljs-string">"300"</span>,
              <span class="hljs-attr">"Height"</span>: <span class="hljs-string">"200"</span>,
              <span class="hljs-attr">"Depth"</span>: <span class="hljs-string">"210"</span>,
              <span class="hljs-attr">"Weight"</span>: <span class="hljs-string">"0.75"</span>
            },
            <span class="hljs-attr">"Price"</span>: <span class="hljs-string">"34.45"</span>,
            <span class="hljs-attr">"Quantity"</span>: <span class="hljs-string">"4"</span>
          },
          {
            <span class="hljs-attr">"ProductID"</span>: <span class="hljs-string">"345664"</span>,
            <span class="hljs-attr">"SKU"</span>: <span class="hljs-string">"0406654603"</span>,
            <span class="hljs-attr">"Product Name"</span>: <span class="hljs-string">"Cloak"</span>,
            <span class="hljs-attr">"Price"</span>: <span class="hljs-string">"107.99"</span>,
            <span class="hljs-attr">"Quantity"</span>: <span class="hljs-string">"1"</span>
          }
        ]
      }
    ]
  }
}
</code></pre>
<p>We could calculate the sum for every order by mapping over each item in the <code>Order</code> array. That would work but JSONata let's us do the same thing more simply.</p>
<pre><code class="lang-js/1">/* Sum all orders in account history */
$sum(Account.Order[0].Product.Price);
</code></pre>
<p>This example is taken from the <a target="_blank" href="http://try.jsonata.org/">JSONata exerciser</a> where you can try it out. What I love the most is the declarative way the transformations are described, errors are handled gracefully and it's even possible to write <a target="_blank" href="http://docs.jsonata.org/programming">functions</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Installing Postgres for Phoenix server]]></title><description><![CDATA[Dear Reader,
I was getting started with Phoenix server, which depends on a Postgres database, when the setup took more time than the get down-to-it. Here is a more linear setup to save time.

Install Postgres using adf
Create database for current use...]]></description><link>https://iampeterbanjo.com/installing-postgres-for-phoenix-server</link><guid isPermaLink="true">https://iampeterbanjo.com/installing-postgres-for-phoenix-server</guid><category><![CDATA[Phoenix framework]]></category><category><![CDATA[Elixir]]></category><category><![CDATA[server side]]></category><category><![CDATA[Functional Programming]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Tue, 07 Feb 2017 06:41:20 GMT</pubDate><content:encoded><![CDATA[<p>Dear Reader,</p>
<p>I was getting started with Phoenix server, which depends on a Postgres database, when the setup took more time than the get down-to-it. Here is a more linear setup to save time.</p>
<ol>
<li>Install Postgres using adf</li>
<li>Create database for current user</li>
<li>Change user password</li>
<li>Install phppgadmin</li>
<li>Create a new phppgadmin user for Phoenix</li>
<li>Update the config/dev.exs file  with database credentials</li>
</ol>
<p>1. Install Postgres using asdf</p>
<p>If asdf is not already installed go to the <a target="_blank" href="https://github.com/asdf-vm/asdf">github page</a> and follow the instructions. Then run the following commands to install the Postgres plugin and set it globally.</p>
<p>$ asdf plugin-add postgres https://github.com/smashedtoatoms/asdf-postgres.git
$ asdf install postgres 9.6.1
$ asdf global postgres 9.6.1</p>
<p>2. Create a database for the current user</p>
<p>As of writting, Postgres automatically creates a user and associates a database with them. But that database is not always created which leads to errors like these.</p>
<pre><code>$ psql: FATAL:  <span class="hljs-keyword">database</span> "user" does <span class="hljs-keyword">not</span> exist
</code></pre><p>To fix this run:</p>
<p>$ createdb your_username</p>
<p>From <a target="_blank" href="https://stackoverflow.com/questions/17633422/psql-fatal-database-user-does-not-exist">this stackoverflow answer</a> it looks like the “user” defaults to the current user which is correct in our case.</p>
<p>3. Change user password</p>
<p>Now that a database has been created, a password has to be set. Run the command below and follow the prompt.</p>
<p>$ postgres=# \password</p>
<p>4. Install phppgadmin</p>
<p>Your installation steps will naturally depend on your platform so why don’t we <a target="_blank" href="https://www.google.co.uk/search?q=install+phppgadmin&amp;oq=install+phppgadmin">ask the professor</a> instead.</p>
<ol>
<li>Create a new phppgadmin user for Phoenix server</li>
</ol>
<p>In Postgres, roles are users so I use the terms interchangeably. Although it’s not necessary to create a new role for development, I do this to avoid giving an application super user privileges. In my setup, my application user is called “developer” and it never expires, inherits from my admin user with login, create db and create roles privileges.</p>
<p>In your browser go to <a target="_blank" href="http://localhost/phppgadmin/">localhost/phppgadmin</a> and login with your user from step 3. Select the roles tab and then create a new role.</p>
<p>6. Update the config/dev.exs file with database credentials</p>
<p>After all that, we can safely run.</p>
<p>$ mix ecto.create</p>
]]></content:encoded></item><item><title><![CDATA[In Elixir data is like a rose]]></title><description><![CDATA[“A rose by another name would smell as sweet” – Juliet (to Romeo), William Shakespeare’s Romeo and Juliet.

Dear Reader,
Elixir is described as a functional programming language with immutable data but somehow you can rebind the values to a variable....]]></description><link>https://iampeterbanjo.com/in-elixir-data-is-like-a-rose</link><guid isPermaLink="true">https://iampeterbanjo.com/in-elixir-data-is-like-a-rose</guid><category><![CDATA[uncategorized]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Sun, 22 Jan 2017 19:58:34 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>“A rose by another name would smell as sweet” – Juliet (to Romeo), William Shakespeare’s Romeo and Juliet.</p>
</blockquote>
<p>Dear Reader,</p>
<p>Elixir is described as a functional programming language with immutable data but somehow you can rebind the values to a variable. Why is this and how can the data still be called immutable? Let’s start with how it is done.</p>
<p>When we create a variable and assign it a value the computer does two things – it allocates a space of memory to save the value and returns an address to this memory space. We often associate these two operations in the word “variable” i.e. a memory space and its address. In Elixir the value at that memory space never changes but we can reassign the label to a new memory space.</p>
<p>Why it’s done this way can be shown by example.</p>
<p># pretending we can't change the data at all...
age = 12</p>
<h1 id="heading-to-add-one-we-need-a-new-variable">...to add one we need a new variable</h1>
<p>old = add_one(age) #=&gt; 13
older = add_one(age) #=&gt; 14</p>
<p>This is OK but it can be quite verbose because we need a new label each time we transform the data. To avoid this pattern, and for your convenience, Elixir makes a new copy of the data and rebinds the label in one step.</p>
<p># new copy, same label
age = 12
age = add_one(age) #=&gt; 13</p>
<p>Immutable data is an advantage because it prevents processes that change data competing for the same memory spaces. This competition can causes data corruption, deadlocks and other issues that can be difficult to find and fix.</p>
<p>Sincerely,</p>
<p>Peter</p>
]]></content:encoded></item><item><title><![CDATA[Installing spf13-vim on Mac OS X]]></title><description><![CDATA[Upgrade to recent version of Mac OS X
Install Xcode
Fix brew install directories

sudo chown -R $USER /usr/local/include
sudo chown -R $USER /usr/local/bin
sudo chown -R $USER /usr/local/share

If Xcode command line tools were installed, change their...]]></description><link>https://iampeterbanjo.com/installing-spf13-vim-on-mac-os-x</link><guid isPermaLink="true">https://iampeterbanjo.com/installing-spf13-vim-on-mac-os-x</guid><category><![CDATA[uncategorized]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Wed, 24 Feb 2016 14:03:04 GMT</pubDate><content:encoded><![CDATA[<p>Upgrade to recent version of Mac OS X</p>
<p>Install Xcode</p>
<p>Fix brew install directories</p>
<ul>
<li>sudo chown -R $USER /usr/local/include</li>
<li>sudo chown -R $USER /usr/local/bin</li>
<li>sudo chown -R $USER /usr/local/share</li>
</ul>
<p>If Xcode command line tools were installed, change their directory to Xcode</p>
<ul>
<li>sudo xcode-select -s /Applications/Xcode.app/Contents/Developer</li>
</ul>
<p>Install ctags, Vim and MacVim with Lua</p>
<ul>
<li>brew install macvim –with-cscope –with-lua</li>
<li>brew install ctags</li>
<li>brew linkapps macvim</li>
<li>brew install vim –with-lua</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[6 lessons from my first hackathon]]></title><description><![CDATA[This past weekend I participated in the hacksummit.org hackathon hosted on the Koding platform. The hackathon started on Saturday February 20th 0:00hrs to Sunday 23:59hrs Pacific Time.  We started with a simple idea, find an interesting data set, lik...]]></description><link>https://iampeterbanjo.com/6-lessons-from-my-first-hackathon</link><guid isPermaLink="true">https://iampeterbanjo.com/6-lessons-from-my-first-hackathon</guid><category><![CDATA[hackathon]]></category><category><![CDATA[technology]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[development]]></category><category><![CDATA[general]]></category><category><![CDATA[innovation]]></category><dc:creator><![CDATA[Peter Banjo]]></dc:creator><pubDate>Wed, 24 Feb 2016 13:50:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1650231073384/dv3bwzIAw.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This past weekend I participated in the <a target="_blank" href="http://hacksummit.org">hacksummit.org</a> hackathon hosted on the <a target="_blank" href="https://koding.com">Koding</a> platform. The hackathon started on Saturday February 20th 0:00hrs to Sunday 23:59hrs Pacific Time.  We started with a simple idea, find an interesting data set, like emails, to send to IBM’s Watson <a target="_blank" href="http://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/personality-insights.html">personality insights</a> or <a target="_blank" href="http://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/tone-analyzer.html">tone analysis</a> engine. In the end, we made an <a target="_blank" href="https://salty-tor-17696.herokuapp.com/">app that worked</a> but not how we thought we would – here’s what I learnt *.</p>
<h2 id="heading-1-ask-for-help">#1 Ask for help</h2>
<p>I recruited my team by posting messages and getting in touch with other coders on the #hackathon channel. I ended up with a team of 6 and had to turn down a few others as we were too close to the start of the hackathon to bring them up to speed. I also had to get in touch with the Koding team to clarify some of the competition rules and they responded quickly.</p>
<h2 id="heading-2-adapt">#2 Adapt</h2>
<blockquote>
<p>“No plan survives contact with the enemy” – Helmet von Multke.</p>
</blockquote>
<p>We had 48 hours to complete the hackathon and after the first day we were still struggling to get data out of Gmail and the agreed mode of working was failing. We tried to keep track of commits and <a target="_blank" href="https://github.com/apanda6/apanda-hackathon/issues">issues on Github</a> but we couldn’t close them fast enough. In addition, two team members were not able to fully participate due to personal and other issues.</p>
<h2 id="heading-3-start-with-the-deal-breakers">#3 Start with the deal breakers</h2>
<p>Interestingly I had read <a target="_blank" href="https://backchannel.com/the-secret-to-moonshots-killing-our-projects-49b18dc7f2d6#.ehgfku3q4">this about the Google[X]’s</a> approach to projects last week:</p>
<blockquote>
<p>Ask cheerfully, “How are we going to try to kill our project today!”</p>
</blockquote>
<p>But I had not yet internalised this lesson.</p>
<p>Our app idea involved connecting with IBM Watson Personality Insights and Gmail but we only prototyped the IBM Watson service and no one in the team had experience with getting data out of Gmail. We assumed that the Google+ login API would give us a token that would work for all Google services as long as we enabled the service in the <a target="_blank" href="https://console.developers.google.com">Google Developer Console</a>. In retrospect this was a critical assumption which should have been verified first and if false, we should have changed our plans before the hackathon started.</p>
<h2 id="heading-4-communication-overheads-slow-down-team-velocity">#4 Communication overheads slow down team velocity</h2>
<p>We had a team of 6, including myself, and communicated mostly on Slack and once on Skype. Our main concern was agreeing on the app idea and not creating code conflicts. As new members joined it took longer to reach a consensus because our team was distributed (London, Switzerland, India and Romania) and it took more time to explain and go through everyone’s contributions. The ideal number might be less than 4. This reminds me of analogy of the <a target="_blank" href="http://javatroopers.com/Mythical_Man_Month.html#Chapter_7">tower of Babel</a> in the Mythical Man Month by Brooks.</p>
<h2 id="heading-5-use-what-you-have">#5 Use what you have</h2>
<p>By Sunday night (GMT) it was clear that we had to move away from Gmail as a data source because we hadn’t figured it out well enough. So with only about 10 hours left I stood at my desk at about 22:30hrs and started coding until 05:30hrs non-stop. I imagined that the entertainment industry would be an interesting subject but it was difficult to find accessible APIs on such short notice. <a target="_blank" href="http://www.last.fm/api">Last.fm’s API</a> worked and so did <a target="_blank" href="http://www.lyricsnmusic.com/">Lyrics N Music</a> so I combined the two to create artist profiles based on their lyrics.</p>
<h2 id="heading-6-be-gracious">#6 Be gracious</h2>
<p>There was a little voice in my head wanting to give myself all the credit for the final app since I contributed most of the working code in the final submission. Maybe I could split credit according to the Github repo commits. Primarily our team was participating in the hackathon as a learning exercise and we all contributed time, ideas, suggestions and feedback – if not code. And if code is the expression of ideas, where do those ideas come from? As a developer I value working code highly but everything and everyone starts from somewhere. I would also like to give credit to my wife for her encouragement during my emotional roller coaster with doubt, ambition and sleep deprivation.</p>
<p>Check out our app – <a target="_blank" href="http://159.8.108.212:3000/">Song Intelligence</a></p>
<p>* British past tense of learn although often confused with “learned” by my American cousins. <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650231072414/SGVRv3pEE.png" alt="🙂" /></p>
]]></content:encoded></item></channel></rss>