<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Home on Sam Atkins</title><link>https://samatkins.co/</link><description>Recent content in Home on Sam Atkins</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><lastBuildDate>Wed, 12 Nov 2025 06:37:40 +0000</lastBuildDate><atom:link href="https://samatkins.co/index.xml" rel="self" type="application/rss+xml"/><item><title>New Tooling in 2025</title><link>https://samatkins.co/post/new-tooling-in-2025/</link><pubDate>Wed, 12 Nov 2025 06:37:40 +0000</pubDate><guid>https://samatkins.co/post/new-tooling-in-2025/</guid><description>&lt;p>I read a blog about the tools someone uses and this prompted the idea to write what I use. OK, I&amp;rsquo;m shamelessly copying the idea of writing about the tools I use day to day. (I can&amp;rsquo;t remember where I saw it but if I find it, I will add a link to it here.)&lt;/p>
&lt;p>After several years of using the same old tools day in and day out, what surprised me this year is not just the new tools I use but how my typical day writing code has changed. AI coding agents getting good is the reason for this.&lt;/p>
&lt;p>Enough of an intro, here are the tools I use at work for software development.&lt;/p>
&lt;h2 id="claude-code">&lt;a href="https://www.anthropic.com" target="_blank" rel="noopener">Claude Code&lt;/a>&lt;/h2>
&lt;p>Let&amp;rsquo;s start with a tool which has fundamentally changed how I work and the tools I now use.&lt;/p>
&lt;p>Claude Code is an AI tool that for me lives up to the hype. It isn&amp;rsquo;t perfect but I consider it an invaluable tool that I use everyday in my job. Claude works away in the background until I&amp;rsquo;m notified (via a hook) that requires my input. I review and may edit the diff in my IDE and either continue coding from there or go back with another prompt to let Claude continue.&lt;/p>
&lt;h2 id="ghostty">&lt;a href="https://ghostty.org" target="_blank" rel="noopener">Ghostty&lt;/a>&lt;/h2>
&lt;p>I liked iTerm. I liked the idea of Warp but wanted more granular control over settings, config and so on.&lt;/p>
&lt;p>Ghostty is a super fast terminal, with zero config required&amp;hellip; but if you do want to configure it (like I did), it can be done in a config file. Perfect for adding to however you manage dotfiles and configuration.&lt;/p>
&lt;p>I can add things like &lt;a href="https://atuin.sh" target="_blank" rel="noopener">&lt;code>atuin&lt;/code>&lt;/a> for shell history and &lt;a href="https://fishshell.com" target="_blank" rel="noopener">Fish shell&lt;/a> for 90s goodness. I held off switching to Fish for so long, I was so invested in my bash dotfiles and aliases etc. But so glad I made the switch, so much easier to work with.&lt;/p>
&lt;h2 id="zellij">&lt;a href="https://zellij.dev" target="_blank" rel="noopener">Zellij&lt;/a>&lt;/h2>
&lt;p>For those missing features in Ghostty (like search), then Zellij is the perfect companion. An easier onboarding experience than tmux. I&amp;rsquo;m super happy with Zellij. I have custom layouts for my dev work, with split panes with e.g. Lazydocker or Claude Code autoloaded.&lt;/p>
&lt;h2 id="jj">&lt;a href="https://jj-vcs.github.io/jj/latest/" target="_blank" rel="noopener">jj&lt;/a>&lt;/h2>
&lt;p>I&amp;rsquo;ve been using Jujutsu Version Control System aka &lt;code>jj&lt;/code> since the start of the year and I love it. No more trying to work out arcane git magic. A simple, consistent UI, yet just as powerful as &lt;code>git&lt;/code>. It took me awhile to get my head around it but now I would not go back to vanilla &lt;code>git&lt;/code>.&lt;/p>
&lt;h2 id="zed">&lt;a href="https://zed.dev" target="_blank" rel="noopener">Zed&lt;/a>&lt;/h2>
&lt;p>I&amp;rsquo;m amazed that my IDE is not as important as other dev tooling. It was always number one on my list. That&amp;rsquo;s how much Claude Code has changed my ways of working.&lt;/p>
&lt;p>I was a long time user of VS Code and before that Sublime Text. Zed reminds me of Sublime with its responsiveness. It&amp;rsquo;s a clean UI, packed with functionality. When they launched the debugger that was when I could make the full time switch from VS Code. I would use this editor alone for the Subtle Mode when using Edit Predictions.&lt;/p>
&lt;p>It&amp;rsquo;s a great IDE. Typically these days I use to review a diff from Claude and do some light editing before either giving another prompt or committing the code.&lt;/p>
&lt;h2 id="obsidian">&lt;a href="https://obsidian.md" target="_blank" rel="noopener">Obsidian&lt;/a>&lt;/h2>
&lt;p>My goto for note taking. The sync service is fantastic at syncing between my laptop and my phone. I keep all my notes here, from work to life to study. A great product.&lt;/p>
&lt;h2 id="antinote">&lt;a href="https://antinote.io" target="_blank" rel="noopener">Antinote&lt;/a>&lt;/h2>
&lt;p>I love Obsidian but sometimes I just need scratch notes that are quick to hand. This is where Antinote shines. And if a scratch note does need to be saved it is easy to export it to Obsidian.&lt;/p></description></item><item><title>How I Use Every Claude Code Feature</title><link>https://samatkins.co/post/how-i-use-every-claude-code-feature/</link><pubDate>Tue, 04 Nov 2025 07:38:55 +0000</pubDate><guid>https://samatkins.co/post/how-i-use-every-claude-code-feature/</guid><description>&lt;p>This is a great read. Lots of information on how to use all the features of Claude Code. Lots here to go through and experiment with. In particular, I was wrong regarding the &lt;code>CLAUDE.md&lt;/code> file and will look at the approach here to improve my use of this file.&lt;/p></description></item><item><title>Learning to Be a Coding Agent Babysitter</title><link>https://samatkins.co/post/learning-to-be-a-coding-agent-babysitter/</link><pubDate>Sat, 18 Oct 2025 09:50:50 +0100</pubDate><guid>https://samatkins.co/post/learning-to-be-a-coding-agent-babysitter/</guid><description>&lt;p>The past few months I have been learning to be an &lt;a href="https://samatkins.co/post/adventures-in-babysitting-coding-agents/">&lt;code>AI coding agent babysitter&lt;/code>&lt;/a>. Sometimes it works well, sometimes less so. Overall I feel like it has been a productivity boost. The sweet spot is in areas where I have good expertise, so I can tell if the agent&amp;rsquo;s output is good or not.&lt;/p>
&lt;p>I&amp;rsquo;m still learning and trying to improve my babysitting skills. Here&amp;rsquo;s what I&amp;rsquo;ve learned so far.&lt;/p>
&lt;h2 id="regularly-clear-the-context">Regularly clear the context&lt;/h2>
&lt;p>If I only had one tip, it would be this one. The key to clearing context regularly is to give really small and tightly defined tasks.&lt;/p>
&lt;p>If the output isn&amp;rsquo;t what I want, I might edit some files myself or give a new prompt in the current context. Typically after a maximum of three or four prompts, even if the output so far isn&amp;rsquo;t what I want, I will clear the context and start over with a new prompt.&lt;/p>
&lt;h2 id="use-planning-mode">Use planning mode&lt;/h2>
&lt;p>I use planning mode to explore new areas of a codebase, to research a task or investigate possible areas where there might be a bug in the code.&lt;/p>
&lt;p>When planning work and I have a good output from the agent, I use a snippet to ask it to tell the agent to &amp;ldquo;write this output as a markdown file in &lt;code>{dir}&lt;/code>. Give the file an appropriate name and start it with today&amp;rsquo;s date &lt;code>{date:medium}&lt;/code> in YYYYMMDD format&amp;rdquo;.&lt;/p>
&lt;h2 id="use-hooks">Use hooks&lt;/h2>
&lt;p>I have a hook with Claude Code that sends a notification when it needs me. For example, this users terminal-notifier on my Mac:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;hooks&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Notification&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;matcher&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;hooks&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;terminal-notifier -message \&amp;#34;Claude needs your input\&amp;#34; -title \&amp;#34;Claude Code\&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This way, if I&amp;rsquo;ve switched focus, I am notified and don&amp;rsquo;t waste time with Claude doing nothing.&lt;/p>
&lt;p>Another hook formats the code after Claude is done. This removes the need to include this in the prompt or worry about if the agent is going to ignore the instruction in the &lt;code>Claude.md&lt;/code> to always format the code.&lt;/p>
&lt;h2 id="write-tests-yourself">Write tests yourself&lt;/h2>
&lt;p>Leave Claude alone to write tests and the output is usually substandard. For example lots of duplicated test cases and tests that don&amp;rsquo;t even test production code. It could be a fragrant abuse of Mocks or by writing actual production code within the test and then asserting the code passes.&lt;/p>
&lt;p>I will either write the tests myself, or at least the outline of the test and ask the agent to complete writing the test.&lt;/p>
&lt;p>Whichever approach I go for, I always review the output. This is necessary for all code the agent writes but especially the tests. If your tests are bad, you can have even less confidence in the production code the agent writes.&lt;/p>
&lt;h2 id="code-reviews">Code reviews&lt;/h2>
&lt;p>After I&amp;rsquo;ve refactored, I will often ask Claude to do a code review of the diff. I do this in plan mode so my diff won&amp;rsquo;t be overwritten. It can sometimes provide some useful feedback that leads to a new prompt in a new context to refactor the code.&lt;/p>
&lt;h2 id="agent-md-files">Agent MD files&lt;/h2>
&lt;p>I find these useful for two key reasons. When I&amp;rsquo;m new to a codebase, it&amp;rsquo;s a useful summary of the repo. An outline of the architecture, what is this repo about etc. Secondly, for the agent, it is useful to provide it with commands for running tests.&lt;/p>
&lt;p>I&amp;rsquo;ve watched an agent try 3 different ways to run tests before I interrupt to remind it activate the Python venv first then run the test command. With this instruction in the Agent markdown file, this type of issue is greatly reduced.&lt;/p>
&lt;p>Do these files help more than that? I&amp;rsquo;m not so sure, but I could be wrong. And all the providers always recommend it, so I still do it for every new repo I work on.&lt;/p>
&lt;h2 id="multiple-agents-in-parallel">Multiple agents in parallel&lt;/h2>
&lt;p>I’ve read about engineers running multiple agents with git work trees. The bottleneck for me is reviewing the output and the context switching involved in that. Running two or more implementing agents is hard mental work so I do this in limited bursts.&lt;/p>
&lt;p>What I do is research or planning tasks with one agent whilst I’m working on the implementation of another. It means once I’m ready to put the implementation work into pull request, I already have some research and a draft plan ready. I can review that in more detail and then start work.&lt;/p>
&lt;p>I want to experiment and iterate with this area some more so I consider this area a work in progress for me.&lt;/p></description></item><item><title>ty the new new hope</title><link>https://samatkins.co/post/ty-the-new-new-hope/</link><pubDate>Sat, 27 Sep 2025 10:10:07 +0100</pubDate><guid>https://samatkins.co/post/ty-the-new-new-hope/</guid><description>&lt;p>This is a riff on my old post about &lt;a href="https://samatkins.co/post/uv-the-new-hope/">&lt;code>uv&lt;/code>&lt;/a>. Astral have succeeded with &lt;code>ruff&lt;/code> and &lt;code>uv&lt;/code>. Can they also succeed with &lt;code>ty&lt;/code>?&lt;/p>
&lt;p>&lt;code>mypy&lt;/code> is good and useful. If you accept the short comings of type annotations in Python. To me, it still feels like a bolt on to the language rather than a real part of it. In particular, when some libraries are not typed it can cause issues. My real issue? It is really slow.&lt;/p>
&lt;p>Can Astral work their magic again?&lt;/p></description></item><item><title>uv reflections</title><link>https://samatkins.co/post/uv-reflections/</link><pubDate>Sat, 09 Aug 2025 08:35:09 +0100</pubDate><guid>https://samatkins.co/post/uv-reflections/</guid><description>&lt;p>Sometime ago, I wrote about my hopes for &lt;a href="https://samatkins.co/post/uv-the-new-hope/" target="_blank" rel="noopener">&lt;code>uv&lt;/code>&lt;/a>. It has surpassed my expectations. There isn&amp;rsquo;t always the time to migrate old projects at work, but for all future projects I by default reach for &lt;code>uv&lt;/code>.&lt;/p>
&lt;p>What I love is it has brought a &amp;ldquo;Cargo&amp;rdquo; like packaging experience to Python. Additions like &lt;code>uv run&lt;/code> and &lt;code>uvx&lt;/code> are also really useful.&lt;/p>
&lt;p>The game change for me is &lt;code>uv venv&lt;/code>. I no longer have to worry or care about installing different versions of Python. If I need to work on an older version of Python &lt;code>uv venv --python 3.10&lt;/code> will do the job, need a newer version &lt;code>uv venv --python 3.13&lt;/code>. You get the idea.&lt;/p>
&lt;p>What&amp;rsquo;s beautiful is if a Python version isn&amp;rsquo;t on my machine, &lt;code>uv venv&lt;/code> will download and install it. The &lt;code>uv venv&lt;/code> command has not only replaced my usage of the &lt;code>venv&lt;/code> tool but also Python version management tools like &lt;code>pyenv&lt;/code>.&lt;/p>
&lt;p>&lt;code>uv&lt;/code> is a great tool and another success from the team at Astral.&lt;/p></description></item><item><title>Adventures in Babysitting Coding Agents</title><link>https://samatkins.co/post/adventures-in-babysitting-coding-agents/</link><pubDate>Tue, 22 Jul 2025 19:57:40 +0100</pubDate><guid>https://samatkins.co/post/adventures-in-babysitting-coding-agents/</guid><description>&lt;p>This is a must listen. Will all Steve Yegge&amp;rsquo;s predictions come true? Only time will tell, but it is a fascinating conversation about the huge change we are seeing in software engineering right now.&lt;/p>
&lt;p>I&amp;rsquo;m not entirely convinced that multi-tasking by babysitting multiple agents is the way ahead for me but I am certainly going to experiment. It is what persuaded me to try Claude Code. Let&amp;rsquo;s see how I get on.&lt;/p></description></item><item><title>Claude Code Hooks</title><link>https://samatkins.co/post/claude-code-hooks/</link><pubDate>Fri, 18 Jul 2025 06:49:14 +0100</pubDate><guid>https://samatkins.co/post/claude-code-hooks/</guid><description>&lt;p>With Claude Code, I recommend using hooks. A small but important step towards helping make the output more deterministic.&lt;/p></description></item><item><title>Claude Code</title><link>https://samatkins.co/post/claude-code/</link><pubDate>Thu, 17 Jul 2025 18:53:33 +0100</pubDate><guid>https://samatkins.co/post/claude-code/</guid><description>&lt;p>The past month or so I’ve been experimenting with Claude Code. It is the first AI/LLM tool that has given me some &lt;a href="https://ghuntley.com/oh-fuck/" target="_blank" rel="noopener">&amp;ldquo;oh fuck&amp;rdquo;&lt;/a> moments. Genuinely impressed. And at the same time, it sometimes makes mistakes or writes terrible unit tests etc.&lt;/p>
&lt;p>On balance though a useful tool. It helps me with my tasks but I can&amp;rsquo;t trust it entirely, I need to code review every single line of code it produces.&lt;/p></description></item><item><title>uv - the new hope?</title><link>https://samatkins.co/post/uv-the-new-hope/</link><pubDate>Tue, 24 Sep 2024 19:09:11 +0000</pubDate><guid>https://samatkins.co/post/uv-the-new-hope/</guid><description>&lt;p>I&amp;rsquo;m genuinely excited about &lt;code>uv&lt;/code>, a new package manager and more for Python, from the creators and maintainers of &lt;code>ruff&lt;/code>. &lt;code>uv&lt;/code> started as a &lt;code>pip&lt;/code> alternative and has now expanded to become closer to their aspiration of being Cargo (the Rust package manger) for Python.&lt;/p>
&lt;p>Firstly, what excites me about uv is it has the potential to be one tool that replaces many. It has functionality to be an alternative to &lt;code>pip&lt;/code>, &lt;code>poetry&lt;/code>, &lt;code>pipx&lt;/code> and &lt;code>pyenv&lt;/code>.&lt;/p>
&lt;p>Secondly, the speed of &lt;code>uv&lt;/code> is incredible.&lt;/p>
&lt;p>Last, and by no means least, what excites me is the workflows behind certain commands. For example:&lt;/p>
&lt;ul>
&lt;li>If you have a &lt;code>uv&lt;/code> project e.g. you have run &lt;code>uv init&lt;/code>, then adding a dependency will auto create a virtual env if one does not already exist, activate it and install the dependency into it. All this by running &lt;code>uv add {pkg}&lt;/code>.&lt;/li>
&lt;li>If you invoke your script or app with &lt;code>uv run&lt;/code> e.g. &lt;code>uv run main.py&lt;/code> then &lt;code>uv&lt;/code> will check the lockfile and virtualenv are consistent and run your Python code in the activated virtual env.&lt;/li>
&lt;/ul>
&lt;p>I think abstracting away the virtualenv like this can be really helpful to Python beginners. And I will really enjoy not having to remember some additional steps when trying to get something done.&lt;/p>
&lt;p>All my personal Python projects will use &lt;code>uv&lt;/code> from now on. I really hope it catches on and becomes &lt;em>the&lt;/em> Python package manager.&lt;/p></description></item><item><title>Die zähen Gespräche haben ein Ende</title><link>https://samatkins.co/post/die-zaehen-gespraeche-haben-ein-ende/</link><pubDate>Wed, 18 Jan 2023 19:47:01 +0000</pubDate><guid>https://samatkins.co/post/die-zaehen-gespraeche-haben-ein-ende/</guid><description>&lt;blockquote>
&lt;p>Die zähen Gespräche haben ein Ende&lt;/p>&lt;/blockquote>
&lt;p>The tough discussions have an end.&lt;/p>
&lt;p>Yann Sommer is joining Bayern for EUR 8 million plus bonus payments. The Swiss keeper will sign a contract until 2025.&lt;/p>
&lt;p>Thank goodness. Really couldn’t see Bayern winning anything this season without signing a top class keeper.&lt;/p>
&lt;p>Will certainly make things interesting when Neuer gets back to full fitness. Of course, that should read &lt;em>if&lt;/em> as there is no guarantee he can return to his previous exceptionally high standard.&lt;/p></description></item><item><title>Klimaterroristen, das sind wir doch alle</title><link>https://samatkins.co/post/klimaterroristen-das-sind-wir-doch-alle/</link><pubDate>Fri, 13 Jan 2023 07:21:56 +0000</pubDate><guid>https://samatkins.co/post/klimaterroristen-das-sind-wir-doch-alle/</guid><description>&lt;p>The title translates as “Climate terrorists, but that&amp;rsquo;s all of us”.&lt;/p>
&lt;p>Some background. The article is about the “Unword of the Year” award for 2022. This annual award is ran by a language-critical campaign group originally launched in 1991. Klimaterrorist - climate terrorist in English - was selected as the “Unword” for 2022.&lt;/p>
&lt;p>The newspaper Die Zeit had this to say:&lt;/p>
&lt;blockquote>
&lt;p>Man muss sich den echten Klimaterroristen als recht normalen Europäer vorstellen. Morgens wacht er in einem aus Plastik und Furnier zusammengelatteten Möbelhausbett auf, dann duscht er 20 Minuten sehr warm, holt noch etwas Wäsche aus dem Trockner, verspeist ein paar Wurstbrote für die Freiheit und steigt sodann in sein Kraftfahrzeug. Mit dem gerät er irgendwo in einen Stau, den er so lange als schicksalsgegeben hinnimmt, bis er davon Wind kriegt, dass am Staubeginn ein paar Aktivistinnen auf der Straße hocken.&lt;/p>
&lt;p>Dann wird er wütend und gebraucht böse Wörter, von denen eins nun von der sprachkritischen Aktion Unwort des Jahres zum Unwort des Jahres 2022 gewählt wurde. Dass es in diesem Fall nicht die boulevardesken Klimakleber sind, sondern die diskursiv etwas herausfordernderen Klimaterroristen, ermöglicht diesen Texteinstieg. Wie schon Klimahysterie für das Jahr 2019 lässt sich die Sache recht einfach umdrehen und gegen die richten, die das Wort eigentlich im Munde führen. Die Kopplung ist schließlich maximal unklar und allein das qualifiziert das Unwort als solches: Es ist sprachlich schlecht gemacht. Denn wer terrorisiert hier nun eigentlich wen oder was und in wessen Namen?&lt;/p>&lt;/blockquote>
&lt;p>My translation:&lt;/p>
&lt;blockquote>
&lt;p>You have to imagine the real climate terrorist as a fairly normal European. In the morning he wakes up in a plastic and veneer furniture store bed, takes a very warm shower for 20 minutes, gets some clothes out of the dryer, eats a few ham sandwiches in the name of freedom and then gets into his car. He gets into a traffic jam somewhere, which he accepts as fated until he gets wind of the fact that a few activists are squatting on the road at the beginning of the jam.&lt;/p>
&lt;p>Then he gets angry and uses nasty words, one of which has now been chosen by the language-critical campaign ‘Unwort des Jahres’ as the Unword of the Year 2022. The fact that in this case it is not the tabloid climate stickers, but the discursively somewhat more challenging climate terrorists, makes it possible for this to be an Unword contender. Like &amp;lsquo;climate hysteria&amp;rsquo; in 2019, the matter can be quite easily turned around and directed against those who actually utter the word. The linking of the words (climate and terrorist) is after all maximally unclear and that alone qualifies the Unwort as such: it is linguistically badly made. For who is actually terrorising whom or what and in whose name?&lt;/p>&lt;/blockquote>
&lt;p>And this really is the clincher:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>For who is actually terrorising whom or what and in whose name?&lt;/strong>&lt;/p>&lt;/blockquote></description></item><item><title>Town Complete Broadhead Signing</title><link>https://samatkins.co/post/town-complete-broadhead-signing/</link><pubDate>Thu, 12 Jan 2023 18:55:50 +0000</pubDate><guid>https://samatkins.co/post/town-complete-broadhead-signing/</guid><description>&lt;p>With Broadhead and Hirst signing, I’m genuinely excited at the additional firepower that Town have upfront now. It’s a strong squad. Here’s to us maintaining our promotion push this season.&lt;/p></description></item><item><title>Dev on the Road</title><link>https://samatkins.co/post/dev-on-the-road/</link><pubDate>Thu, 12 Jan 2023 12:25:57 +0000</pubDate><guid>https://samatkins.co/post/dev-on-the-road/</guid><description>&lt;p>I’ve always decided against getting an iPad because I want a machine I can develop on. There are good options in this post to make an iPad enough of a dev machine to fix minor emergencies if you are on the road. One of the options is using &lt;a href="https://www.gitpod.io/" target="_blank" rel="noopener">Gitpod.io&lt;/a>. I&amp;rsquo;ve experimented with it before and was impressed.&lt;/p>
&lt;p>Although if I had to choose between a MacBook or an iPad, I would still choose a MacBook.&lt;/p></description></item><item><title>Bring Back Personal Blogging</title><link>https://samatkins.co/post/bring-back-personal-blogging/</link><pubDate>Thu, 12 Jan 2023 12:20:17 +0000</pubDate><guid>https://samatkins.co/post/bring-back-personal-blogging/</guid><description>&lt;blockquote>
&lt;p>The biggest reason personal blogs need to make a comeback is a simple one: we should all be in control of our own platforms.&lt;/p>&lt;/blockquote>
&lt;p>Amen. This was my motivation to revamp my blog this year.&lt;/p></description></item><item><title>Books I Want to Read in 2023</title><link>https://samatkins.co/post/books-i-want-to-read-in-2023/</link><pubDate>Tue, 03 Jan 2023 17:25:20 +0000</pubDate><guid>https://samatkins.co/post/books-i-want-to-read-in-2023/</guid><description>&lt;p>This year, like last year, I want to read some good software engineering books. These are the ones I currently have on my reading list:&lt;/p>
&lt;ul>
&lt;li>Leading Snowflakes by Oren Ellenbogen&lt;/li>
&lt;li>Modern Software Engineering by Dave Farley&lt;/li>
&lt;li>Building Microservices: Designing Fine-grained Systems by Sam Newman&lt;/li>
&lt;li>Machine Learning Engineering with Python by Andrew P. McMahon&lt;/li>
&lt;/ul>
&lt;p>Why do I want to read each of these books?&lt;/p>
&lt;h2 id="leading-snowflakes-by-oren-ellenbogen">Leading Snowflakes by Oren Ellenbogen&lt;/h2>
&lt;p>As someone recently promoted to Engineering Management, albeit with prior experience working in management, I am keen to read books to help new engineering managers. I enjoy Oren&amp;rsquo;s newsletter and have read good reviews for his book, so that is why Leading Snowflakes by Oren Ellenbogen is on my list.&lt;/p>
&lt;h2 id="modern-software-engineering-by-dave-farley">Modern Software Engineering by Dave Farley&lt;/h2>
&lt;p>This book has been recommended to me by several Engineers who I respect. This is enough of a reason to add it to my 2023 reading list.&lt;/p>
&lt;h2 id="building-microservices-designing-fine-grained-systems-by-sam-newman">Building Microservices: Designing Fine-grained Systems by Sam Newman&lt;/h2>
&lt;p>I really enjoyed and learned a lot from Saw Newman&amp;rsquo;s book Monolith to Microservices so in 2023 I want to read another book by him on microservices.&lt;/p>
&lt;h2 id="machine-learning-engineering-with-python-by-andrew-p-mcmahon">Machine Learning Engineering with Python by Andrew P. McMahon&lt;/h2>
&lt;p>This book interests me because I work to integrate machine learning into our product at work. I&amp;rsquo;m keen to read more about MLOps in particular but also to get more familiar with the parts of Machine Learning Engineering I don&amp;rsquo;t encounter every day at work.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This is my &amp;ldquo;minimum&amp;rdquo; list. I hope to read more software engineering and management books than this but if I only manage these then I shall still be satisfied.&lt;/p>
&lt;p>Which books do you have on your reading list for 2023?&lt;/p></description></item><item><title>Docker Multi-Stage Builds</title><link>https://samatkins.co/post/docker-multi-stage-builds/</link><pubDate>Sat, 06 Feb 2021 11:04:50 +0000</pubDate><guid>https://samatkins.co/post/docker-multi-stage-builds/</guid><description>&lt;p>At work, we&amp;rsquo;re using Docker multi-stage builds to get smaller image sizes. I thought I&amp;rsquo;d try it out with a small learning project. The headline result is I reduced a Docker image from 305MB to 12.2MB.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># before&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker image ls
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">helloworldapi latest b24c966e9bgg &lt;span class="m">6&lt;/span> weeks ago 305MB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># after&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker image ls
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">helloworldapi latest f1f76e06146h &lt;span class="m">7&lt;/span> seconds ago 12.2MB
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="multi-stage-builds">Multi-Stage Builds&lt;/h2>
&lt;p>How do multi-stage builds work? Every instruction i.e. line in a Dockerfile adds a layer to the image. A common practice was to have a development version of your Dockerfile which included everything you needed and a slimmed down version used for production. With multi-stage builds you don&amp;rsquo;t need to maintain two Dockerfiles. Instead you can use the syntax &lt;code>FROM ... AS &amp;lt;NAME&amp;gt;&lt;/code> to name a stage, and then copy that named stage when you want want to use it. Only what is needed is brought across in this &lt;code>COPY&lt;/code> and all the other layers are discarded. This results in smaller image sizes.&lt;/p>
&lt;p>Here&amp;rsquo;s an example from the learning &lt;a href="https://github.com/sam-atkins/helloworldapi" target="_blank" rel="noopener">project&lt;/a> I mentioned earlier. This is the Dockerfile before without using multi-stage. The image size was 305MB.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Dockerfile" data-lang="Dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="k">FROM&lt;/span>&lt;span class="s"> golang:1.15.5-alpine&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /app&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> . .&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> go mod download&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> go build -o main .&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">CMD&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;/app/main&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is the Dockerfile using a multi-stage, named as &lt;code>builder&lt;/code> and used like this &lt;code>COPY --from=builder /app .&lt;/code> i.e. copy the named stage into the &lt;code>/app&lt;/code> directory.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Dockerfile" data-lang="Dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="k">FROM&lt;/span>&lt;span class="s"> golang:1.15.5-alpine AS builder&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /app&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> main.go .&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> go build -o main .&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> alpine:latest&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /app&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> --from&lt;span class="o">=&lt;/span>builder /app .&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">CMD&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;/app/main&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As a result of using this multi-stage the image size is now 12.2MB.&lt;/p>
&lt;h2 id="named-stages">Named Stages&lt;/h2>
&lt;p>Stages are not named by default and you can refer to each stage by an integer starting at 0 for the first &lt;code>FROM&lt;/code>. So if the Dockerfile read &lt;code>FROM golang:1.15.5-alpine&lt;/code> we could copy it across like this &lt;code>COPY --from=0 /app .&lt;/code>. I prefer to use names as it makes it more obvious what is happening in the Dockerfile.&lt;/p>
&lt;h2 id="target-a-stage">Target a Stage&lt;/h2>
&lt;p>How can you use multi-stage builds? When building an image you can target a stage. For example, a build stage named &lt;code>dev&lt;/code> in a Dockerfile would be built like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker build --target dev -t &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">DOCKER_REGISTRY&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>/&lt;span class="si">${&lt;/span>&lt;span class="nv">IMAGE_NAME&lt;/span>&lt;span class="si">}&lt;/span>:&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">IMAGE_TAG&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">-dev&amp;#34;&lt;/span> .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The example above assumes variables are set for the Docker registry where the image is published, the image name and the image tag.&lt;/p>
&lt;p>If the image is published then you can use this in Docker Compose:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">service-name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">$DOCKER_REGISTRY/$IMAGE_NAME:$IMAGE_TAG-dev&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Alternatively, if the Dockerfile and source code are available on your local machine, you can target it like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">service-name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">context&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">path-to-service-name-source-code&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dev&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Git Hook Prepend Commit Message</title><link>https://samatkins.co/post/git-hook-prepend-commit-message/</link><pubDate>Sat, 11 Jan 2020 16:37:34 +0000</pubDate><guid>https://samatkins.co/post/git-hook-prepend-commit-message/</guid><description>&lt;p>At work we have to add the Jira issue number to each pull request, branch and commit. This is to help Management with planning. Unfortunately for me, adding the Jira ID to each commit often broke with my existing workflow or I often just simply forgot to do add it. To remove this as an issue, how about a script to automate prepending the git commit message with the Jira ticket ID?&lt;/p>
&lt;h2 id="git-hook-script">Git Hook Script&lt;/h2>
&lt;p>For the impatient, here&amp;rsquo;s the script.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="ch">#!/usr/bin/env python3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Git hook to automatically prefix a git commit message with an issue number
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">(e.g. Jira ticket number) from the current branch name.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">re&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">sys&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">subprocess&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">check_output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">commit_msg_filepath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">argv&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">branch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">check_output&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s2">&amp;#34;git&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;symbolic-ref&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--short&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;HEAD&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">regex&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;(chore|feature|fix|hotfix)\/(\w+-\d+)&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="k">match&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">regex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">branch&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">issue_number&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="k">match&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">regex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">branch&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">commit_msg_filepath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;r+&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">commit_msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">read&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">seek&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># correctly positions issue_number when writing commit message&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;[&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">issue_number&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">] &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">commit_msg&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>First up, the script has some prerequisites and makes some assumptions:&lt;/p>
&lt;ul>
&lt;li>Requires Python 3&lt;/li>
&lt;li>Git branches follow the convention of using chore, feature, fix or hotfix, a &lt;code>/&lt;/code> followed by the Jira issue number (e.g. EX-100) and then the &amp;ldquo;branch name&amp;rdquo; e.g. &lt;code>feature/EX-100-example-branch-name&lt;/code>.&lt;/li>
&lt;li>Assumes there are valid branches that do not require this script to run e.g. at work release branches do not contain a Jira ticket ID.&lt;/li>
&lt;/ul>
&lt;p>With that out the way, let&amp;rsquo;s walk through the script and see what it does.&lt;/p>
&lt;p>The shebang is important because we need to make sure this script executes using Python 3. Per the &lt;a href="https://docs.python.org/3/using/unix.html?highlight=shebang#miscellaneous" target="_blank" rel="noopener">Python docs&lt;/a>, &amp;ldquo;a good choice is usually &lt;code>#!/usr/bin/env python3&lt;/code>&amp;rdquo;.&lt;/p>
&lt;p>Next is a docstring to remind myself several months later what this script does. Then the imports required for the script. All imports are from the standard library so no need to pip install anything.&lt;/p>
&lt;p>The variable &lt;code>commit_msg_filepath&lt;/code> is a system argument passed into the script. We use this later on, opening it as a file to write the commit message.&lt;/p>
&lt;p>We find the branch name by using &lt;a href="https://docs.python.org/3.8/library/subprocess.html#subprocess.check_output" target="_blank" rel="noopener">&lt;code>subprocess.check_output&lt;/code>&lt;/a>&lt;/p>
&lt;p>From the Python docs:&lt;/p>
&lt;blockquote>
&lt;p>Run command with arguments and return its output. By default, this function will return the data as encoded bytes.&lt;/p>&lt;/blockquote>
&lt;p>It runs the git command &lt;code>git symbolic-ref --short HEAD&lt;/code> and the return is decoded to a string and stripped of whitespace.&lt;/p>
&lt;p>The regex pattern for the git branch naming convention is declared as the &lt;code>regex&lt;/code> variable. This allows the script to extract the Jira ID. This script is lenient so if there is no match, it assumes there is a valid reason for the branch not having a Jira ID. If the regex has a match we enter the &lt;code>if&lt;/code> block. This is the code which amends the commit message.&lt;/p>
&lt;p>The Jira issue number is group 2 in the &lt;code>match&lt;/code> regex. It opens the commit message as a file and reads in the contents of the file.&lt;/p>
&lt;p>&lt;code>seek()&lt;/code> is an interesting one. If writing a commit message in vim then &lt;code>seek(0, 0)&lt;/code> places the Jira ID as the first part of the commit message placeholder. Comment out &lt;code>seek(0, 0)&lt;/code> and you&amp;rsquo;ll see the Jira ID half way down in the git commit message verbiage yet it will still get written as the first part of the commit message. That&amp;rsquo;s because the final line writes the actually git commit message e.g. after you type it, save and exit Vim the message gets written as &lt;code>[EX-100] example commit message&lt;/code>.&lt;/p>
&lt;h2 id="using-the-script">Using the script&lt;/h2>
&lt;p>Finally, to make this script work there are two options.&lt;/p>
&lt;p>Option one is to add to a single repo or to each repo you want this script to run. This wasn&amp;rsquo;t an option for me as we have many micro-services which I might work on and didn&amp;rsquo;t want to add the script to each repo.&lt;/p>
&lt;p>Option two is to point global git config to a directory of your git hooks. This is what I went for and here&amp;rsquo;s how to do it.&lt;/p>
&lt;h3 id="global-git-hooks">Global Git Hooks&lt;/h3>
&lt;p>Save the script in a directory where you keep or plan to keep all your global git hooks. Make sure the script is executable &lt;code>chmod +x prepare-commit-msg&lt;/code>.&lt;/p>
&lt;p>Then run this to point the global hooks to your directory:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git config --global core.hooksPath /path/to/git_hooks
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Run &lt;code>cat .gitconfig&lt;/code> to check the output:&lt;/p>
&lt;pre tabindex="0">&lt;code> │ File: .gitconfig
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ [user]
2 │ name = Sam Atkins
...
5 │ [core]
6 │ hooksPath = /path-to/git_hooks
&lt;/code>&lt;/pre>&lt;h2 id="final-thought">Final thought&lt;/h2>
&lt;p>Here&amp;rsquo;s a link to the script on &lt;a href="https://github.com/sam-atkins/util-scripts/tree/main/git_hooks" target="_blank" rel="noopener">Github&lt;/a>. Thanks for reading.&lt;/p></description></item><item><title>Using Docker Commands with Aliases and Bash Functions</title><link>https://samatkins.co/post/using-docker-commands-aliases-bash-functions/</link><pubDate>Sun, 13 Oct 2019 07:06:45 +0100</pubDate><guid>https://samatkins.co/post/using-docker-commands-aliases-bash-functions/</guid><description>&lt;p>We use Docker at work and so I find myself running the same commands over and over. To save time I saved frequent &lt;code>docker&lt;/code> and &lt;code>docker-compose&lt;/code> commands as aliases and functions.&lt;/p>
&lt;h2 id="aliases">Aliases&lt;/h2>
&lt;p>These aliases save a few key strokes for running often used commands:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># general compose aliases&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">dcb&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;docker-compose build&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">dcu&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;docker-compose up&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">dcub&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;docker-compose up --build&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">dcs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;docker-compose stop&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># list all running containers&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">dps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;docker ps&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># stop all running containers&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">dsa&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;docker ps -q | awk &amp;#39;{print &lt;/span>&lt;span class="nv">$1&lt;/span>&lt;span class="s2">}&amp;#39; | xargs -o docker stop&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># list dangling images&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">dlist&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;docker volume ls -qf dangling=true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="functions">Functions&lt;/h2>
&lt;p>Some commands are better saved as functions.&lt;/p>
&lt;p>When developing and writing unit tests, I will often want to get to the bash prompt in the test container. I saved this function as &lt;code>dcbash.sh&lt;/code> and aliased it as &lt;code>dcbash&lt;/code> to save a few keystrokes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Run a bash shell in the specified container (with docker-compose)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># a simple help menu: if no arg is passed in, the function usage is printed&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> &lt;span class="nv">$#&lt;/span> -ne &lt;span class="m">1&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Usage: &lt;/span>&lt;span class="nv">$FUNCNAME&lt;/span>&lt;span class="s2"> CONTAINER_NAME&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># this echo is optional but I like to maintain familiarity with the actual command&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;CMD: docker-compose run --entrypoint=&amp;#34;&amp;#34; &lt;/span>&lt;span class="nv">$1&lt;/span>&lt;span class="s2"> /bin/bash&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker-compose run --entrypoint&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span> &lt;span class="nv">$1&lt;/span> /bin/bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For example &lt;code>dcbash pretend-service-test&lt;/code> runs &lt;code>docker-compose run --entrypoint=&amp;quot;&amp;quot; pretend-service-test /bin/bash&lt;/code>.&lt;/p>
&lt;p>Images take up space and are not auto removed. I&amp;rsquo;ve encountered errors before about lack of space so a periodic tidy up of unused images and containers is recommended.&lt;/p>
&lt;p>I saved this as &lt;code>drmi.sh&lt;/code> and it removes dangling Docker images:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># remove docker dangling images&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rmi &lt;span class="k">$(&lt;/span>docker images --filter &lt;span class="s2">&amp;#34;dangling=true&amp;#34;&lt;/span> -q --no-trunc&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I saved this as &lt;code>drmc.sh&lt;/code> and it removes all non-running containers:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># removes all non-running containers&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rm &lt;span class="k">$(&lt;/span>docker ps -q -a&lt;span class="k">)&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>These aliases and functions help my productivity by saving a key strokes and saving time looking up the commands for a periodic tidy up of Docker images and containers. I hope they may prove useful to you too.&lt;/p></description></item><item><title>Hugo Pipes</title><link>https://samatkins.co/post/hugo-pipes/</link><pubDate>Sat, 28 Sep 2019 13:47:05 +0100</pubDate><guid>https://samatkins.co/post/hugo-pipes/</guid><description>&lt;p>Hugo version 0.43 introduced Hugo Pipes which processes assets e.g. save preprocessed Sass files to CSS files. I finally got round to adding this as part of adapting the Ghostwriter theme I use on my site (a forked Ghostwriter theme by Juraj Bubniak.&lt;/p>
&lt;h2 id="why-hugo-pipes-and-not-webpack">Why Hugo Pipes and not Webpack&lt;/h2>
&lt;p>The theme had a webpack build for the Sass files which could be replaced by Hugo Pipes. This was attractive as it simplified the build process for my static site and removed webpack and a whole load of dependencies I no longer need to worry about keeping up-to-date.&lt;/p>
&lt;h2 id="how-i-added-hugo-pipes">How I added Hugo Pipes&lt;/h2>
&lt;p>The first step was to move the Sass files to a top level directory called &lt;code>/assets&lt;/code>.&lt;/p>
&lt;p>The second step was to add this pipeline:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ $style := resources.Get &amp;#34;sass/main.scss&amp;#34; | resources.ToCSS | resources.Minify | resources.Fingerprint }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $style.Permalink }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For organisation, I added the above to a new partial called &lt;code>layouts/partials/styles.html&lt;/code> and then included this partial into the &lt;code>header.html&lt;/code> partial using &lt;code>partialCached&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ partialCached &amp;#34;styles.html&amp;#34; . }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Why use &lt;code>partialCached&lt;/code>?&lt;/p>
&lt;blockquote>
&lt;p>allows for caching of partials that do not need to be re-rendered on every invocation&lt;/p>&lt;/blockquote>
&lt;p>After some testing to check it worked, the final step for local development was the happy task of deleting the webpack config and associated files.&lt;/p>
&lt;p>In terms of deploying, I use Netlify and there turned out to be one gotcha.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">error: failed to transform resource: TOCSS: failed to transform &lt;span class="s2">&amp;#34;scss/main.scss&amp;#34;&lt;/span> &lt;span class="o">(&lt;/span>text/x-scss&lt;span class="o">)&lt;/span>: this feature is not available in your current Hugo version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The issue per this &lt;a href="https://www.netlify.com/blog/2019/03/14/a-more-flexible-build-architecture-with-updated-linux/" target="_blank" rel="noopener">post from Netlify&lt;/a> is due to an older version of an Ubuntu image which was incompatible with the version of Hugo. In order to use Hugo Pipes you need to make sure you are using the new Ubuntu image. If you recently set up a Hugo site on Netlify this new Ubuntu image is the default. My site has been on Netlify for some time so I needed to go into the settings and select the new Ubuntu Xenial build image.&lt;/p>
&lt;p>&lt;img src="https://samatkins.co/img/netlify_build_image.png" alt="“Screenshot of Netlify UI menus to select an Ubuntu build image”">&lt;/p>
&lt;p>With that done, I re-deployed and this time the deploy went through and everything worked.&lt;/p></description></item><item><title>Empty Git Commits</title><link>https://samatkins.co/post/empty-git-commits/</link><pubDate>Wed, 18 Sep 2019 20:13:56 +0100</pubDate><guid>https://samatkins.co/post/empty-git-commits/</guid><description>&lt;p>Some build pipelines have steps which are only triggered if a certain phrase is contained in the commit message. The problem is sometimes e.g. after a code review there may be nothing to commit but you need to start this build process.&lt;/p>
&lt;p>Try doing an empty commit in VS Code and a warning message says &amp;ldquo;there are no changes to commit&amp;rdquo;.&lt;/p>
&lt;p>The solution when you need an empty commit is to use the allow empty flag and add the commit message you need:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git commit --allow-empty -m &lt;span class="s2">&amp;#34;[magic build]&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the example above the string &lt;code>[magic build]&lt;/code> triggers additional steps in the CI build. Problem solved.&lt;/p></description></item><item><title>Reflections on DevOps Barcelona 2019</title><link>https://samatkins.co/post/reflections-devops-barcelona-2019/</link><pubDate>Tue, 23 Jul 2019 20:08:21 +0100</pubDate><guid>https://samatkins.co/post/reflections-devops-barcelona-2019/</guid><description>&lt;p>In June I attended a DevOps conference in Barcelona. Whilst I work in web development, I have a growing interest in DevOps which is mainly due to my previous career in supply chain and lean. This was a key motivation for attending: to learn more about different people&amp;rsquo;s experiences with DevOps.&lt;/p>
&lt;p>I&amp;rsquo;ve had these high level thoughts sat in &amp;ldquo;draft&amp;rdquo; for a while so thought I&amp;rsquo;d smarten them up a bit and post to my blog.&lt;/p>
&lt;h3 id="summary">Summary&lt;/h3>
&lt;p>First up, it was a well run and good conference. I enjoyed that it was single track as there&amp;rsquo;s no feeling of missing out if two talks scheduled at the same time are of interest. Lots of good speakers and I&amp;rsquo;m full of admiration for everyone that presented, especially the majority doing so in a second language.&lt;/p>
&lt;p>As for the talks, there were several that really resonated with me.&lt;/p>
&lt;h3 id="devops-culture">DevOps Culture&lt;/h3>
&lt;p>Nationwide had two good talks on culture and building teams from Marc Cluet and Aubrey Stearn. It&amp;rsquo;s always exciting to hear about tools but people and culture are the foundation of DevOps. Both talks were excellent examples of how Nationwide are building a great DevOps culture.&lt;/p>
&lt;h3 id="security">Security&lt;/h3>
&lt;p>Shout out to Néstor Salceda at Sysdig for an impressive demonstration of Falco. Falco is a &amp;ldquo;behavioural activity monitor designed to detect anomalous activity in your applications&amp;rdquo;. Néstor Salceda&amp;rsquo;s demo was great because he demonstrated how the container was stopped once anomalous activity was detected. Very impressive.&lt;/p>
&lt;h3 id="managing-failure-in-a-distributed-world">Managing Failure in a Distributed World&lt;/h3>
&lt;p>The best talk in terms of execution and the slick demo was by Nic Jackson (and his colleague whose name I sadly can&amp;rsquo;t find in my notes) from Hashicorp about Managing Failure in a Distributed World. A very slick demo showed how multiple cloud providers could be used to ensure improve latency but also as a fallback if one cloud provider has an outage, traffic can be switched to another cloud provider.&lt;/p>
&lt;h3 id="testing-in-production">Testing in Production&lt;/h3>
&lt;p>My favourite talk was Alex Soto&amp;rsquo;s (Redhat) talk on testing in production. This was a real eye-opener and definitely something to read more on. It was controversial to me because I&amp;rsquo;d aways read and experienced directly at work that you have multiple stages e.g. dev for general development of features, QA or staging for testing of features and bug fixes before deploying to production.&lt;/p>
&lt;p>The approach outlined in the talk is all development is done in production behind feature flags. Internal users can toggle if they see production or beta/alpha development e.g. feature or bug fixes. As I understand it this approach extends from CI and trunk based development where all work is merged directly to trunk but behind feature flags until it is ready for prime time.&lt;/p>
&lt;p>The key benefit of the approach Alex outlined is that you only need to have one stage, namely production. However he warned it is very hard to and to start slowly. I still have a lot to learn so this idea is still quite scary to me, but the idea is eye opening and makes me rethink my previous assumptions on how things should be done.&lt;/p>
&lt;h3 id="final-thought">Final Thought&lt;/h3>
&lt;p>And finally, Barcelona is a beautiful city and I want to visit again. 😎&lt;/p></description></item><item><title>2 years as a professional software engineer</title><link>https://samatkins.co/post/2-years-professional-software-engineer/</link><pubDate>Mon, 15 Jul 2019 20:09:10 +0100</pubDate><guid>https://samatkins.co/post/2-years-professional-software-engineer/</guid><description>&lt;p>Time really does fly. As I write this, I have unlocked the two year milestone of working as a professional software engineer. It only feels like a short time ago I set myself the goal of changing career and becoming a professional software engineer. I feel incredibly privileged to have had the opportunity to change careers, to be working for Big Health and working in web development.&lt;/p>
&lt;p>I was discussing with a friend at work some tips he was collecting for a friend of his who was just starting out as a professional web developer. I thought it would be useful exercise to post an expanded write up of the advice that would have benefited me just before starting my job. My hope is that this may also prove useful to others.&lt;/p>
&lt;h3 id="learn-how-to-learn">Learn how to learn&lt;/h3>
&lt;p>There is one constant in this career and that is you should always be learning. Find out the most effective ways for you to learn in a sustainable manner and keep learning.&lt;/p>
&lt;h3 id="dont-reinvent-the-wheel">Don&amp;rsquo;t reinvent the wheel&lt;/h3>
&lt;p>I learn best by building stuff but there isn&amp;rsquo;t time to build everything. Give less weight to reinventing things and doing a lot of coding, and more attention to reusing other tools and investigating what&amp;rsquo;s out there.&lt;/p>
&lt;h3 id="dealing-with-imposter-syndrome">Dealing with imposter syndrome&lt;/h3>
&lt;p>Don&amp;rsquo;t compare yourself to other developers. Remember your own journey and progression. A trick I use to remind myself how much I have learned is to keep a journal of achievements. This keeps me grounded as I know I still have a lot to learn but looking at my journal I can see I have learned a lot in my 2 years as a professional software engineer.&lt;/p>
&lt;h3 id="hone-your-problem-solving">Hone your problem solving&lt;/h3>
&lt;p>Improving your problem solving skills will help with bug fixes and in general working in web development. I found this a good article on &lt;a href="https://dev.to/aspittel/moving-past-tutorials-8-tips-for-problem-solving-3e0p" target="_blank" rel="noopener">problem solving&lt;/a>.&lt;/p>
&lt;h3 id="broaden-your-horizons-and-stay-up-to-date">Broaden your horizons and stay up to date&lt;/h3>
&lt;p>Listening to podcasts and reading blogs are a good way to immerse yourself in the latest developments, get familiar with terminology, and learn about how other engineers think and work.&lt;/p>
&lt;p>In addition to finding good podcasts and blogs, read some of the best programming books out there to get familiar with best practice programming principles, software design and other important topics which interest you. Here&amp;rsquo;s a post on some &lt;a href="https://samatkins.co/post/recommended-programming-books/">good books&lt;/a> I ready last year (two of the books were recommend to me by my VP of Engineering).&lt;/p>
&lt;h3 id="its-not-just-about-the-code">It&amp;rsquo;s not just about the code&lt;/h3>
&lt;p>Soft skills are important. If you&amp;rsquo;re a career changer like me your soft skills you&amp;rsquo;ve developed will stand you in good stead. When you work as a professional programmer, you won&amp;rsquo;t be coding all day every day. You&amp;rsquo;ll be interacting with other people on a daily basis. Don&amp;rsquo;t neglect your soft skills.&lt;/p>
&lt;h3 id="understand-process">Understand process&lt;/h3>
&lt;p>I&amp;rsquo;ll admit I&amp;rsquo;m a process geek but even for those who are bored by process, realise that at work process will impact how you and the team work, and it will be a major influence on how effective the team is. Whilst it will differ at each company, we follow Agile, with daily stand-ups and sprints, and follow GitHub flow as part of a release train process. Process is important and getting familiar with the basics e.g. how to open a GitHub pull request will help you get up to speed.&lt;/p>
&lt;h3 id="focus-on-what-to-learn">Focus on what to learn&lt;/h3>
&lt;p>Focus on a few areas rather than trying to learn everything.&lt;/p>
&lt;p>There&amp;rsquo;s a mountain of stuff to learn so have a plan. Something that really helped me to put some structure around my learning and a path to follow was &lt;a href="https://www.freecodecamp.org" target="_blank" rel="noopener">freeCodeCamp&lt;/a>, followed by &lt;a href="https://udacity.com" target="_blank" rel="noopener">Udacity&lt;/a> Nanodegrees. You can choose a different education provider - and there&amp;rsquo;s lots of high quality out there - but following a curriculum put together by experts will help you to focus. This applies to getting your first software engineering job but I would also argue for continuing your ongoing learning.&lt;/p>
&lt;p>An additional good resource to help guide learning activities are these &lt;a href="https://github.com/kamranahmedse/developer-roadmap" target="_blank" rel="noopener">developer roadmaps&lt;/a>.&lt;/p>
&lt;h3 id="avoid-burnout">Avoid burnout&lt;/h3>
&lt;p>Software engineering is a tough job. The temptation - with me at least - was to code at work and then do coding learning projects in my spare time. Whilst I still do this to an extent, it&amp;rsquo;s also important to be able to step away and recover and recuperate.&lt;/p>
&lt;p>Have a hobby to get away from the mental stresses of coding. Spend time with your friends and family. Exercise. Seriously, do lots of exercise. This is a good read on how to &lt;a href="https://dev.to/ilonacodes/how-to-stay-fit-physically-and-mentally-and-keep-coding-5a4p" target="_blank" rel="noopener">stay fit physically and mentally&lt;/a>.&lt;/p></description></item><item><title>Pipenv and Poetry package managers</title><link>https://samatkins.co/post/pipenv-poetry/</link><pubDate>Sun, 14 Apr 2019 09:09:58 +0100</pubDate><guid>https://samatkins.co/post/pipenv-poetry/</guid><description>&lt;p>Python is a wonderful language for beginners but I remember when starting out how Pip, the defacto package manager for Python (at least at the time) didn&amp;rsquo;t seem as fully featured as others such as npm. Having to manually update &lt;code>requirements.txt&lt;/code> or remember some other command to get it updated seemed an oversight. Add to that the need for virtual environments due to the whole Python 2 vs Python 3 thing and it can hinder new programmers from getting up and running with a Python project.&lt;/p>
&lt;p>That&amp;rsquo;s why the additions of &lt;a href="https://pipenv.readthedocs.io/en/latest/" target="_blank" rel="noopener">Pipenv&lt;/a> and &lt;a href="https://poetry.eustace.io" target="_blank" rel="noopener">Poetry&lt;/a> have been welcome. I&amp;rsquo;ve now used them for some of my own learning projects and am impressed with them both. Here&amp;rsquo;s my quick overview of them.&lt;/p>
&lt;h2 id="pipenv">Pipenv&lt;/h2>
&lt;p>Brought to you by Kenneth Reitz, the same author as &lt;code>requests&lt;/code>, Pipenv combines the functionality of Pip and a tool like virtualenv. Through a few simple and intuitive commands, you can create a new virtual environment, install packages and Pipenv handles creating and managing your requirements in &lt;code>Pipfile&lt;/code> and &lt;code>Pipfile.lock&lt;/code> files.&lt;/p>
&lt;p>I use it for some scripts I&amp;rsquo;ve written and I&amp;rsquo;m a huge fan.&lt;/p>
&lt;h2 id="poetry">Poetry&lt;/h2>
&lt;p>Poetry is used to help you manage libraries you are developing. It also has a few simple and intuitive commands to manage the dependencies in your project, including one to scaffold out a directory structure with starter files for your library. However, you will still need to use something like virtualenv to manage the version of Python you are using. Poetry uses a &lt;code>pyproject.toml&lt;/code> and &lt;code>poetry.lock&lt;/code> for the dependencies. I like this file format and the real strength is the &lt;code>pyproject.toml&lt;/code> file can be used instead of &lt;code>setup.py&lt;/code> to manage the packaging of your library to PyPi - &lt;a href="https://pypi.org" target="_blank" rel="noopener">Python Package Index&lt;/a>.&lt;/p>
&lt;p>I only have the one experience of creating a package available on PyPi but found using Poetry very useful during this process.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Both are good additions to the Python ecosystem, and provide good alternatives to the Python programmer. It&amp;rsquo;s important to remember the distinction in use cases for the two. As it says in Pipenv&amp;rsquo;s documentation:&lt;/p>
&lt;blockquote>
&lt;p>There is a subtle but very important distinction to be made between applications and libraries. This is a very common source of confusion in the Python community.
Libraries provide reusable functionality to other libraries and applications (let’s use the umbrella term projects here). They are required to work alongside other libraries, all with their own set of subdependencies. They define abstract dependencies.&lt;/p>&lt;/blockquote>
&lt;p>Which should you use? To me that depends on the use case.&lt;/p>
&lt;ul>
&lt;li>Scripts: use Pipenv&lt;/li>
&lt;li>Libraries: use Poetry&lt;/li>
&lt;li>Applications: use Pipenv (or Docker)&lt;/li>
&lt;/ul>
&lt;p>In most cases when building an application as part of a learning project, using Docker makes most sense for me. I&amp;rsquo;ve had some issues with Pipenv working in Docker (some strange caching issues that I didn&amp;rsquo;t have time to get to the bottom of), so when I&amp;rsquo;m using Docker I combine it with Pip and a &lt;code>requirements.txt&lt;/code> file. It is entirely likely the Docker/Pipenv issues was something I set up incorrectly so using Pipenv and Docker might be a good combination for you.&lt;/p>
&lt;p>To conclude, in my opinion Pipenv and Poetry are great tools and welcome additions to the Python ecosystem.&lt;/p></description></item><item><title>Code Snippets in VS Code</title><link>https://samatkins.co/post/code-snippets-vscode/</link><pubDate>Sun, 17 Mar 2019 17:17:10 +0000</pubDate><guid>https://samatkins.co/post/code-snippets-vscode/</guid><description>&lt;p>Adding code snippets in your IDE are a great way of saving key strokes on code that you often use.&lt;/p>
&lt;p>In Python specific IDEs such as PyCharm, enabling docstrings is built-in and available via preferences. I prefer using VS Code as I don&amp;rsquo;t just code in Python and generally prefer it as my IDE. But you don&amp;rsquo;t need to lose out on this small piece of functionality because that&amp;rsquo;s where creating your own code snippets comes in.&lt;/p>
&lt;p>For those who don&amp;rsquo;t code in Python, and are asking what is a Python docstring? Here&amp;rsquo;s the official definition:&lt;/p>
&lt;blockquote>
&lt;p>A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition&lt;/p>&lt;/blockquote>
&lt;p>You can read more about docstrings &lt;a href="https://www.python.org/dev/peps/pep-0257/" target="_blank" rel="noopener">here&lt;/a>.&lt;/p>
&lt;p>Here&amp;rsquo;s how to set up a code snippet in VS Code, using the Google style of docstring as an example.&lt;/p>
&lt;p>In the menu bar select: Code -&amp;gt; Preferences -&amp;gt; User Snippets&lt;/p>
&lt;p>Select Python from the menu. This opens a file called &lt;code>python.json&lt;/code>&lt;/p>
&lt;p>In this file add the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;Python docstring&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;prefix&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;_docstring&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;body&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;\&amp;#34;\&amp;#34;\&amp;#34;${1:description}\n\nArgs:\n\t${2:param name} (${3:param type}): ${4:describe the param} \n\nReturns: \n\t${5:type}: ${6:description}\n\&amp;#34;\&amp;#34;\&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Python docstring - Google style&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This snippet example outputs the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;description
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Args:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> param name (param type): describe the param
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Returns:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> type: description
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>What does the syntax in the actual snippet mean?&lt;/p>
&lt;ul>
&lt;li>The key &lt;code>Python docstring&lt;/code> is the name of the snippet&lt;/li>
&lt;li>The &lt;code>prefix&lt;/code> is what you type in order to trigger the snippet&lt;/li>
&lt;li>The &lt;code>body&lt;/code> is the output of the snippet once triggered&lt;/li>
&lt;li>The description is displayed whilst typing and selecting a snippet&lt;/li>
&lt;/ul>
&lt;p>Within the body of the snippet the items with the syntax &lt;code>${1}&lt;/code> are very helpful. The cursor will tab through each of these so you can overwrite the placeholders. In the example above, first of all &amp;ldquo;description&amp;rdquo; is highlighted and when you type you can overwrite this text. Hit tab and the cursor will move to &amp;ldquo;param name&amp;rdquo;, tab again and &amp;ldquo;param type&amp;rdquo; and so on.&lt;/p>
&lt;p>I find snippets a small but powerful way of increasing productivity and reducing my frustration. I hope this helps you to add snippets in VS Code and increase your productivity too.&lt;/p>
&lt;h2 id="update">Update&lt;/h2>
&lt;p>The above example shows how to create a snippet but is actually not a good example. If you want to semi-automate creating Python docstrings in VS Code then I recommend the extension &lt;a href="https://marketplace.visualstudio.com/items?itemName=iansan5653.format-python-docstrings" target="_blank" rel="noopener">Python Docstring Formatter&lt;/a>.&lt;/p></description></item><item><title>Command Line Git Aliases</title><link>https://samatkins.co/post/command-line-git-aliases/</link><pubDate>Mon, 11 Feb 2019 19:39:18 +0000</pubDate><guid>https://samatkins.co/post/command-line-git-aliases/</guid><description>&lt;p>Working with version control is a must for good software development. I use git personally and at work. The GUI in VS Code is good for some git work e.g. viewing diffs. However, for some tasks it is fast and productive to use the command line.&lt;/p>
&lt;p>There are some commands that are very useful but are too long to type each time i.e. they are good candidates to save as an alias. For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Command to switch to master branch, fetch and pull latest from remote&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git checkout master &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> git fetch &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> git pull origin master
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Command to push all commits to remote from your local branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push origin &lt;span class="k">$(&lt;/span>git_current_branch&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Command to fetch and pull all commits from remote to local for your current branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git fetch &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> git pull origin &lt;span class="k">$(&lt;/span>git_current_branch&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Pretty prints the git log. I typically use when I have merged a feature branch to master and want to find the commit as part of then tagging the release&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git log --graph --pretty&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;\&amp;#39;&amp;#39;%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&amp;lt;%an&amp;gt;%Creset&amp;#39;&lt;/span>&lt;span class="se">\&amp;#39;&lt;/span>&lt;span class="err">&amp;#39;&lt;/span> --abbrev-commit --all
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="setting-up-aliases">Setting up aliases&lt;/h2>
&lt;p>There are several options to set these up as aliases. This is how I did it on a MacBook.&lt;/p>
&lt;h3 id="bash-profile">bash profile&lt;/h3>
&lt;p>In your &lt;code>.bash_profile&lt;/code> which is in your home directly i.e. &lt;code>~/.bash_profile&lt;/code>add the aliases.&lt;/p>
&lt;p>An example for git fetch and pull:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">ggpull&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;git fetch &amp;amp;&amp;amp; git pull origin $(git_current_branch)&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When I type the alias &lt;code>ggpull&lt;/code> in the command line, the command &lt;code>git fetch &amp;amp;&amp;amp; git pull origin $(git_current_branch)&lt;/code> will run.&lt;/p>
&lt;h3 id="zsh">zsh&lt;/h3>
&lt;p>If like me you use zsh, add the below to the to the bottom of &lt;code>.zshrc&lt;/code>. This ensures all your git alias goodness in &lt;code>.bash_profile&lt;/code> is available even if you use zsh.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> -f ~/.bash_profile &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> . ~/.bash_profile&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>My preference is to keep all my aliases in &lt;code>.bash_profile&lt;/code> for forward compatibility. If I were to stop using zsh or want to use these aliases on a Linux machine then I already have my bash profile file ready.&lt;/p>
&lt;p>I hope this is helpful in setting up some of your own aliases and saving a few keystrokes each day.&lt;/p></description></item><item><title>3 Recommended Programming Books</title><link>https://samatkins.co/post/recommended-programming-books/</link><pubDate>Wed, 23 Jan 2019 07:00:27 +0000</pubDate><guid>https://samatkins.co/post/recommended-programming-books/</guid><description>&lt;p>As I reflect on 2018, there were three programming books I enjoyed reading and learned a lot from. I thought I would share my summary of each book.&lt;/p>
&lt;h3 id="the-pragmatic-programmer-by-andrew-hunt-and-david-thomas">The Pragmatic Programmer by Andrew Hunt and David Thomas&lt;/h3>
&lt;p>Recommended to me by my boss, this is a timeless guide to programming. Programming and technology moves fast, yet despite being written in 1999 this book is still relevant today. In my opinion, the fact this is the case shows just how important the principles the book teaches are. This book really helped me with understanding programming best practices and how I can build those best practices into my daily work. It&amp;rsquo;s a reference to keep coming back to as I progress in my programming career.&lt;/p>
&lt;h3 id="coders-at-work-by-peter-seibel">Coders at Work by Peter Seibel&lt;/h3>
&lt;p>Another recommendation from my boss, this is a series of interviews with incredible programmers. It is interesting reading about the different approaches these programmers take to their work.&lt;/p>
&lt;p>Of particular interest was reading the contrasting and complementary philosophies and approaches of Brendan Eich and Douglas Crockford when discussing developing the JavaScript language.&lt;/p>
&lt;p>I also enjoyed reading about Brad Fitzpatrick and his incredible blend of programming skills and business/open source acumen with LiveJournal and Memcached as great examples of this.&lt;/p>
&lt;p>And from a broader industry perspective a very topical and important theme, reading about Fran Allen&amp;rsquo;s experience of the how the industry became male dominated and her views on the importance of increasing diversity.&lt;/p>
&lt;h3 id="the-devops-handbook-by-gene-kim-jez-humble-patrick-debois-john-willis">The DevOps Handbook by Gene Kim, Jez Humble, Patrick Debois, John Willis&lt;/h3>
&lt;p>This was probably my favourite programming book I read last year. It&amp;rsquo;s full title is The DevOps Handbook:: How to Create World-Class Agility, Reliability, and Security in Technology Organizations&lt;/p>
&lt;p>My background, prior to switching careers, was in Supply Chain and Operations (in the manufacturing sense) including lean and process optimisation work. This book was a real eye opener, taking a manufacturing operational view to building software. The book explains how a DevOps culture looking at People, Process and Tools can drastically improve a software company&amp;rsquo;s performance, in terms of speed to market and software resilience.&lt;/p>
&lt;p>The insight that infrastructure as code is actually the most important aspect to use version control (rather than as I had previously assumed for development code) was a huge point but once digested makes sense. If you have an interest in DevOps and how teams work to build software, I highly recommend this book.&lt;/p></description></item><item><title>Pytest Tricks: Freezegun and Parametrize</title><link>https://samatkins.co/post/pytest-tricks-freezegun-parametrize/</link><pubDate>Mon, 05 Nov 2018 19:38:08 +0000</pubDate><guid>https://samatkins.co/post/pytest-tricks-freezegun-parametrize/</guid><description>&lt;p>I always try to work following Test Driven Development. I recently used Pytest to write some unit tests and discovered a couple of neat tricks from a work colleague.&lt;/p>
&lt;h2 id="context">Context&lt;/h2>
&lt;p>I needed to write a function which determined if a user&amp;rsquo;s account had been created within a specified time window. The function returns a boolean i.e. true if the account was created within the time window and false otherwise.&lt;/p>
&lt;p>Here&amp;rsquo;s a function I wrote (amended to remove any work sensitive information):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datetime&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">timedelta&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TIME_WINDOW_DURATION&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">timedelta&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">minutes&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">30&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">_check_if_user_created_in_time_window&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">account_creation&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> If the user&amp;#39;s account creation time falls within this time window,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> return True
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Parameters
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> ----------
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> account_creation: timestamp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Timestamp of when user&amp;#39;s account was created
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Returns
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> -------
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> bool
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> True if tag to be applied, False otherwise
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">account_creation_datetime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_cast_datetime_string_to_datetime_type&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">account_creation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">now&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">utcnow&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_gets_tag&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">now&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">TIME_WINDOW_DURATION&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">account_creation_datetime&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">now&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">user_gets_tag&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="writing-tests">Writing tests&lt;/h2>
&lt;p>Writing tests when you have to match against a timestamp is tricky because it could create fragile tests. In other words, a test that may or may not pass, and the pass or failure does not tell you if it is the code failing or because the timestamps do not match.&lt;/p>
&lt;p>So the first tip is to use &lt;code>freezegun&lt;/code>. This allows you to effectively set the date and time when the system is under test so you can make assertions against the function.&lt;/p>
&lt;p>Here&amp;rsquo;s an example of this in practice:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@freeze_time&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;2018-09-07 16:35:00&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_user_created_in_time_window_returns_true&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">example_manager&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Example&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">account_creation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;2018-09-07 16:05:01&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_gets_tag&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">example_manager&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_check_if_user_created_in_time_window&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">account_creation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">user_gets_tag&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The freeze_time decorator sets the system under test date time as &lt;code>2018-09-07 16:35:00&lt;/code> so when we assert an account creation time of &lt;code>2018-09-07 16:05:01&lt;/code> it falls within the time window of 30 minutes i.e. evaluates to true.&lt;/p>
&lt;p>As you would expect, I wanted to make different assertions based on different frozen times and so wrote another test like the above but with a different date time passed in as the argument to the decorator. That&amp;rsquo;s all well and good as it tests the code but it goes against the DRY (don&amp;rsquo;t repeat yourself) principle.&lt;/p>
&lt;p>So here&amp;rsquo;s the second trick I learned:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@pytest.mark.parametrize&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;account_creation&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;2018-09-07 16:34:00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;2018-09-07 16:05:01&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@freeze_time&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;2018-09-07 16:35:00&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_user_created_in_time_window_returns_true&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">account_creation&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">example_manager&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Example&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_gets_tag&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">example_manager&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_check_if_user_created_in_time_window&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">account_creation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">user_gets_tag&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Pytest - per the &lt;a href="https://docs.pytest.org/en/latest/parametrize.html#more-examples" target="_blank" rel="noopener">docs&lt;/a> - &amp;ldquo;enables parametrization of arguments for a test function&amp;rdquo;. So how does this work?&lt;/p>
&lt;p>Like freeze time, you wrap the unit test with a decorator which takes two arguments. The first is a string which is the name of the argument. This should also be passed in as an argument to the test function. The second argument is a list of the parameters. In the example above, I&amp;rsquo;ve added two different date time strings as parameters. This means when the test runs, it will run twice, using the first parameter and then the second. This keeps the code DRY whilst allowing multiple assertions. What&amp;rsquo;s also neat is when you run the tests with verbosity &lt;code>pytest -vv&lt;/code> the output displays the test being run along with the parameter used. The unit test above displays:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">test_example.py::test_user_created_in_time_window_returns_true&lt;span class="o">[&lt;/span>2018-09-07 16:34:00&lt;span class="o">]&lt;/span> PASSED
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">test_example.py::test_user_created_in_time_window_returns_true&lt;span class="o">[&lt;/span>2018-09-07 16:05:01&lt;span class="o">]&lt;/span> PASSED
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Two nice tips to help write good unit tests and keep the code DRY.&lt;/p></description></item><item><title>Python Getters and Setters</title><link>https://samatkins.co/post/python-getters-setters/</link><pubDate>Mon, 08 Oct 2018 20:31:40 +0100</pubDate><guid>https://samatkins.co/post/python-getters-setters/</guid><description>&lt;p>At work recently I had to add some getters and setters to enable user&amp;rsquo;s responses to a question being persisted to the database. A few new things came up so to reinforce my learning, I decided to write this.&lt;/p>
&lt;h2 id="context">Context&lt;/h2>
&lt;p>The issue arose when trying to handle a list of the user&amp;rsquo;s answers to a question. Let&amp;rsquo;s say the question was &amp;ldquo;What sports to do you like?&amp;rdquo; and the user can answer either one or several of &amp;ldquo;football, basketball, tennis, rugby, cycling&amp;rdquo; or just &amp;ldquo;none of the above&amp;rdquo;.&lt;/p>
&lt;p>If the user were to choose football and cycling, a Python list would be part of the client request to the API.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;football&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;cycling&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Trying to persist this list to the database led to an operational error:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">OperationalError: &lt;span class="o">(&lt;/span>OperationalError&lt;span class="o">)&lt;/span> &lt;span class="o">(&lt;/span>1241, &lt;span class="s1">&amp;#39;Operand should contain 1 column(s)&amp;#39;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The reason for this error is SQL databases cannot store lists. Relational databases are designed specifically to store one value per row/column combination.&lt;/p>
&lt;p>Each value should be stored in its own database column. However due to various business and technical reasons, it was decided this was not the right option in this case.&lt;/p>
&lt;p>So this is where getters and setters come in.&lt;/p>
&lt;h2 id="what-are-getters-and-setters">What are getters and setters?&lt;/h2>
&lt;p>Getters and setters allow you to protect your data by controlling who can do what to the data. For each variable, a get method &amp;ldquo;gets&amp;rdquo; the value and the set method &amp;ldquo;sets&amp;rdquo; the value.&lt;/p>
&lt;h2 id="getters-and-setters-example">Getters and setters example&lt;/h2>
&lt;p>For my use case, the getters and setters were actually fairly straightforward. Manipulating a string to a list and back again.&lt;/p>
&lt;p>Here&amp;rsquo;s an illustrative example.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">ExampleSportsQuestion&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_sporting_interests&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">db&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Column&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;sporting_interests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">db&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">150&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@property&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">sporting_interests&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Get sporting_interests
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_sporting_interests&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_sporting_interests&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;,&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@sporting_interests.setter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">sporting_interests&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">list_sporting_interests&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Set sporting_interests
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sporting_interests_choices&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;football&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;basketball&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;tennis&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;rugby&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;cycling&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;noneOfTheAbove&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">interest&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">list_sporting_interests&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">interest&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">sporting_interests_choices&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">AttributeError&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Invalid value &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">interest&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> for &amp;#34;&lt;/span> &lt;span class="s2">&amp;#34;_sporting_interests property&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">string_sporting_interests&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;,&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">list_sporting_interests&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_sporting_interests&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">string_sporting_interests&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The getter in this example creates a list, splitting the string using a &lt;code>,&lt;/code> comma as a delimiter.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># starting point is this string which is the value persisted in the database column&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;football,cycling&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># the getter transforms it to a list&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;football&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;cycling&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If the database entry is None then defensively the getter returns an empty &lt;code>[]&lt;/code> list.&lt;/p>
&lt;p>The setter checks the user&amp;rsquo;s answers are in the available choices and if not, raises an error. If the answers are all valid, the list is converted to a string with each answer separated by a &lt;code>,&lt;/code> comma.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># the setter does the reverse, it takes the list provided by the client&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;football&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;cycling&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># and transforms to a string to be persisted in the database&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;football,cycling&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And that&amp;rsquo;s it. Along with some unit tests, this provided the solution and was ready for code review.&lt;/p>
&lt;p>It was useful to go through the process of exploring this topic, reading about relationship databases and normalisation, possible use cases for getters and setters, and how to implement Python getters and setters. Writing this up in a blogpost has helped to solidify my learning, and I look forward to reading this again sometime in the future to see how much I&amp;rsquo;ve learned since this was written and how much I agree/disagree with it 😁.&lt;/p></description></item><item><title>Converting a React App to TypeScript</title><link>https://samatkins.co/post/converting-react-app-typescript/</link><pubDate>Fri, 20 Apr 2018 00:00:00 +0000</pubDate><guid>https://samatkins.co/post/converting-react-app-typescript/</guid><description>&lt;p>&lt;img src="https://samatkins.co/img/i_heart_typescript.png" alt="“Screenshot of Typescript with the hashtag I love Typescript”">&lt;/p>
&lt;p>Learning some TypeScript has been on my to do list for sometime. I finally found some time and started by reading the docs to get familiar with it. The next step to really help learn it was to actually use it for a project so I decided to convert an existing React app to use TypeScript. This blogpost is a guide on how I did exactly that.&lt;/p>
&lt;p>First up: Wow, developing with TypeScript is amazing. Most definitely &lt;code>#iHeartTypeScript&lt;/code> !&lt;/p>
&lt;p>And now let&amp;rsquo;s get started.&lt;/p>
&lt;h2 id="first-things-first-react-app-with-typescript-configuration">First things first: React app with TypeScript configuration&lt;/h2>
&lt;p>To make things simpler I used &lt;code>create-react-app&lt;/code> with TypeScript flags in order to scaffold a React app with a TypeScript config. My thought process was I could this config in my existing React app and it abstracts away the whole Webpack configuration (another thing on my learning list by the way).&lt;/p>
&lt;p>This is the command to run to get a TypeScript React app:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npx create-react-app TypeScript-app --scripts-version&lt;span class="o">=&lt;/span>react-scripts-ts
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is basically a fork of &lt;code>create-react-app&lt;/code>: &lt;a href="https://github.com/wmonk/create-react-app-TypeScript" target="_blank" rel="noopener">https://github.com/wmonk/create-react-app-TypeScript&lt;/a>&lt;/p>
&lt;h3 id="configuration">Configuration&lt;/h3>
&lt;p>Now I had a TypeScript config, I used this in my existing React app as a first step to converting it.&lt;/p>
&lt;p>First up I created a new git branch &lt;code>git checkout -b convert-to-TypeScript&lt;/code>for my work. Now I could replace the React app config with the TypeScript config, and work through the errors until the app compiles:&lt;/p>
&lt;ul>
&lt;li>copy over all the ts files e.g. &lt;code>tsconfig.json&lt;/code>&lt;/li>
&lt;li>copy over the scripts and dependencies into &lt;code>package.json&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>I can&amp;rsquo;t live without Prettier code formatting so to get Prettier to live happily with TSLint formatting I will add the TSLint-Config-Prettier package like this&lt;code>yarn add tslint-config-prettier --dev&lt;/code>&lt;/p>
&lt;p>And add a &lt;code>.prettierrc&lt;/code> file so Prettier formatting is aligned to TypeScript linting.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="l">// .prettierrc.yaml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">parser&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">TypeScript&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">singleQuote&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">trailingComma&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">all&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">semi&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And for complete sanity, I deleted the node modules then installed all dependencies again with &lt;code>yarn install&lt;/code> to make sure everything is installed as it should be.&lt;/p>
&lt;h2 id="get-the-app-to-compile">Get the app to compile&lt;/h2>
&lt;p>With the config in place, it was time to try running the app and see what happened i.e. what errors I would get.&lt;/p>
&lt;p>So &lt;code>yarn start&lt;/code> and let the fun begin.&lt;/p>
&lt;h3 id="missing-indextsx">Missing index.tsx&lt;/h3>
&lt;pre tabindex="0">&lt;code>Failed to load tsconfig.json: Missing baseUrl in compilerOptions
Could not find a required file.
Name: index.tsx
&lt;/code>&lt;/pre>&lt;p>This was two different errors. The second about the &amp;ldquo;required file&amp;rdquo; was a simple fix. TypeScript was looking for a TypeScript file as an entry point i.e. file &lt;code>index.tsx&lt;/code>. I changed the existing entry point &lt;code>index.js&lt;/code> to &lt;code>index.tsx&lt;/code>.&lt;/p>
&lt;p>For the other error, it seems the config I copied over was not quite right so I added the missing baseurl in compiler options in &lt;code>tsconfig.json&lt;/code> like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;compilerOptions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;baseUrl&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;.&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="no-default-export">No default export&lt;/h3>
&lt;p>The error read:&lt;/p>
&lt;pre tabindex="0">&lt;code>// path shortened for brevity purposes:
(3,8): Module &amp;#39;&amp;#34;... /myreads/node_modules/@types/react/index&amp;#34;&amp;#39; has no default export.
&lt;/code>&lt;/pre>&lt;p>This was a common issue across multiple files. The fix was straightforward in terms of syntax:&lt;/p>
&lt;p>&lt;code>import React from 'react';&lt;/code> needs to be &lt;code>import * as React from 'react';&lt;/code> and then fix how the class is declared &lt;code>class BooksApp extends React.Component&lt;/code> instead of &lt;code>class BooksApp extends Component&lt;/code>.&lt;/p>
&lt;p>In addition, in the same file I had &lt;code>import ReactDOM from 'react-dom';&lt;/code> which needs to be &lt;code>import * as ReactDOM from 'react-dom';&lt;/code>&lt;/p>
&lt;p>Now the harder part. Why was this an issue for TypeScript? And how did the fix work?&lt;/p>
&lt;p>After some research, here&amp;rsquo;s what I learned. The React package (and many other packages) don&amp;rsquo;t actually have a default export. Instead they have named exports. So why doesn&amp;rsquo;t this work with TypeScript? Basically it&amp;rsquo;s a difference between how Babel and TypeScript handle this. Babel is used by &lt;code>create-react-app&lt;/code> to compile the JavaScript and it creates a synthetic default export from all of the named exports, whereas TypeScript doesn&amp;rsquo;t follow this approach.&lt;/p>
&lt;p>So in TypeScript you need to import everything and then use the named export when required. For example, to use the React Component named export you would do this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">as&lt;/span> &lt;span class="nx">React&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">class&lt;/span> &lt;span class="nx">BooksApp&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Component&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... snip
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="convert-to-types">Convert to Types&lt;/h2>
&lt;p>OK, good progress made because the app compiled but other than the config and the entry point &lt;code>index.tsx&lt;/code> there was no TypeScript in sight. Next step then was to convert the next JavaScript file to a TypeScript file and after the entry point, the first component is &lt;code>App.js&lt;/code>.&lt;/p>
&lt;p>I changed the filename &lt;code>App.js&lt;/code> to &lt;code>App.tsx&lt;/code>, restarted my development server (&lt;code>yarn start&lt;/code>) and started working on fixing the &lt;code>type&lt;/code> errors.&lt;/p>
&lt;p>A side note, as part of my development process, in &lt;code>tsconfig.json&lt;/code> I sometimes toggled the setting &lt;code>&amp;quot;noImplicitAny&amp;quot;: false,&lt;/code> between true and false. This was to allow/disallow &lt;code>any&lt;/code> types in order to check what needs type checking versus getting the App to compile. The aim is to have this set to &lt;code>true&lt;/code> for complete type checking but it&amp;rsquo;s sometimes useful to have the app compile and check how things are working before going back to check &lt;code>any&lt;/code> types.&lt;/p>
&lt;h3 id="convert-apptsx">Convert App.tsx&lt;/h3>
&lt;p>The first type error was the books array on my state object:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="nx">myreads&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">App&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tsx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Parameter&lt;/span> &lt;span class="s1">&amp;#39;book&amp;#39;&lt;/span> &lt;span class="nx">implicitly&lt;/span> &lt;span class="nx">has&lt;/span> &lt;span class="nx">an&lt;/span> &lt;span class="s1">&amp;#39;any&amp;#39;&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// code:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">books&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To fix this I added an interface to set the contract for state should look like. I set both the books and shelves array as optional i.e. &lt;code>?&lt;/code> denotes optional. The reason for making this optional is the initial state is empty until the component has mounted, i.e. the API returns data.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// file: /myreads/src/interfaces/stateProps.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">StateProps&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">books&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">object&lt;/span>&lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">shelves&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">object&lt;/span>&lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In addition, I created an interface for the book object:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// file: /myreads/src/interfaces/bookObject.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">BookObject&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">allowAnonLogging&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">authors&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">averageRating&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">canonicalVolumeLink&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">categories&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">contentVersion&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">description&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">imageLinks&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">smallThumbnail&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">thumbnail&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">industryIdentifiers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">identifier&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">identifier&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">infoLink&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">language&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">maturityRating&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">pageCount&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">panelizationSummary&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">containsEpubBubbles&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">containsImageBubbles&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">previewLink&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">printType&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">publishedDate&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">publisher&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ratingsCount&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readingModes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">text&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">image&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">shelf&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">subtitle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>and used it like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">updateBook&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">book&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">BookObject&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">shelf&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// snip
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The next compile error was this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="err">/myreads/src/App.tsx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="mi">72&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">11&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Value&lt;/span> &lt;span class="nx">must&lt;/span> &lt;span class="nx">be&lt;/span> &lt;span class="nx">set&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kr">boolean&lt;/span> &lt;span class="nx">attributes&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>&amp;lt;Route /&amp;gt;&lt;/code> component takes prop &lt;code>exact&lt;/code> to match an exact url path. Before the return statement, I declared &lt;code>exact&lt;/code> as a constant and set the type to a bool like this: &lt;code>const exact: boolean = true;&lt;/code> and then as before pass in &lt;code>exact={exact}&lt;/code> to the component.&lt;/p>
&lt;p>The next error related to my method to update books and the args passed into it. Here&amp;rsquo;s the error and how I fixed it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// error
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="err">/myreads/src/App.tsx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="mi">74&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">30&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Parameter&lt;/span> &lt;span class="s1">&amp;#39;shelf&amp;#39;&lt;/span> &lt;span class="nx">implicitly&lt;/span> &lt;span class="nx">has&lt;/span> &lt;span class="nx">an&lt;/span> &lt;span class="s1">&amp;#39;any&amp;#39;&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Update code to fix the error
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">onUpdateBook&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{(&lt;/span>&lt;span class="nx">shelf&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">book&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">BookObject&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">updateBook&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">book&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">shelf&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>💥 BOOM 💥&lt;/p>
&lt;p>App.tsx fully converted to TypeScript! A small celebration and then I continued converting other files to TypeScript.&lt;/p>
&lt;h3 id="converting-a-js-to-a-ts-file">Converting a .js to a .ts file&lt;/h3>
&lt;p>In other words, &lt;code>utils/BooksAPI.js&lt;/code> to &lt;code>utils/BooksAPI.ts&lt;/code>.&lt;/p>
&lt;p>Next up I took on a straight JavaScript to TypeScript conversion with no JSX. Converting this file involved stating the types for the args to each of the functions. I also re-used the BookObject interface:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// after
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">update&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">book&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">BookObject&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">shelf&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">update&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">book&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">BookObject&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">shelf&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">search&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">query&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">maxResults&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">number&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="string-or-array-of-string-types">String or array of string types?&lt;/h3>
&lt;p>This demonstrates the usefulness of TypeScript.&lt;/p>
&lt;p>The compile error was this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="err">/myreads/src/containers/Book/Book.tsx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="mi">15&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">31&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Parameter&lt;/span> &lt;span class="s1">&amp;#39;writers&amp;#39;&lt;/span> &lt;span class="nx">implicitly&lt;/span> &lt;span class="nx">has&lt;/span> &lt;span class="nx">an&lt;/span> &lt;span class="s1">&amp;#39;any&amp;#39;&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>OK, easy I thought and added a string type. Wrong! But the error messages are so helpful!&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">index&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">js&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="mi">2177&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="nx">myreads&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">containers&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Book&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Book&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tsx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="mi">19&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">42&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Property&lt;/span> &lt;span class="s1">&amp;#39;join&amp;#39;&lt;/span> &lt;span class="nx">does&lt;/span> &lt;span class="nx">not&lt;/span> &lt;span class="nx">exist&lt;/span> &lt;span class="nx">on&lt;/span> &lt;span class="nx">type&lt;/span> &lt;span class="s1">&amp;#39;string&amp;#39;&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So of course it&amp;rsquo;s not a string, it&amp;rsquo;s an array of string types. I corrected the code like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">formattedAuthorStr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">writers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">[])&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">writers&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">undefined&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">writers&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">writers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">writers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;, &amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="nx">writers&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="synthetic-events">Synthetic Events&lt;/h3>
&lt;p>Parameter &amp;rsquo;e&amp;rsquo; implicitly has an &amp;lsquo;any&amp;rsquo; type.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">handleMoveBook&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">e&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">selectedBook&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">props&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">book&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">selectedShelf&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onUpdateBook&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">selectedBook&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">selectedShelf&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>My solution:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">MoveBookEvent&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">target&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">handleMoveBook&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">MoveBookEvent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And then two other interfaces to manage the props in the component. The first for what is passed into to the component, and the second for managing props which are passed back up to update a book when moving it between bookshelves.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">BookProps&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">authors&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">shelf&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">BookComponentProps&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">book&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">authors&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">[],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">shelf&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">bookImgUrl&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onUpdateBook&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">book&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">BookProps&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">shelf&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// usage
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">Book&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">BookComponentProps&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="searchpage-component">SearchPage Component&lt;/h3>
&lt;p>This was an interesting compile error. This was the first Class I was dealing with and so it was a little different from some of the other stuff I&amp;rsquo;d looked at.&lt;/p>
&lt;p>The TypeScript compile error was this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="err">/myreads/src/App.tsx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="mi">76&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">15&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Property&lt;/span> &lt;span class="s1">&amp;#39;books&amp;#39;&lt;/span> &lt;span class="nx">does&lt;/span> &lt;span class="nx">not&lt;/span> &lt;span class="nx">exist&lt;/span> &lt;span class="nx">on&lt;/span> &lt;span class="nx">type&lt;/span> &lt;span class="s1">&amp;#39;IntrinsicAttributes &amp;amp;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">IntrinsicClassAttributes&amp;lt;SearchPage&amp;gt; &amp;amp; Readonly&amp;lt;{ children?: ReactNode; }&amp;gt; ...&amp;#39;&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After a bit of searching and docs reading I found a few things that helped to solve the issue.&lt;/p>
&lt;p>First up, from the TypeScript docs re generics and generic classes:&lt;/p>
&lt;blockquote>
&lt;p>A generic class has a similar shape to a generic interface. Generic classes have a generic type parameter list in angle brackets (&amp;lt;&amp;gt;) following the name of the class.&lt;/p>&lt;/blockquote>
&lt;p>Next, a React class requires a generic class in the form of &lt;code>&amp;lt;props, state&amp;gt;&lt;/code>. Within each of these generic classes, you state the types. In the end I used interfaces for props and state which I added next to the class like this &lt;code>React.Component&amp;lt;SearchPageProps, SearchPageState&amp;gt;&lt;/code>&lt;/p>
&lt;p>The full example is here:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">SearchPageProps&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">books&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">BookObject&lt;/span>&lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onUpdateBook&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">book&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">BookProps&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">shelf&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onSelectSearchPage&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">SearchPageState&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">error&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">userSearch&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">updatedSearchResults&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">class&lt;/span> &lt;span class="nx">SearchPage&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Component&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">SearchPageProps&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">SearchPageState&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// snip
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This resolved the type compile errors.&lt;/p>
&lt;h3 id="node-module-react-debounce-input-placeholder-text">Node Module React-Debounce-Input &amp;lsquo;placeholder&amp;rsquo; text&lt;/h3>
&lt;p>This one puzzled me for a few hours late one evening. In the end, I shut down my laptop and went to bed. In the morning, a possible solution came to me whilst I was having breakfast.&lt;/p>
&lt;p>Key lesson here: &lt;strong>don&amp;rsquo;t code when tired&lt;/strong>, take a break and get plenty of good sleep.&lt;/p>
&lt;p>So the error that caused me so many problems was this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">Property&lt;/span> &lt;span class="s1">&amp;#39;placeholder&amp;#39;&lt;/span> &lt;span class="nx">does&lt;/span> &lt;span class="nx">not&lt;/span> &lt;span class="nx">exist&lt;/span> &lt;span class="nx">on&lt;/span> &lt;span class="nx">type&lt;/span> &lt;span class="s1">&amp;#39;IntrinsicAttributes &amp;amp;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">IntrinsicClassAttributes&amp;lt;Component&amp;lt;ThemedOuterStyledProps&amp;lt;WithOptionalTheme...&amp;#39;&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It&amp;rsquo;s on a node module &lt;code>react-debounce-input&lt;/code> used as part of the search functionality. Here is the code as used in the &lt;code>SearchPage.tsx&lt;/code> component.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Styles&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">SearchBooksBarInput&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">minLength&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">debounceTimeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="mi">300&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onChange&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">e&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">handleUserSearch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// the line below was causing the TypeScript error
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">placeholder&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Search by title or author&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userSearch&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The solution was to add to this line &lt;code>readonly placeholder?: string | number;&lt;/code> in this file &lt;code>node_modules/react-debounce-input/src/index.d.ts&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="nx">type&lt;/span> &lt;span class="nx">DebounceInputProps&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">WrappedComponent&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">WrappedComponentProps&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">WrappedComponentProps&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">element&lt;/span>&lt;span class="o">?:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">|&lt;/span> &lt;span class="nx">string&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">|&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ComponentType&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">PropConstraints&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">WrappedComponent&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">onChange&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ChangeEventHandler&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">WrappedComponent&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">onKeyDown&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">KeyboardEventHandler&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">WrappedComponent&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">onBlur&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">FocusEventHandler&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">WrappedComponent&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">string&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// new type added on the line below
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">placeholder&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">string&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">minLength&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">debounceTimeout&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">forceNotifyByEnter&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">forceNotifyOnBlur&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readonly&lt;/span> &lt;span class="nx">inputRef&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Ref&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">WrappedComponent&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This fixed the TypeScript compile error because previously TypeScript was looking for the property &lt;code>placeholder&lt;/code> on the object and couldn&amp;rsquo;t find it.&lt;/p>
&lt;p>Having fixed it in the dependency, I wasn&amp;rsquo;t then sure how to keep my changes. I found a good answer on Stack Overflow:&lt;/p>
&lt;blockquote>
&lt;ol>
&lt;li>Send a PR to the actual npm package, if the change is like a bug fix or enhancement that aligns with the actual packages goal.&lt;/li>
&lt;li>Fork the package repo, and make changes and use it in your project as a dependency, in case you are adding changes that does not align with the goals of the actual package&lt;/li>
&lt;li>Move the package code into your source code, and use it as source code rather than a package from npm&lt;/li>
&lt;/ol>&lt;/blockquote>
&lt;p>I decided to try out option 1 first and see what happened. I opened a PR to the repo&amp;hellip; and it was quickly accepted and merged.&lt;/p>
&lt;p>&lt;img src="https://samatkins.co/img/pr_ts_debounce.png" alt="“Screenshot of merged Pull Request”">&lt;/p>
&lt;p>By the way, that was my first code contribution to an open source project. 🏆&lt;/p>
&lt;h2 id="other-stuff">Other stuff&lt;/h2>
&lt;h3 id="using-consolelog">Using console.log&lt;/h3>
&lt;p>As part of my development process, I wanted to use &lt;code>console.log()&lt;/code> but this causes TypeScript to fail to compile due to a TSLint error. Rather than change the config and then potentially make git commits with console logs still in the code, I opted for this instead to temporarily disable TSLint:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* tslint:disable */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* tslint:enable */&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="vs-code-config">VS Code config&lt;/h3>
&lt;p>Finally, some thoughts on using VS Code with TypeScript.&lt;/p>
&lt;p>One recommended config change is to set this setting to true in your user settings:&lt;/p>
&lt;p>&lt;code>&amp;quot;TypeScript.implementationsCodeLens.enabled&amp;quot;: true,&lt;/code>&lt;/p>
&lt;p>This means you&amp;rsquo;ll see the number of implementation of your interfaces in your code e.g.&lt;/p>
&lt;p>&lt;img src="https://samatkins.co/img/vs_code_ide_example1.png" alt="“VS Code and TypeScript integration example 1”">&lt;/p>
&lt;p>&lt;img src="https://samatkins.co/img/vs_code_ide_example2.png" alt="“VS Code and TypeScript integration example 2”">&lt;/p>
&lt;p>TypeScript and VS Code really play well together, as you would expect, I mean VS Code is written in TypeScript. VS Code becomes even more IDE like and it&amp;rsquo;s really productive to work with TypeScript in VS Code. I&amp;rsquo;m a huge fan of both.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>To conclude, using TypeScript is a fantastic development experience and it achieves this by:&lt;/p>
&lt;ul>
&lt;li>boosting my developer productivity, and&lt;/li>
&lt;li>improving even further VS Code&amp;rsquo;s capabilities&lt;/li>
&lt;/ul>
&lt;p>It was good fun converting my React app to using TypeScript. When I find time I will convert some other React apps to use TypeScript.&lt;/p>
&lt;p>Next up, convincing the team at work to use TypeScript! 😏&lt;/p></description></item><item><title>Udacity React NanoDegree reflections</title><link>https://samatkins.co/post/react-nanodegree-reflections/</link><pubDate>Fri, 23 Mar 2018 00:00:00 +0000</pubDate><guid>https://samatkins.co/post/react-nanodegree-reflections/</guid><description>&lt;p>&lt;img src="https://samatkins.co/img/react-nanodegree.png" alt="“React JS Nanodegree”">&lt;/p>
&lt;p>Image: &lt;a href="https://eu.udacity.com/course/react-nanodegree--nd019" target="_blank" rel="noopener">React.js Nanodegree&lt;/a>&lt;/p>
&lt;p>I completed my third Udacity Nanodegree in January 2018. This time the subject was React and React Native. It was a very rewarding yet demanding course. I&amp;rsquo;m glad I did it, learned a lot from completing it, but am glad it&amp;rsquo;s over - I get my evenings and weekends back.&lt;/p>
&lt;p>I find it helpful to reflect on what I learned, what worked well and what didn&amp;rsquo;t, to help with my learning in the future. So here are some key reflections on the Nanodegree.&lt;/p>
&lt;h2 id="match-the-tool-to-a-need">Match the tool to a need&lt;/h2>
&lt;p>The first project was a Book reading app called &lt;a href="https://github.com/sam-atkins/reactnd-myreads" target="_blank" rel="noopener">MyReads&lt;/a>. The lessons and project deliberately specified using only React to manage the app&amp;rsquo;s state. That&amp;rsquo;s right: no Redux. 🙈&lt;/p>
&lt;p>Why? To make you understand why you should use Redux, and why you shouldn&amp;rsquo;t. In other words, when is a tool useful, what purpose does it serve? And will my use case be met by using this tool?&lt;/p>
&lt;p>So the first project involved passing state down from component to component aka &amp;ldquo;prop drilling&amp;rdquo;. For the most part, this wasn&amp;rsquo;t an issue, with a couple of exceptions which were a bit painful. It also becomes clear if the app grew in complexity, then something like Redux would be a useful addition to the project&amp;rsquo;s dependencies.&lt;/p>
&lt;p>The second project was &lt;a href="https://github.com/sam-atkins/readable" target="_blank" rel="noopener">Readable&lt;/a>, a reddit type clone, built with React and Redux. This was a more complex app and the project reinforced the message in the lessons on the benefits that Redux would bring to the app.&lt;/p>
&lt;p>Over the course of these two projects, the point was made about the benefits (and downsides) to using Redux. Applied more generally to programming, I think this is a useful lesson to make sure it is clear what the requirement is and then to search for the right way to fulfil it.&lt;/p>
&lt;p>An important lesson: Learn to know when to use a tool and when not. If it doesn&amp;rsquo;t fit your use case, then don&amp;rsquo;t use it.&lt;/p>
&lt;h2 id="debugging">Debugging&lt;/h2>
&lt;p>I was reading an old blogpost of mine and I read this statement:&lt;/p>
&lt;blockquote>
&lt;p>the power of using &lt;code>console.log()&lt;/code>&lt;/p>&lt;/blockquote>
&lt;p>Debugging is a skill that I still need to improve on. Bug fixes at work are a necessary part of the job and it&amp;rsquo;s a great feeling when you find and fix a bug.&lt;/p>
&lt;p>The React Nanodegree helped me to learn some far more powerful ways of debugging when developing:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Using the debugger in Virtual Studio Code or Chrome devtools to add breaks in order to step through the code. Understanding the value of a variable at a certain point in time, seeing how and when different functions get called and with what values, is really powerful and an order of magnitude more powerful than the plain old &lt;code>console.log()&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Using Redux devtools and Redux logger to see exactly what is going on with the state of the app, when actions and reducers are getting called, and how state is being updated (but not mutated of course)&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="rubber-ducking">Rubber ducking&lt;/h2>
&lt;p>The Nanodegree provides you with a mentor who is available for weekly check ins, and also ad-hoc questions in between. I found it very helpful to have a mentor. Most of the time I found my interactions with my mentor were rubber ducking.&lt;/p>
&lt;p>Rubber duck debugging or rubber ducking is a phrase based on a story in the excellent book The Pragmatic Programmer in which a programmer would carry around a rubber duck and debug their code by forcing themselves to explain it, line-by-line, to the duck.&lt;/p>
&lt;p>This was a powerful technique for me and really helped on numerous occasions. The process of explaining the issue helped me to understand it better and usually as a result of this I found a solution presented itself.&lt;/p>
&lt;p>In addition to that I should add my mentor was extremely knowledgeable and helpful, as well as a good rubber duck listener.&lt;/p>
&lt;h2 id="learning-style-and-approach">Learning style and approach&lt;/h2>
&lt;p>I like the combination of video explanations, complimented with text based learning, solidified by implementing what I&amp;rsquo;ve learned during the course lessons. The mentor plus code reviews make this broadly similar in some respects to working professionally as a web developer. I learned a lot and really enjoyed the course.&lt;/p>
&lt;p>The downside to the course is it was very time intensive. Udacity are very upfront about the time requirements but with a full time job, and a family life outside of work, the need to study for about 10 hours per week is a large commitment. Now I&amp;rsquo;ve completed the course, I can say it was a worthwhile investment, however I will look to study in smaller, bite size chunks for the foreseeable future. 30 minutes a day reading a technical book, or practising with some new code, or building a new little project piece by piece is more manageable and over time should still prove effective.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>All in all, an excellent course and it has helped to improve my React and React Native skills considerably. In addition, I learned some important broader aspects to being a software engineer which I can apply regardless of the language / library.&lt;/p></description></item><item><title>Setting up ESLint and Prettier</title><link>https://samatkins.co/post/setting-up-eslint-and-prettier/</link><pubDate>Sat, 10 Feb 2018 00:00:00 +0000</pubDate><guid>https://samatkins.co/post/setting-up-eslint-and-prettier/</guid><description>&lt;h2 id="set-up-eslint-and-prettier">Set-up ESLint and Prettier&lt;/h2>
&lt;p>I&amp;rsquo;m a big fan of linting and I love the configurability of ESLint with the auto formatting capabilities of Prettier. It&amp;rsquo;s been a revelation. Learning best practices in terms of ESLint rules and formatting from Prettier, plus no more bikeshedding at work in pull requests on coding style. Let the machine take care of it for you.&lt;/p>
&lt;p>Each time I set-up a new JavaScript project, I set-up this configuration in my editor of choice &lt;a href="https://code.visualstudio.com/" target="_blank" rel="noopener">Visual Studio Code&lt;/a>, so this blogpost is my step by step guide to remind myself how to implement ESLint and Prettier to work in VS Code.&lt;/p>
&lt;h3 id="specifics">Specifics&lt;/h3>
&lt;p>Before diving into the detail, a few points:&lt;/p>
&lt;ul>
&lt;li>I install the config for each project locally as I prefer that level of control to fine tune for a project&amp;rsquo;s specific needs.&lt;/li>
&lt;li>&lt;a href="https://yarnpkg.com/en/" target="_blank" rel="noopener">Yarn&lt;/a> is used as the package manager but swop out the equivalent &lt;code>npm&lt;/code> commands if preferred.&lt;/li>
&lt;li>As mentioned above, the final step includes VS Code configuration but check Prettier&amp;rsquo;s docs for set-up with different editors.&lt;/li>
&lt;/ul>
&lt;h3 id="install-eslint">Install ESLint&lt;/h3>
&lt;p>Step one is to install ESLint with the AirBnB style guide config. In order to do this, follow the instructions from &lt;a href="https://www.npmjs.com/package/eslint-config-airbnb" target="_blank" rel="noopener">AirBnB&lt;/a>:&lt;/p>
&lt;blockquote>
&lt;p>If you use yarn, run npm info &amp;ldquo;eslint-config-airbnb@latest&amp;rdquo; peerDependencies to list the peer dependencies and versions, then run yarn add &amp;ndash;dev &lt;dependency>@&lt;version> for each listed peer dependency.&lt;/p>&lt;/blockquote>
&lt;p>For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">➜ ~ npm info &lt;span class="s2">&amp;#34;eslint-config-airbnb@latest&amp;#34;&lt;/span> peerDependencies
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">{&lt;/span> eslint: &lt;span class="s1">&amp;#39;^4.9.0&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;eslint-plugin-import&amp;#39;&lt;/span>: &lt;span class="s1">&amp;#39;^2.7.0&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;eslint-plugin-jsx-a11y&amp;#39;&lt;/span>: &lt;span class="s1">&amp;#39;^6.0.2&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;eslint-plugin-react&amp;#39;&lt;/span>: &lt;span class="s1">&amp;#39;^7.4.0&amp;#39;&lt;/span> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Based on this info, the install command would look like.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">yarn add --dev eslint babel-eslint eslint-plugin-import@^2.7.0 eslint-plugin-jsx-a11y@^6.0.2 eslint-plugin-react@^7.4.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You&amp;rsquo;ll notice the install of &lt;code>eslint&lt;/code> and &lt;code>babel-eslint&lt;/code> in addition to the AirBnB config.&lt;/p>
&lt;p>Once complete, add a &lt;code>.eslintrc.yml&lt;/code> to the root of your project and add the config from the &lt;a href="https://github.com/sam-atkins/repo-dotfiles/blob/master/.eslintrc.yml" target="_blank" rel="noopener">dotfiles template repo&lt;/a>.&lt;/p>
&lt;h3 id="install-prettier">Install Prettier&lt;/h3>
&lt;p>Next up, install Prettier and some plugs so it all plays nicely together:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">yarn add prettier --dev --exact
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yarn add --dev prettier eslint-plugin-prettier eslint-config-prettier eslint-config-airbnb
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is important because both ESLint and Prettier will try to format code which we don&amp;rsquo;t want. Instead we want Prettier to format the code based on the ESLint rules defined in the &lt;code>.eslintrc.yml&lt;/code> file.&lt;/p>
&lt;h3 id="vs-code-extensions-and-settings">VS Code extensions and settings&lt;/h3>
&lt;p>The final step is to configure the editor so it plays nicely with the project&amp;rsquo;s ESLint and Prettier config.&lt;/p>
&lt;p>These steps are specific for VS Code. First, install two extensions:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" target="_blank" rel="noopener">ESLint&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" target="_blank" rel="noopener">Prettier - Code formatter&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Second, add these user settings (CMD + , to open user settings):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// My personal preference; set for JS only to avoid formatting other file types
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="s2">&amp;#34;[javascript]&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;editor.formatOnSave&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// This stops VS Code from trying to autoformat code
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="s2">&amp;#34;javascript.format.enable&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// This one is obvious, right?
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="s2">&amp;#34;prettier.eslintIntegration&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That&amp;rsquo;s it. Your project is ready to be linted and formatted.&lt;/p></description></item><item><title>Becoming a professional web developer</title><link>https://samatkins.co/post/becoming-a-pro-web-dev/</link><pubDate>Wed, 08 Nov 2017 00:00:00 +0000</pubDate><guid>https://samatkins.co/post/becoming-a-pro-web-dev/</guid><description>&lt;p>&lt;img src="https://samatkins.co/img/mountain.jpg" alt="“Photo of a mountain”">&lt;/p>
&lt;p>Photo by &lt;a href="https://unsplash.com/@rohittandon" target="_blank" rel="noopener">Rohit Tandon&lt;/a> on Unsplash&lt;/p>
&lt;p>A few years ago I decided to transition career and become a professional web developer. Married, with a young son, and a full time job as a management consultant (i.e. long hours and lots of travel) this was not an easy undertaking. Yet - 🎉 👍 - as I write this now I have worked as a professional full stack engineer for six months.&lt;/p>
&lt;p>This blogpost is the advice and tips I wish I’d had at the beginning of my learning journey, and I hope some of these prove useful to you too.&lt;/p>
&lt;h2 id="know-your-goal-and-be-focussed-on-what-you-learn-and-when">Know your goal and be focussed on what you learn and when&lt;/h2>
&lt;p>I&amp;rsquo;m curious in nature and always like to play with shiny new things. Whilst this has advantages, there are clearly disadvantages such as getting distracted and not working on moving closer to your actual goal. Early on when learning I would often dive off into a rabbit hole learning something which whilst perhaps useful didn&amp;rsquo;t directly get me to my goal: becoming a professional web developer.&lt;/p>
&lt;p>My solution was to develop a flexible framework with specific goals and objectives to reach my goal. Completing the Intro to Programming Udacity Nanodegree was an early objective. Anything that wasn&amp;rsquo;t directly related to this I questioned, asking myself &amp;ldquo;what will I gain my doing this?&amp;rdquo;. It stopped me on several occasions from wasting time on unnecessary diversions. Sometimes though I would allow myself to learn something which was useful, fun (it should always be fun) and would indirectly benefit me to reaching my goal. For example, I learned some Gulp which wasn&amp;rsquo;t part of any qualification but it taught me about web development processes and helped improve my productivity, automating some key processes.&lt;/p>
&lt;p>So if you&amp;rsquo;re like me, then pick what you learn and work on carefully and make sure it&amp;rsquo;s moving you forwards.&lt;/p>
&lt;h2 id="find-your-learning-style">Find your learning style&lt;/h2>
&lt;p>Related to knowing your goal, find out how you learn best and put some structure around your learning in order to reach your goal.&lt;/p>
&lt;p>Let&amp;rsquo;s unpack that statement.&lt;/p>
&lt;h3 id="curriculum">Curriculum&lt;/h3>
&lt;p>What I mean by this is putting some structure around what you learn. I credit &lt;a href="https://www.freecodecamp.org/" target="_blank" rel="noopener">freeCodeCamp&lt;/a> with providing initial structure for me to learn some of the basics of web development and most importantly at the time that I really enjoyed it and wanted to pursue it as a career.&lt;/p>
&lt;p>At Udacity I studied two Nanodegrees, first the &lt;a href="https://www.udacity.com/course/intro-to-programming-nanodegree--nd000" target="_blank" rel="noopener">Introduction to Programming&lt;/a> to build on the basics I learned from freeCodeCamp. Next I studied the &lt;a href="https://www.udacity.com/course/full-stack-web-developer-nanodegree--nd004" target="_blank" rel="noopener">Full Stack Nanodegree&lt;/a> and I credit this fantastic course as being instrumental in successfully transitioning career to become a professional web developer.&lt;/p>
&lt;p>In fact, I&amp;rsquo;m such a fan of Udacity that at the time of writing I&amp;rsquo;m studying my third Nanodegree on React, Redux and React Native, but more on this another time.&lt;/p>
&lt;h3 id="mentor">Mentor&lt;/h3>
&lt;p>I need community and people to turn to help guide me in the right direction. Learning to code is hard, but with the right support you can learn. I will always appreciate freeCodeCamp because it helped me to discover coding but I found the self directed nature of freeCodeCamp wasn&amp;rsquo;t for me. Udacity on the other hand with mentor support and code reviews was a much better fit. A mentor/coach system worked wonders for me. So find what works for you and go with that.&lt;/p>
&lt;h3 id="learn-by-doing">Learn by doing&lt;/h3>
&lt;p>I’m no good just working on learning algorithms just for the sake of learning an algorithm. I get they are important, and learning some is very important. But just trying to learn them on their own I quickly find boring. Only doing Codewars isn’t for me. I learn best my doing. Build something and work out along the way the best way to do this. Sure this means knowing your &lt;code>map&lt;/code>from your &lt;code>reduce&lt;/code> from your &lt;code>filter&lt;/code> but use them whilst you&amp;rsquo;re building a project.&lt;/p>
&lt;h2 id="build-stuff">Build stuff&lt;/h2>
&lt;blockquote>
&lt;p>The best way to learn to code is to code.&lt;/p>&lt;/blockquote>
&lt;p>I don&amp;rsquo;t know who this quote can be attributed to but I wholeheartedly agree with it. It builds on my point above to learn by doing.&lt;/p>
&lt;p>Once you&amp;rsquo;ve got some of the basics of HTML, CSS and some JavaScript, then start to build things. For me it is undoubtedly the best way to learn. I built lots of project apps, initially as part of following freeCodeCamp, then as part of my Udacity Full Stack Nanodegree, and then also some others independently.&lt;/p>
&lt;p>As you progress, complement this with learning more about your language of choice. For example, one of my favourite parts of Wes Bos&amp;rsquo; excellent &lt;a href="https://javascript30.com/" target="_blank" rel="noopener">JavaScript 30&lt;/a> course is array cardio - learning to work with array methods.&lt;/p>
&lt;h2 id="use-abstractions-to-your-advantage">Use abstractions to your advantage&lt;/h2>
&lt;p>There&amp;rsquo;s an absolute ton of stuff to learn. It&amp;rsquo;s impossible to learn it all. So lean on others for some of the heavy lifting. A great example of this is I started learning React a few months ago. Using &lt;a href="https://github.com/facebookincubator/create-react-app" target="_blank" rel="noopener">create-react-app&lt;/a> to create a React app with no build configuration (e.g. not having to worry about WebPack) is awesome. Just run &lt;code>create-react-app my-app&lt;/code> and then get coding in React.&lt;/p>
&lt;p>In other words, be smart about what you learn. I want to learn about WebPack but for me personal there&amp;rsquo;s more benefit to learning React and React Native first.&lt;/p>
&lt;h2 id="be-professional">Be professional&lt;/h2>
&lt;p>I had no complete idea of what it meant to be a professional web developer and six months in I&amp;rsquo;m still learning huge amounts everyday on what it is and what it means. But you can make some assumptions based on blogposts and articles and build that into your learning.&lt;/p>
&lt;p>Having technical expertise and writing good code is just one part of it. Writing good documentation, using git and a service such as GitHub, working proficiently with your editor/IDE of choice in a local environment are just some of the parts of being an engineer. This means during your own studying, you could for example:&lt;/p>
&lt;ul>
&lt;li>Write great documentation in your project READMEs&lt;/li>
&lt;li>Use git and Github (or Gitlab, Bitbucket if you prefer). By the way, if you find git on the command line intimidating, then use a GUI such as &lt;a href="https://www.gitkraken.com/" target="_blank" rel="noopener">GitKraken&lt;/a> or &lt;a href="https://www.sourcetreeapp.com/" target="_blank" rel="noopener">SourceTree&lt;/a>. Just start using git as soon as possible!&lt;/li>
&lt;li>Develop locally rather than an online service such as Codepen. There is nothing wrong with Codepen and there&amp;rsquo;s some fantastic stuff in there. It&amp;rsquo;s also a great way to abstract away alot of the set-up. At the same time I quickly became frustrated with using it as my develop environment and so&amp;hellip;&lt;/li>
&lt;li>Pick an Editor/IDE e.g. Sublime, Atom, VS Code etc and configure it to your liking and master it&amp;rsquo;s functionality, key bindings etc. I much prefer using my editor because I can set it up how I like and work efficiently.&lt;/li>
&lt;li>Use a linter e.g. flake8 for Python, ESLint for Javascript&lt;/li>
&lt;/ul>
&lt;h2 id="market-yourself">Market yourself&lt;/h2>
&lt;p>Showcase your ability, commitment and ability to learn. I have learned a lot since I started coding, and have learned even more in six months working as a web developer. My knowledge feels like a drop in ocean compared to my more experienced colleagues. But I think one of the reasons I was hired was my desire and ability to learn. Just writing this on a CV or saying this in an interview won&amp;rsquo;t get you very far, you need evidence to back it up. And this is where Github (or similar service ) comes in to play. It&amp;rsquo;s a marketplace for you, your projects, your code, your documentation and your desire to learn.&lt;/p>
&lt;p>So start using git today already!&lt;/p>
&lt;h2 id="join-a-community">Join a community&lt;/h2>
&lt;p>Learning on your own can be tough. Seek out community for people also learning to code. This could be with the organisation you&amp;rsquo;re learning with e.g. freeCodeCamp or Udacity. Or if you&amp;rsquo;re following your own path, or to complement them, definitely check out &lt;a href="https://www.codenewbie.org/" target="_blank" rel="noopener">CodeNewbie&lt;/a> and the &lt;a href="https://github.com/Kallaway/100-days-of-code" target="_blank" rel="noopener">100daysOfCode&lt;/a> hashtag on Twitter organised by &lt;a href="https://twitter.com/ka11away" target="_blank" rel="noopener">@ka11away&lt;/a>. Both communities are superb and very supportive.&lt;/p>
&lt;h2 id="immerse-yourself">Immerse yourself&lt;/h2>
&lt;p>Get familiar with terminology and what developers talk about, as well as emerging trends and technologies. I find Podcasts are a great way of doing this. I listen to podcasts on my commute to work and it&amp;rsquo;s a good way of relaxing but still learning. There&amp;rsquo;s lots of great podcasts out there but some personal favourites are:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://syntax.fm/" target="_blank" rel="noopener">Syntax Podcast&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://changelog.com/podcast" target="_blank" rel="noopener">The Changelog&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://changelog.com/jsparty" target="_blank" rel="noopener">JS Party&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Reading news, articles, blogposts and email newsletters are further good sources of information to help keep abreast of latest developments. Some recommendations include:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://medium.freecodecamp.org/" target="_blank" rel="noopener">freeCodeCamp Medium publication&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://hackernoon.com/" target="_blank" rel="noopener">Hacker Noon&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/" target="_blank" rel="noopener">Hacker News&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="what-are-you-afraid-of">What are you afraid of?&lt;/h2>
&lt;blockquote>
&lt;p>Best advice I've heard and followed re my career: “What would you do if you weren't afraid?&lt;/p>&lt;/blockquote>
&lt;p>My motivating force was thinking &amp;ldquo;where do I want to be in 5 years time?&amp;rdquo;. I was nervous about changing careers but the fear of being stuck in the same job in 5 years time was a far bigger fear. In other words, doing nothing was a bigger risk than attempting to change careers. I set a plan, had some contingency and fall back options and went for it.&lt;/p>
&lt;p>Think what your motivating force is and use that to remind yourself of why you are doing this. I kept a log of my achievements, however minor or trivial, so in moments of despair I could look at it and remind myself of how much I had already learned, and the progress I was making to achieve my goal.&lt;/p>
&lt;p>So make a plan, get learning and I look forward to welcoming you in the future as a fellow professional software engineer.&lt;/p></description></item><item><title>100 Days of Code Challenge Summary</title><link>https://samatkins.co/post/hundred-days-of-code-challenge-summary/</link><pubDate>Mon, 28 Aug 2017 08:15:16 +0000</pubDate><guid>https://samatkins.co/post/hundred-days-of-code-challenge-summary/</guid><description>&lt;p>&lt;img src="https://samatkins.co/img/code.jpg" alt="“Image of code on a computer screen”">
&lt;a href="https://unsplash.com/@_imkiran" target="_blank" rel="noopener">Photo by Sai Kiran Anagani on Unsplash&lt;/a>&lt;/p>
&lt;p>Earlier this year I completed the 100 days of code challenge. A few Twitterers suggested I write a summary of my 100 day coding challenge experience. Better late than never, here is my summary of the challenge which capped off months of previous hard work, to the extent that I am immensely proud of what I achieved over the 100 day challenge and the first quarter of 2017.&lt;/p>
&lt;p>To summarise, my key achievements and projects were:&lt;/p>
&lt;h2 id="key-achievements">Key Achievements&lt;/h2>
&lt;ul>
&lt;li>Udacity Full-Stack Nanodegree graduate.&lt;/li>
&lt;li>Built a portfolio with several projects (listed below).&lt;/li>
&lt;li>And saving the best until last, I managed to transition my career into professional web development by landing a fantastic role at as a Junior Full Stack Web Developer at &lt;a href="https://www.bighealth.com/" target="_blank" rel="noopener">Big Health&lt;/a>.&lt;/li>
&lt;/ul>
&lt;h2 id="main-projects">Main Projects&lt;/h2>
&lt;h3 id="udacity">Udacity:&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://samatkins.co/post/building-map-project/">Neighbourhood Map (Knockout.js)&lt;/a>&lt;/li>
&lt;li>Catalogue app (Python, Flask)&lt;/li>
&lt;li>Multi-user blog (Python)&lt;/li>
&lt;li>Portfolio site (HTML, JavaScript)&lt;/li>
&lt;/ul>
&lt;h3 id="self-learning">Self-learning:&lt;/h3>
&lt;ul>
&lt;li>Pomodoro clock (React.js)&lt;/li>
&lt;li>Markdown previewer (React.js)&lt;/li>
&lt;/ul>
&lt;h2 id="what-i-learned">What I learned&lt;/h2>
&lt;h3 id="forming-a-habit">Forming a habit&lt;/h3>
&lt;p>The habit of coding everyday is invaluable. It helps with learning. It also helped me to realise that coding everyday was a joy and not a chore.&lt;/p>
&lt;h3 id="workflow">Workflow&lt;/h3>
&lt;p>Balancing work, life and studying (including the 100 day challenge) is hard. I wanted to make sure that whenever I was coding, I was efficient and actually coding. So taking the time to learn about workflow and how to make it more efficient is very worthwhile. Learning git and Gulp, setting-up an editor and other such things made me far more productive.&lt;/p>
&lt;h3 id="read-the-docs">Read the docs&lt;/h3>
&lt;p>I remember the Catalogue Item project from my Nanodegree as the first real time that I was able to pretty much refer to official documentation to help build functionality for my app. Getting to grips with technical documentation and learning how to apply it to your own code was a big moment for me.&lt;/p>
&lt;h3 id="be-professional">Be professional&lt;/h3>
&lt;p>If you are like me and want to change career, then work as professionally as you can immediately. I soaked up lots of the Udacity free video courses to try and work on hygiene factors e.g. using git for version control, learning about and implementing good documentation practices (e.g. always have a README, and be thorough in what you document), using Gulp to automate my workflow etc. Read up on what professional web developers actual do and then attempt to replicate that for your coding. This is a key reason I originally switched from using Codepen (as suggested for a lot of the FreeCodeCamp exercises and projects) to Sublime Text, and git and GitHub.&lt;/p>
&lt;h2 id="the-hard-work-starts-now">The hard work starts now&lt;/h2>
&lt;p>The 100 day challenge taught me a lot. I used it as an opportunity to learn and help drive my focus to transitioning my career into becoming a professional web developer. Having completed the challenge and changed career, I now the hard work over my entire career starts now, and doesn&amp;rsquo;t end. I&amp;rsquo;ll blog more on making the transition and starting out as a junior developer in future posts.&lt;/p></description></item><item><title>Neighbourhood Map Project</title><link>https://samatkins.co/post/building-map-project/</link><pubDate>Sat, 18 Feb 2017 10:15:16 +0000</pubDate><guid>https://samatkins.co/post/building-map-project/</guid><description>&lt;p>&lt;img src="https://samatkins.co/img/map_medium.png" alt="“An image of a Google map integrated with my app Neighbourhood Map”">&lt;/p>
&lt;p>This post relates to a front-end JavaScript and Knockout.js MVVM project, using APIs from Google Maps and Foursquare. The GitHub repo is &lt;a href="https://github.com/sam-atkins/fsnd-neighbourhood-map" target="_blank" rel="noopener">here&lt;/a>, and a demo is live on &lt;a href="http://cubiio-map.surge.sh/" target="_blank" rel="noopener">surge.sh&lt;/a>.&lt;/p>
&lt;h2 id="context">Context&lt;/h2>
&lt;p>I’m writing about my Neighbourhood Map project for one main reason. It has been a really hard project, and the best way to learn about it is to write about it.&lt;/p>
&lt;p>Before I dive into the detail, this blog post is primarily aimed at present and future me. As I mentioned above, it is to help with my learning but also for me to look back on and see how I worked through this project.&lt;/p>
&lt;p>If anyone else does read this post and notices any technical JavaScript errors or areas I could improve upon, please let me know. I am passionate about learning and would love to hear from you.&lt;/p>
&lt;h2 id="design">Design&lt;/h2>
&lt;p>The key part of this project is to use the Model View ViewModel (MVVM) design paradigm. Part of the rubric stipulated using &lt;a href="http://knockoutjs.com/" target="_blank" rel="noopener">Knockout.js&lt;/a>, a JavaScript organisational library.&lt;/p>
&lt;p>First I had to get my head around how the architecture of the app, using the MVVM paradigm. The rubric from Udacity for the project stated:&lt;/p>
&lt;blockquote>
&lt;p>Code is properly separated based upon Knockout best practices (follow an MVVM pattern, avoid updating the DOM manually with jQuery or JS, use observables rather than forcing refreshes manually, etc). &lt;strong>Knockout should not be used to handle the Google Map API.&lt;/strong>&lt;/p>&lt;/blockquote>
&lt;p>My emphasis on the final sentence. This is the part that threw me, although I interpreted it too literally. Knockout should be used to manage the View (UI) and connect to the Model via the ViewModel, but Google Maps API should be used to handle the actual aspects of the map.&lt;/p>
&lt;p>My 1:1 session with the Udacity coach helped to put things in perspective, and at this stage, whilst planning is important, over analysis and designing too complex an architecture (for my skill level) is not good. Better to plan, build, get things working, then refactor later as appropriate.&lt;/p>
&lt;p>I followed the MVVM design based on the lessons and the mini project (cat clicker) to get up and running.&lt;/p>
&lt;p>Then I was into the challenge full on.&lt;/p>
&lt;h2 id="problems-and-solutions">Problems and solutions&lt;/h2>
&lt;p>This is not an exhaustive list but pulls out some of the key problems I faced and how I solved them.&lt;/p>
&lt;h3 id="managing-foursquare-json-responses">Managing Foursquare JSON responses&lt;/h3>
&lt;p>Resolving the issue where Foursquare does not always have the info for each venue e.g. one venue has a description and a price range, another venue does not. This caused the infowindows to throw errors and not open.&lt;/p>
&lt;p>&lt;strong>Solution:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>A forum post recommended using ‘hasOwnProperty` - &lt;a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty" target="_blank" rel="noopener">MDN: hasOwnProperty&lt;/a>&lt;/li>
&lt;li>For example:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">venues&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hasOwnProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="err">“&lt;/span>&lt;span class="nx">venues&lt;/span>&lt;span class="err">”&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">venues&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="err">“”&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">venues&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="err">“”&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// do something
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// do something else
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="allowing-a-user-to-filter-an-array">Allowing a user to filter an array&lt;/h3>
&lt;p>Ideas and inspiration on how to solve this came from a few sources.&lt;/p>
&lt;p>First of all, a blog post by Ryan Niemeyer on &lt;a href="http://www.knockmeout.net/2011/04/utility-functions-in-knockoutjs.html" target="_blank" rel="noopener">Utility functions in KnockoutJS&lt;/a>. This in particular was the idea behind options to filter an array, combined with &lt;code>indexOf&lt;/code> (see this from &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf" target="_blank" rel="noopener">MDN re Array.prototype.indexOf()&lt;/a>).&lt;/p>
&lt;p>Secondly, as the first options were not providing a completed solution, in the end, via one of the forum mentors, this link to a &lt;a href="http://codepen.io/prather-mcs/pen/KpjbNN/left" target="_blank" rel="noopener">KO Maps example&lt;/a> provided the inspiration I needed.&lt;/p>
&lt;p>This gave me ideas on how to solve not just this challenge but also creating a favourites list. To fully understand it, I picked it apart, studied it, slept on it, and then developed a plan how I could make it work for a user filtered list and also for a favourites list.&lt;/p>
&lt;h4 id="plan">Plan&lt;/h4>
&lt;p>In essence, the approach is to create an array, then an observable array based on the original array. The user filter input - from the data-bind &lt;code>textInput()&lt;/code> - is used to filter the observable array. In short, this is how to create a list which can be filtered by user input but which shows as complete (i.e. has the contents of the original array) if no user input is detected.&lt;/p>
&lt;p>The next challenge is to link the filtered list to the rendering of the location markers. The Google Maps API includes &lt;code>setVisible(bool)&lt;/code> for the location markers. So the logic which determines what is in the observable array then also needs to switch the location marker to &lt;code>setVisible(true)&lt;/code>.&lt;/p>
&lt;h4 id="implementation">Implementation&lt;/h4>
&lt;p>I stepped through trying to get my plan to work and encountered a few errors along the way:&lt;/p>
&lt;p>&lt;strong>Issue 1: Cannot locate the markers&lt;/strong>&lt;/p>
&lt;p>Error: &lt;code>Uncaught TypeError: Cannot read property 'setVisible' of undefined&lt;/code>&lt;/p>
&lt;p>So tried to add &lt;code>this.marker = null;&lt;/code> to my Constructor as the marker was not part of my Model i.e. not in the constructor to add a property when instantiating an object.&lt;/p>
&lt;p>&lt;strong>Issue 2: null ain’t it&lt;/strong>&lt;/p>
&lt;p>Error: &lt;code>Uncaught TypeError: Cannot read property 'setVisible' of null&lt;/code>&lt;/p>
&lt;p>Nope, that’s not quite it although thinking it through, I still believe I need this object property.&lt;/p>
&lt;p>&lt;strong>Issue 3: Need to refactor how I instantiate my objects&lt;/strong>&lt;/p>
&lt;p>Why? So &lt;code>locationItem.marker&lt;/code> is available to my &lt;code>runAttractionFilter&lt;/code> function.&lt;/p>
&lt;p>This solved it. It works! 😀 👍&lt;/p>
&lt;p>&lt;strong>Solution&lt;/strong>&lt;/p>
&lt;p>I split out the functions like this:&lt;/p>
&lt;p>Create an array, instantiating &lt;code>new&lt;/code> objects.&lt;/p>
&lt;p>Add to this array of objects with further information, using separate functions, crucially without the keyword &lt;code>new&lt;/code>. In other words, add to the existing objects, do not create “new” objects.&lt;/p>
&lt;p>This allows the mirrored observable array to render the list in the html file (via &lt;code>foreach: arrayName&lt;/code>) and it shows a complete list.&lt;/p>
&lt;p>As soon as the user starts to search, the filter is applied via the &lt;code>databind=“textinput: userFilter”&lt;/code>. In addition, a KO &lt;code>keyup&lt;/code> event is added to the data-bind so the function managing the observable is run after each keyup.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;attractions are below&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">attractions&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// search and filter an array based on user input
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// set-up empty observable array for visible attractions
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">filteredAttractions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ko&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">observableArray&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// populate visible attractions array with objects from attractions array
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">attractions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">locationItem&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">filteredAttractions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">locationItem&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;filtered Attractions are below&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">filteredAttractions&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// set user filter as ko observable
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userFilter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ko&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">observable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// filter function: updates observableArray and
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// sets visibility of location markers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">runAttractionFilter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">searchFilter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userFilter&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">toLowerCase&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. clear the array
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">filteredAttractions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">removeAll&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. run the filter and only add to the array if a match
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">attractions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">locationItem&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// set marker to false i.e. invisible
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">locationItem&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">marker&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setVisible&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">locationItem&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">toLowerCase&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">indexOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">searchFilter&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">filteredAttractions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">locationItem&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// for each item in the array, set visibility to true i.e. visible
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">filteredAttractions&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">locationItem&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">locationItem&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">marker&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setVisible&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="favourite-locations">Favourite locations&lt;/h3>
&lt;h4 id="attempt-1-using-ko-observables">Attempt 1: Using KO Observables&lt;/h4>
&lt;p>First, I need to plan how to set the object properties and observables.&lt;/p>
&lt;p>Add &lt;code>this.favourite&lt;/code> to my constructor, with a default value &lt;code>false&lt;/code>.&lt;/p>
&lt;p>HTML &lt;code>data-binds&lt;/code>: In a similar way to the toggle functionality for my responsive menu, I plan to toggle the css styling for the favourite icon.&lt;/p>
&lt;p>&lt;code>this.toggleFav = ko.observable(false);&lt;/code>&lt;/p>
&lt;p>When a user clicks on the favourite icon, this will call the function &lt;code>manageFav()&lt;/code> which will switch &lt;code>toggleFav&lt;/code> observable from false to true, or true to false.&lt;/p>
&lt;p>It will also activate a CSS style to change the icon e.g. from black to yellow.&lt;/p>
&lt;p>Then an observable array, originally based on the &lt;code>attractions&lt;/code> array, will repopulate. Only &lt;code>truthy&lt;/code> fav observables will be pushed into this new array.&lt;/p>
&lt;p>This observable array with &lt;code>truthy&lt;/code> properties will be used with a &lt;code>data-bind foreach&lt;/code>to iterate a list in the &lt;code>index.html&lt;/code> page.&lt;/p>
&lt;p>&lt;strong>Issues&lt;/strong>&lt;/p>
&lt;p>HTML data binds must be in the html file, not in the JavaScript &lt;code>contentString&lt;/code> used to render the contents of the infowindow.&lt;/p>
&lt;p>This is my test. It worked as below but move the html snippet into Javascript i.e. the var &lt;code>contentString&lt;/code> which informs the rendering of the marker’s infowindow and it doesn’t.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">// html file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;fav&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">i&lt;/span> &lt;span class="na">data-bind&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;click: favouriteAttractions&amp;#34;&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;fa fa-star&amp;#34;&lt;/span> &lt;span class="na">aria-hidden&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">i&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// js file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">favouriteAttractions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;You clicked on the star&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="attempt-2-using-google-maps-api-for-rightclick-on-the-marker">Attempt 2: Using Google Maps API for ‘rightclick’ on the marker&lt;/h4>
&lt;p>Let’s explore the Google Maps API docs for some insight.&lt;/p>
&lt;p>&lt;a href="https://developers.google.com/maps/documentation/javascript/3.exp/reference" target="_blank" rel="noopener">Google Maps JavaScript API V3 Reference | Google Maps JavaScript API&lt;/a>&lt;/p>
&lt;p>Second best option is to go for a &lt;code>rightclick&lt;/code> event using the Google Maps API. I don’t like the UX of the right click, although it’s a lot less clunky than a double click which I also tried but it really didn’t work at all.&lt;/p>
&lt;p>I ran this to test it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">locationItem&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">marker&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;rightclick&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;right click on marker &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">locationItem&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">favouriteAttractions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">locationItem&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">favouriteAttractions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">locationItem&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;You want to favourite &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">locationItem&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then built up the code for a favourites list.&lt;/p>
&lt;p>I added favourite to the Constructor with a &lt;code>falsy&lt;/code> default:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">favourite&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then an if statement to toggle truthy to falsy, and vice versa.&lt;/p>
&lt;p>Then I built an observable array to store the favourites, which only pushed to this array if &lt;code>locationItem.favourite == true&lt;/code>.&lt;/p>
&lt;p>In the HTML &lt;code>data-bind&lt;/code> I added a &lt;code>foreach: favAttractions&lt;/code>.&lt;/p>
&lt;p>After some backwards and forwards resolving console errors, I got it working but with one bug.&lt;/p>
&lt;p>The array rendered as a list and it duplicated the entries e.g.&lt;/p>
&lt;p>I like venue 1; renders venue 1.&lt;/p>
&lt;p>Then I like venue 2; renders venue 1, venue 1, venue 2.&lt;/p>
&lt;p>Hmmm…&lt;/p>
&lt;p>After some experimenting, I realised I needed to clear the array before repopulating with venues where &lt;code>locationItem = true&lt;/code>.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>That’s it. Project complete! It feels great to have built something which tested my skills so thoroughly.&lt;/p>
&lt;p>My key takeaways from this project are:&lt;/p>
&lt;ol>
&lt;li>Plan but do not over plan. Better to have a good plan, then build some code which can be refactored later.&lt;/li>
&lt;li>Use the power of &lt;code>console.log()&lt;/code>. It&amp;rsquo;s an invaluable way of determining what is actually going on, if your code is doing what you think it is, and it really helped with my understanding of how my code behaves.&lt;/li>
&lt;li>Try to keep things simple. Sometimes easier said than done but strive for it nevertheless.&lt;/li>
&lt;li>Read the docs!&lt;/li>
&lt;li>Try to solve the problem yourself but after a certain point, ask for help/guidance i.e. someone experienced to point you in the right direction.&lt;/li>
&lt;/ol>
&lt;p>Thanks for reading. I hope this proves useful (to future me) to see how I tackled a difficult project.&lt;/p></description></item><item><title>Sublime Text 3 and Python</title><link>https://samatkins.co/post/sublime-text-python/</link><pubDate>Sun, 08 Jan 2017 10:15:16 +0000</pubDate><guid>https://samatkins.co/post/sublime-text-python/</guid><description>&lt;p>As I wrote &lt;a href="https://samatkins.co/100daysofcode.html">recently&lt;/a>, after jumping between Javascript, GoLang and Python, I&amp;rsquo;ve decided to heed some good advice and focus on one language for awhile. And that language is Python.&lt;/p>
&lt;p>This post is about my Sublime Text 3 set-up for programming in Python.&lt;/p>
&lt;p>As an aside, this post is about getting your machine set up to manage different Python projects - &lt;a href="https://samatkins.co/python-project-virtualenv.html">Managing Python Projects with Virtualenv&lt;/a>.&lt;/p>
&lt;h2 id="why-sublime-text">Why Sublime Text?&lt;/h2>
&lt;p>Whilst I’m still a fan of Atom, the number one reason I switched to Sublime is speed. As a beginner, it’s a little bit harder to set-up and get to know than Atom but still completely worth it.&lt;/p>
&lt;p>The best way to get to grips with the power of Sublime text is to check out Wes Bos’ superb course on &lt;a href="http://wesbos.com/sublime-text-book/" target="_blank" rel="noopener">Sublime Text&lt;/a>.&lt;/p>
&lt;p>So what are the must-have Python packages for Sublime?&lt;/p>
&lt;h2 id="packages">Packages&lt;/h2>
&lt;p>Package Control - this is not Python specific but in general. It makes installing / removing packages really easy. To browse for packages go to &lt;a href="https://packagecontrol.io/" target="_blank" rel="noopener">Package Control - the Sublime Text package manager&lt;/a>.&lt;/p>
&lt;p>&lt;a href="https://packagecontrol.io/packages/Anaconda" target="_blank" rel="noopener">Anaconda&lt;/a> - this is a really powerful package for linting and autocomplete, amongst many other things. It also has a Build System (Tools - Build System - Anaconda Build System) for running Python scripts within Sublime Text where appropriate.&lt;/p>
&lt;p>&lt;a href="https://packagecontrol.io/packages/Terminal" target="_blank" rel="noopener">Terminal&lt;/a> - not just for Python but this is useful to run Python in a Terminal inside Sublime. It is also an alternative to the Anaconda Build System.&lt;/p>
&lt;p>Theme and colours - an excellent way to live browse different themes and colours directly in Sublime Text is to use the &lt;a href="https://packagecontrol.io/packages/Colorsublime" target="_blank" rel="noopener">Colorsublime package&lt;/a>. Although theme and colour is of course very much a subjective and personal choice, my current favourite for coding in Python is:&lt;/p>
&lt;ul>
&lt;li>Theme: Boxy Monokai&lt;/li>
&lt;li>Colour Scheme: Flatland Monokai&lt;/li>
&lt;/ul>
&lt;p>And that’s it. Thanks for reading!&lt;/p></description></item><item><title>100 Days of Code</title><link>https://samatkins.co/post/hundred-days-of-code/</link><pubDate>Sun, 01 Jan 2017 10:15:16 +0000</pubDate><guid>https://samatkins.co/post/hundred-days-of-code/</guid><description>&lt;p>I&amp;rsquo;ve joined the #100DaysOfCode Challenge. Over Christmas I decided to focus in Q1 2017 on completing Udacity&amp;rsquo;s Full-Stack Nanodegree. As part of this, a complementary objective is I want to really get to grips with Python.&lt;/p>
&lt;p>When I read about the &lt;strong>#100DaysOfCode&lt;/strong> challenge I thought it was an ideal way of holding myself accountable for my Q1 objectives. It will reinforce the habit of coding every day and helping to learn Python. I also hope to get involved in and contribute to the programming community and make some friends along the way.&lt;/p>
&lt;h3 id="tldr">tl;dr&lt;/h3>
&lt;p>&lt;strong>Q1 2017 Objectives:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Complete Udacity Full-Stack Nanodegree&lt;/li>
&lt;li>Continue to learn the Python programming language&lt;/li>
&lt;li>Complete the #100DaysOfCode challenge*&lt;/li>
&lt;li>Contribute to the programming community&lt;/li>
&lt;li>Build some sh*t&lt;/li>
&lt;/ol>
&lt;p>(*Well, 87 days actually but I can complete the challenge in April.)&lt;/p>
&lt;h3 id="follow-me">Follow Me&lt;/h3>
&lt;p>My log recording each day&amp;rsquo;s progress is on &lt;a href="https://github.com/sam-atkins/100-days-of-code/blob/master/log.md" target="_blank" rel="noopener">Github&lt;/a>.&lt;/p></description></item><item><title>Managing Python projects with Virtualenv</title><link>https://samatkins.co/post/python-projects-virtualenv/</link><pubDate>Fri, 30 Dec 2016 10:15:16 +0000</pubDate><guid>https://samatkins.co/post/python-projects-virtualenv/</guid><description>&lt;h2 id="python-2-versus-3">Python 2 versus 3&lt;/h2>
&lt;p>To summarise the Python wiki:&lt;/p>
&lt;blockquote>
&lt;p>“Python 2.x is legacy, Python 3.x is the present and future of the language.”&lt;/p>&lt;/blockquote>
&lt;p>More info is available on the Python &lt;a href="https://wiki.python.org/moin/Python2orPython3" target="_blank" rel="noopener">wiki&lt;/a>.&lt;/p>
&lt;h3 id="why-use-python-2">Why use Python 2?&lt;/h3>
&lt;p>Wherever possible, use Python 3. However, sometimes Python 2 may be necessary and per the Python wiki the two key reasons are likely to be:
deploying to an environment you don’t control so it may impose a specific version of Python
if you want to use a third party package or utility which does not have a Python 3 compatible version&lt;/p>
&lt;h2 id="running-p2-and-p3-on-macos">Running P2 and P3 on macOS&lt;/h2>
&lt;p>macOS comes with Python 2 pre-installed. I believe it is used in the Operating System. As a result of this, even if you want to use Python 3, you should not try to uninstall Python 2. At least this is my summary of some Stackoverflow articles and I’m not going to experiment to find out if they are right or wrong.
It is actually fairly straightforward to install Python 3. Downloads for Python 3 are available at Python.org. This downloads an installer with GUI instructions on how to install.&lt;/p>
&lt;p>Another option is to use brew e.g.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ brew install python3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once Python 3 is installed, use which to check Python is installed correctly.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ which python
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/usr/local/bin/python
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ which python3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/usr/local/bin/python3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The results should show Python is in the above directories. If this is not the case, the installs may not be correct.&lt;/p>
&lt;h2 id="intro-to-virtual-environments">Intro to Virtual Environments&lt;/h2>
&lt;p>I read more and more support is becoming available for Python 3 but for example the Udacity course I’m following is still using Python 2, and Google Cloud still requires Python 2 (at least from my beginner’s understanding so I might be wrong on this one).
Either way, how do you manage manage dependencies if you are switching between Python 2 and 3?&lt;/p>
&lt;p>VirtualEnv to the rescue. Virtualenv is a tool to create isolated Python environments. It allows you to use packages and utilities within a virtual environment i.e. “has its own installation directories”&lt;/p>
&lt;h2 id="my-documentation">My documentation&lt;/h2>
&lt;p>The documentation for using Python and virtualenv is dispersed across multiple sites, in particular regarding managing projects in Python 2 versus 3. The primary purpose of this blogpost is a consolidation of various things I’ve found for my own future reference.&lt;/p>
&lt;h3 id="how-to-use-virtualenv">How to use virtualenv&lt;/h3>
&lt;p>The below refers to using Python 2. Separate Python 3 instructions follow after.&lt;/p>
&lt;p>Start a project repo as per normal:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir new-python-project
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> new-python-project
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once inside the project directory, run the command &lt;code>virtualenv env&lt;/code>&lt;/p>
&lt;p>&lt;code>env&lt;/code> is the directory to place all the virtualenv good stuff. This name can be changed but I always leave this as the default so I know exactly what this is.
It also has the small advantage that env is already included in the Github Python .gitignore template, saving a few keystrokes to get your git repo up and running.&lt;/p>
&lt;h4 id="pip">pip&lt;/h4>
&lt;p>To install package dependencies, you first need to activate the virtual environment.&lt;/p>
&lt;p>The command is:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ &lt;span class="nb">source&lt;/span> env/bin/activate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This must be run from within the project directory.&lt;/p>
&lt;p>To save some typing, I saved activate as an alias in my zsh alias config.&lt;/p>
&lt;p>To deactivate, simply type the command &lt;code>deactivate&lt;/code>.&lt;/p>
&lt;h4 id="requirementstxt">Requirements.txt&lt;/h4>
&lt;p>Add a pip requirements.txt file to the root of your project directory.
To do this type the command &lt;code>pip freeze pip freeze &amp;gt; requirements.txt&lt;/code>&lt;/p>
&lt;p>This is similar to a package.json file for Gulp with all your packages and dependencies. It makes it possible to duplicate the project using the same dependencies. For example, by running pip install -r requirements.txt you would install the specific package dependencies.&lt;/p>
&lt;h2 id="python-3">Python 3&lt;/h2>
&lt;p>To set-up a Python 3 amend the virtualenv command as below:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">virtualenv -p python3 env
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Activate and deactivate work as above. Make sure to activate the virtual env prior to installing any packages and utilities but also to ensure your code is executed correctly. Without the virtualenv being active, your system may look to run the default Python install i.e. Python 2 and your code may not execute.&lt;/p>
&lt;hr>
&lt;p>Thanks for reading. As I mentioned, the main purpose of this is a consolidation of useful information I found in order to manage Python and virtual environments for my projects.
Should anyone else read this, and spot any improvements or corrections, please let me know. Otherwise, I hope it may also prove useful to other readers.&lt;/p></description></item><item><title>Installing Tree on OSx</title><link>https://samatkins.co/post/installing-tree/</link><pubDate>Mon, 19 Sep 2016 10:15:16 +0000</pubDate><guid>https://samatkins.co/post/installing-tree/</guid><description>&lt;p>This is how to install Tree on macOS in the Terminal.app. It assumes Homebrew is installed. &lt;a href="http://brew.sh/" target="_blank" rel="noopener">Homebrew&lt;/a> is a package manager for OSx.&lt;/p>
&lt;p>To install run this command using Homebrew: &lt;code>brew install tree&lt;/code>&lt;/p>
&lt;p>Now that it is installed, here&amp;rsquo;s how to use it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Useful to run this to test it installed properly and displays help&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tree --help
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Lists all files and directories&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tree -a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Shows the tree of specified directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tree -a &amp;lt;directory name&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Lists directories only in the pwd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tree -d
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="thoughts">Thoughts&lt;/h2>
&lt;p>An aside, previously I never really understood the power (and thrill) of using the command line. It&amp;rsquo;s relatively simple to install and use this binary, and yet it gave me such satisfaction from discovering tree to instal/use. I think this is a sign that pursuing learning to code is the right thing for me.&lt;/p></description></item><item><title>Why I'm using Hugo static site generator</title><link>https://samatkins.co/post/why-hugo/</link><pubDate>Sun, 18 Sep 2016 10:15:16 +0000</pubDate><guid>https://samatkins.co/post/why-hugo/</guid><description>&lt;p>Encouraged by a conversation with Andy @ Udacity, I made the decision to start a technical coding blog with a focus on helping me to learn more and document my coding journey.&lt;/p>
&lt;p>I did waver if this was the right thing to do but &lt;a href="https://medium.freecodecamp.com/new-contributors-to-open-source-please-blog-more-920af14cffd#.ecg8qppwg" target="_blank" rel="noopener">this&lt;/a> and &lt;a href="https://emptysqua.re/blog/write-an-excellent-programming-blog/" target="_blank" rel="noopener">this&lt;/a> helped convince me to go ahead.&lt;/p>
&lt;p>The next decision was then how to set-up it all up.&lt;/p>
&lt;h2 id="diy">DIY&lt;/h2>
&lt;p>It was immediately clear to me I wanted a Do It Yourself option. I wanted a project to help develop my technical learning rather than just using Wordpress.com or Medium to share my views, opinions and learnings. (Although I think I may experiment with posting some blogs on these sites as well.)&lt;/p>
&lt;p>Having blogged before using a self-hosted Wordpress.org site I didn&amp;rsquo;t want to go down that route again. I&amp;rsquo;m a big fan of Wordpress, especially if your site is more than just a blog. &lt;strong>But&lt;/strong> page load times, the admin of having to upgrade for security, was all too much for what I wanted to be just a simple blog site.&lt;/p>
&lt;h2 id="static-site-generators">Static site generators&lt;/h2>
&lt;p>Whilst on Github I noticed the option to set-up a GitHub Page, with their preferred option of Jekyll. This sounded really interesting.&lt;/p>
&lt;p>To digress for a moment, &lt;a href="https://gohugo.io/overview/introduction/" target="_blank" rel="noopener">this&lt;/a> is a good explanation of what a static site generator is.&lt;/p>
&lt;p>I narrowed down my options to Pelican, Jekyll and Hugo. Let&amp;rsquo;s take a look at each one in turn.&lt;/p>
&lt;h3 id="pelican">Pelican&lt;/h3>
&lt;p>As a &lt;em>codenewbie&lt;/em>, I liked the idea of Pelican as it uses Python. Python is to me a great language for a beginner. The syntax is really straightforward and easy to comprehend. This was a big plus for. Also, the documentation and quick start are both good for Pelican.&lt;/p>
&lt;p>The downsides are the lack of themes. Of course I could develop my own but my focus has to be learning to code and having some kind of qualification and portfolio to show for it. For me, building a theme is someway down the &lt;em>TODO&lt;/em> list.&lt;/p>
&lt;p>I tried Pelican and managed to get a site up and running, but ran into some technical difficulties trying to switch between templates. My failing I know but this prompted me to try
another option.&lt;/p>
&lt;h3 id="jekyll">Jekyll&lt;/h3>
&lt;p>I plan at some stage to learn some Ruby. Initially I was put off by using something that used a different language but after my experience with Pelican I thought I would try it out.&lt;/p>
&lt;p>I took on more than I could manage at this stage. The list of things to install is long, and Xcode itself took ages (4GB download and install took hours - be warned!).&lt;/p>
&lt;p>The issue for me was I kept encountering all sorts of problems to install and set-up, from the versions of installed bundler, ruby and rubygems not being compatible, to receiving errors
when trying to build the site.&lt;/p>
&lt;p>Whilst the community are really helpful, and I hate to quit, I had to admit defeat. I think if you are familiar with Ruby then Jekyll is a really good option. For me at this stage, it was too much of a challenge.&lt;/p>
&lt;h3 id="hugo">Hugo&lt;/h3>
&lt;p>I was initially reluctant to try Hugo as again it was a different language from my initial focus list (html and css obviously, javascript and python). What swayed me was my experience with Pelican and Jekyll (primarily due to my lack of programming and technical knowledge it must be said), the &lt;a href="http://themes.gohugo.io/" target="_blank" rel="noopener">good selection of themes&lt;/a> available for Hugo, and the
&lt;a href="http://gohugo.io/" target="_blank" rel="noopener">excellent user documentation&lt;/a>.&lt;/p>
&lt;p>With Hugo I had zero difficulties with getting a simple site set-up. The documentation is really good in explaining how to install Hugo and get a site set-up.&lt;/p>
&lt;p>I now have the satisfaction of having &amp;lsquo;built&amp;rsquo; my own site, can learn more about configuring the site but it is also fairly light touch in maintaining it. (Now as light touch as Wordpress.com or Medium but this is a decision I knowingly took for good reason). I can use it as my blog as part of my journey to learn to code. I also plan to learn more about the Go language, and see how I can use Hugo to build a site.&lt;/p>
&lt;p>As an added bonus, it has introduced me to the Go language and I&amp;rsquo;ve added to my learning list.&lt;/p>
&lt;h2 id="quick-guide-to-hugo">Quick Guide to Hugo&lt;/h2>
&lt;p>This section is intended as a quick reference guide to the key aspects of installing Hugo.&lt;/p>
&lt;p>First up, check out these links for really good explanations on installing and setting up Hugo:&lt;/p>
&lt;ul>
&lt;li>Installing Hugo on a &lt;a href="http://gohugo.io/tutorials/installing-on-mac/" target="_blank" rel="noopener">Mac&lt;/a>&lt;/li>
&lt;li>Superb step by step guide to hosting on &lt;a href="http://gohugo.io/tutorials/github-pages-blog/#introduction" target="_blank" rel="noopener">GitHub Pages&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Secondly, here are the key points and commands to consider in order to install and use Hugo:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># To install Hugo via Homebrew&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install hugo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install Pygments for code syntax highlighting&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install Pygments
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># To check if Hugo is installed and display help&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hugo &lt;span class="nb">help&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Check which version of Hugo is installed&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hugo version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Command to quickly scaffold a new site called exampleName.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hugo new site exampleName
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Hugo runs its own webserver to render the files, includes drafts&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hugo server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Hugo runs its own webserver to render the files, includes drafts&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hugo server --buildDrafts
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If a theme is not specified in your config file, add `-t themeName` to the command&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Hugo builds your site into the public folder&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hugo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Why I'm blogging again</title><link>https://samatkins.co/post/blogging-again/</link><pubDate>Sat, 17 Sep 2016 10:15:16 +0000</pubDate><guid>https://samatkins.co/post/blogging-again/</guid><description>&lt;p>First, some context. I have blogged before. For several years I blogged on cycling (road, mountain biking, and as a means of simply getting from A to B).&lt;/p>
&lt;p>I stopped it in the end because it was very time consuming. Not just the writing but the maintenance of my self-hosted WordPress site.&lt;/p>
&lt;p>I vowed when I quit, &lt;strong>no more blogging&lt;/strong>.&lt;/p>
&lt;p>So why start again?&lt;/p>
&lt;h2 id="my-reasons-to-blog">My reasons to blog&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Learning&lt;/strong> - to summarise this &lt;a href="https://emptysqua.re/blog/write-an-excellent-programming-blog/" target="_blank" rel="noopener">excellent blogpost&lt;/a>, “writing is thinking. There is no more thorough way to understand than to explain in writing.”&lt;/li>
&lt;li>&lt;strong>Open source&lt;/strong> - by sharing my learning and technical knowledge to the public (sounds grand, I&amp;rsquo;m under no illusions I am likely to be the only person who will probably read this) it forces me to really learn, it may help others, and I can start the journey of becoming known in the open source community&lt;/li>
&lt;li>&lt;strong>Project&lt;/strong> - building my own site (more on this in another post), I can take on a technical project and learn by doing&lt;/li>
&lt;li>&lt;strong>Community&lt;/strong> - again without any illusions of grandeur, if one blogpost helps just one person then I am playing a part in the community of helping others learn to code&lt;/li>
&lt;/ul></description></item><item><title>About</title><link>https://samatkins.co/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://samatkins.co/about/</guid><description>&lt;p>Hi, I’m Sam. I&amp;rsquo;m a software engineer.&lt;/p>
&lt;p>I live in London, England.&lt;/p>
&lt;p>At work, I am a Senior Software Engineer at &lt;a href="https://www.otera.ai" target="_blank" rel="noopener">Otera&lt;/a>. Previously, I worked at two other tech start/scale-ups:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://eigentech.com/" target="_blank" rel="noopener">Eigen Technologies&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.bighealth.com/" target="_blank" rel="noopener">Big Health&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>After university, I began my career in supply chain working at a logistics provider and then a manufacturer. I then worked for close to ten years at in the Big Four Management Consultancy firms KPMG and EY. I subsequently transitioned to a software engineering career at Big Health.&lt;/p>
&lt;p>On this site you can find:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://samatkins.co/post">articles&lt;/a>: blogs I&amp;rsquo;ve written&lt;/li>
&lt;li>&lt;a href="https://samatkins.co/now">now&lt;/a>: about me now and what I&amp;rsquo;m currently doing&lt;/li>
&lt;/ul></description></item><item><title>Now</title><link>https://samatkins.co/now/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://samatkins.co/now/</guid><description>&lt;p>(This is a &lt;a href="https://nownownow.com/about" target="_blank" rel="noopener">now page&lt;/a>, and if you have your own site, you &lt;a href="https://sive.rs/now2" target="_blank" rel="noopener">should make one&lt;/a>, too).&lt;/p>
&lt;p>Updated on 16th November 2025 at my home in London, UK.&lt;/p>
&lt;hr>
&lt;h2 id="work">Work&lt;/h2>
&lt;p>After a couple of years as an Engineering Manager, on the &lt;a href="https://charity.wtf/2017/05/11/the-engineer-manager-pendulum/" target="_blank" rel="noopener">engineer/manager pendulum&lt;/a> I&amp;rsquo;m working on the technical side again as a Senior Software Engineer at Otera.&lt;/p>
&lt;h2 id="exercise">Exercise&lt;/h2>
&lt;p>Around a year ago I started going regularly to the gym. I enjoy lifting weights. I have also started going bouldering with my son which is great exercise and a great way to get rid of stress.&lt;/p>
&lt;h2 id="reading">Reading&lt;/h2>
&lt;p>I enjoy reading, mainly sci-fi, thrillers and crime novels. I try to avoid reading too much politics and news as I find it too depressing. I&amp;rsquo;ve deleted anything with an algorithmic feed from my phone to avoid distractions and doom-scrolling.&lt;/p>
&lt;h2 id="learning">Learning&lt;/h2>
&lt;p>I listen to technical podcasts, my current favourite remains the &lt;a href="https://changelog.com/podcast" target="_blank" rel="noopener">Changelog&lt;/a>. I also read technical software engineering books and articles to learn new things and keep up to date on the latest in software engineering.&lt;/p>
&lt;h2 id="family">Family&lt;/h2>
&lt;p>I live in London with my family.&lt;/p></description></item></channel></rss>