<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>NotesToSelf.Dev</title>
    <link>https://notestoself.dev/</link>
    <description>Recent content on NotesToSelf.Dev</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-gb</language>
    
	<atom:link href="https://notestoself.dev/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Bash script to run CDK synth only if there are changed TS files</title>
      <link>https://notestoself.dev/posts/cdk-synth-on-change-bash-script/</link>
      <pubDate>Tue, 08 Apr 2025 10:57:23 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/cdk-synth-on-change-bash-script/</guid>
      <description>The cdk synth command can be pretty slow, even when running for a single small stack. We also often want to run the CDK synth command regularly as part of local development setups, so it&amp;rsquo;s nice to skip the re-synth if no relevant files have changed.
Here&amp;rsquo;s a bash script that checks for changed TypeScript source files by comparing their timestamp to that of the generated template file from the previous synth.</description>
    </item>
    
    <item>
      <title>AWS SAM local (WSGI) drops headers with underscores</title>
      <link>https://notestoself.dev/posts/sam-local-wsgi-drops-headers-underscore/</link>
      <pubDate>Sun, 30 Mar 2025 11:57:23 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/sam-local-wsgi-drops-headers-underscore/</guid>
      <description>TLDR: SAM local drops headers that contain an underscore, due to using Flask and WSGI internally, which differs from the behaviour of a deployed API Gateway.
I&amp;rsquo;m working on an existing project that uses AWS CDK to provision API Gateway in front of some Lambda functions. The team would like to be able to run this locally for faster development iterations, so I set that up with the sam local start-api command, using the template emitted by cdk synth.</description>
    </item>
    
    <item>
      <title>Cache a public Docker image in a GitHub Action</title>
      <link>https://notestoself.dev/posts/github-action-cache-public-docker-image/</link>
      <pubDate>Sun, 23 Mar 2025 11:57:23 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/github-action-cache-public-docker-image/</guid>
      <description>Here&amp;rsquo;s a reusable GitHub Action definition that caches a public Docker image:
---name:Dockerpublicimagecachedescription:PullandcacheapublicDockerimageforreuseinputs:image_name_tag:description:Dockerimagenameandtagtopullandcacherequired:trueruns:using:&amp;#34;composite&amp;#34;steps:- name:Restoreimagecacheid:restore_cacheuses:actions/cache@v4with:path:&amp;#39;ci/cache/docker&amp;#39;key:&amp;#39;cache-docker-${{ inputs.image_name_tag }}&amp;#39;- name:Updateimagecacheoncachemissif:steps.restore_cache.outputs.cache-hit!=&amp;#39;true&amp;#39;shell:bashrun:&amp;gt; docker pull &amp;#39;${{ inputs.image_name_tag }}&amp;#39;&amp;amp;&amp;amp;mkdir-p$(dirname&amp;#39;./ci/cache/docker/${{ inputs.image_name_tag }}.tar&amp;#39;)&amp;amp;&amp;amp;dockerimagesave&amp;#39;${{ inputs.image_name_tag }}&amp;#39;--output&amp;#39;./ci/cache/docker/${{ inputs.image_name_tag }}.tar&amp;#39;- name:Useimagecacheoncachehitif:steps.restore_cache.outputs.cache-hit==&amp;#39;true&amp;#39;shell:bashrun:&amp;gt; docker image load --input &amp;#39;./ci/cache/docker/${{ inputs.image_name_tag }}.tar&amp;#39;If you have this action definition at .github/actions/docker_image_cache in the repo, then you can use it from a workflow like this:
---jobs:foo_job:runs-on:ubuntu-lateststeps:- name:LocalStackpublicimagecacheuses:./.github/actions/docker_image_cachewith:image_name_tag:&amp;#39;localstack/localstack:4.2.0&amp;#39;After the first run, you should see output like this from the action, showing that it has loaded the Docker image from the cache:</description>
    </item>
    
    <item>
      <title>eslint changed TypeScript files only</title>
      <link>https://notestoself.dev/posts/eslint-changed-ts-files-only/</link>
      <pubDate>Sun, 23 Mar 2025 10:57:23 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/eslint-changed-ts-files-only/</guid>
      <description>It&amp;rsquo;s best if a linter is applied from the beginning of a project, and with default linting rules, as that way the project benefits from the linting with the minimum possible effort.
However, we often join existing projects which have already been developed without the use of a linter, by which point there are possibly thousands of linter violations. In that situation, one option is to incrementally improve the linting by only applying the linter to changed files.</description>
    </item>
    
    <item>
      <title>Splitting a text file into multiple separate files by section with csplit</title>
      <link>https://notestoself.dev/posts/bash-split-text-file-multiple-separate-sections/</link>
      <pubDate>Wed, 26 Feb 2025 10:57:23 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/bash-split-text-file-multiple-separate-sections/</guid>
      <description>Here&amp;rsquo;s a bash command to split a text file into multiple separate files by section delimited by a regex pattern matching titled sections in the source file:
csplit \  -z \  -f section_ \  -b &amp;#34;%02d.txt&amp;#34; \  document.txt \  &amp;#39;/\nSection [0-9]\+\n/&amp;#39; \  &amp;#39;{*}&amp;#39; The key part is the /\nSection [0-9]\+\n/ regex pattern, which matches section titles such as &amp;ldquo;Section 18&amp;rdquo; alone on a line.</description>
    </item>
    
    <item>
      <title>Python &#34;raises in exception group&#34; context manager for tests</title>
      <link>https://notestoself.dev/posts/python-tests-raises-exception-group-matching-context-manager/</link>
      <pubDate>Sun, 05 Jan 2025 17:11:23 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/python-tests-raises-exception-group-matching-context-manager/</guid>
      <description>Here&amp;rsquo;s a Python testing utility function for asserting on an expected Exception type from within an ExceptionGroup:
from contextlib import contextmanager @contextmanager def raises_in_group( expected_exception: type[Exception], match_message: str = &amp;#34;&amp;#34;, ): &amp;#34;&amp;#34;&amp;#34; Like pytest.raises, but for an Exception inside an ExceptionGroup, for example coming from asyncio.TaskGroup. &amp;#34;&amp;#34;&amp;#34; matched_type = [] matched_message = [] try: yield except* expected_exception as e: assert isinstance(e, ExceptionGroup) matched_type = [exp for exp in e.exceptions if isinstance(exp, expected_exception)] matched_message = [exp for exp in matched_type if match_message in str(exp)] assert len(matched_type) &amp;gt;= 1, f&amp;#34;Expected at least one {expected_exception} exception.</description>
    </item>
    
    <item>
      <title>Creating AWS Organizational Units and Accounts for Prod and Dev in CloudFormation</title>
      <link>https://notestoself.dev/posts/aws-cloudformation-create-organizational-units-accounts-prod-dev/</link>
      <pubDate>Sat, 05 Oct 2024 15:44:45 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/aws-cloudformation-create-organizational-units-accounts-prod-dev/</guid>
      <description>Here&amp;rsquo;s an AWS CloudFormation template and deployment script to set up Organizational Units and Accounts for Prod and Dev for a new project or department.
Deploying a CloudFormation stack from this template will require a user in the Organization&amp;rsquo;s management account with the AdministratorAccess permission set.
The created Organizational Unit (OU) and Account structure will look this:
Organization Root [pre-existing] ╚═ Management Account [pre-existing] ╚═ Project Parent OU ╚═ Project Prod OU ╚═ Project Prod Account ╚═ Project Dev OU ╚═ Project Dev Account  The OUs are not strictly necessary, but they help to group the whole project under a single OU, and to allow for other accounts to be added later within the Prod and Dev OUs if necessary.</description>
    </item>
    
    <item>
      <title>Reduce pragma no cover comments with tool.coverage.report exclude_also</title>
      <link>https://notestoself.dev/posts/py-coverage-pragma-no-cover-pyproject-tool-coverage-report-exclude-also/</link>
      <pubDate>Sun, 29 Sep 2024 19:45:13 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/py-coverage-pragma-no-cover-pyproject-tool-coverage-report-exclude-also/</guid>
      <description>If you&amp;rsquo;re using Coverage.py or pytest-cov in your Python project, you probably end up with # pragma: no cover comments all over the place to prevent odd lines from being marked as missing coverage, like this:
class Foobar: def do_something(): # pragma: no cover raise NotImplementedError() You can reduce the need for these # pragma: no cover comments by configuring Coverage.py to ignore certain patterns by default. This can be configured in the pyproject.</description>
    </item>
    
    <item>
      <title>Github Action to post a Telegram notification with no dependencies</title>
      <link>https://notestoself.dev/posts/github-action-telegram-notification-no-dependencies/</link>
      <pubDate>Sun, 29 Sep 2024 19:32:04 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/github-action-telegram-notification-no-dependencies/</guid>
      <description>Here&amp;rsquo;s a Github Action that posts a notification message to a Telegram chat, without using any dependencies.
---name:PosttoTelegramdescription:PostamessagetoaTelegramchannelinputs:telegram_bot_token:description:AuthtokenoftheTelegrambotviawhichtopost.required:truetelegram_chat_id:description:IDoftheTelegramchanneltowhichtosendthemessage.required:truemessage_content:description:StringcontentoftheTelegrammessagetosend.required:trueruns:using:&amp;#34;composite&amp;#34;steps:- shell:bashrun:| curl &amp;#34;https://api.telegram.org/bot${{ inputs.telegram_bot_token }}/sendMessage&amp;#34; \--header&amp;#34;Content-Type: application/json&amp;#34;\--requestPOST\--data&amp;#39;{ &amp;#34;chat_id&amp;#34;: &amp;#34;${{ inputs.telegram_chat_id }}&amp;#34;, &amp;#34;text&amp;#34;: &amp;#34;${{ inputs.message_content }}&amp;#34; }&amp;#39;The key part of the action is the bash script that uses curl to post a message to Telegram:
curl &amp;#34;https://api.telegram.org/bot${{inputs.telegram_bot_token}}/sendMessage&amp;#34; \  --header &amp;#34;Content-Type: application/json&amp;#34; \  --request POST \  --data &amp;#39;{ &amp;#34;chat_id&amp;#34;: &amp;#34;${{ inputs.telegram_chat_id }}&amp;#34;, &amp;#34;text&amp;#34;: &amp;#34;${{ inputs.</description>
    </item>
    
    <item>
      <title>Python Coverage boolean compound condition line exit missing branch coverage</title>
      <link>https://notestoself.dev/posts/py-coverage-compound-boolean-line-exit/</link>
      <pubDate>Sat, 28 Sep 2024 13:11:39 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/py-coverage-compound-boolean-line-exit/</guid>
      <description>With branch coverage enabled in Coverage.py or pytest-coverage, you might see it report missing coverage on a compound boolean condition, like this:
def foobar() -&amp;gt; bool: return ( condition_1() and condition_2() and condition_3() and condition_4() and condition_5() ) That might lead to a report that the line where the boolean compound condition begins to the exit of the function are missing coverage. The report will look like this:
---------- coverage: platform linux, python 3.</description>
    </item>
    
    <item>
      <title>Python recursive override path function and mixin class</title>
      <link>https://notestoself.dev/posts/python-recursive-override-paths-function-mixin/</link>
      <pubDate>Wed, 25 Sep 2024 13:36:56 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/python-recursive-override-paths-function-mixin/</guid>
      <description>Here&amp;rsquo;s a recursive utility function in Python that allows overriding nested paths through objects that can be mutable mappings, mutable sequences or dataclass instances.
The function allows doing things like this:
override_paths( { &amp;#34;foo0&amp;#34;: &amp;#34;bar&amp;#34;, &amp;#34;lvl1&amp;#34;: { &amp;#34;foo1&amp;#34;: &amp;#34;bar1&amp;#34;, &amp;#34;list1&amp;#34;: [1, 2, 3, 4, 5, 6], &amp;#34;foobar1&amp;#34;: Foobar(foo=&amp;#34;hello1&amp;#34;), }, }, foo0=&amp;#34;123&amp;#34;, lvl1__foo1=&amp;#34;456&amp;#34;, lvl1__list1__3=23, lvl1__foobar1__foo=&amp;#34;hello2&amp;#34;, ) { &amp;#34;foo0&amp;#34;: &amp;#34;123&amp;#34;, &amp;#34;lvl1&amp;#34;: { &amp;#34;foo1&amp;#34;: &amp;#34;456&amp;#34;, &amp;#34;list1&amp;#34;: [1, 2, 3, 23, 5, 6], &amp;#34;foobar1&amp;#34;: Foobar(foo=&amp;#34;hello2&amp;#34;), }, } There is also a OverridableMixin that makes it convenient to do things like this inline:</description>
    </item>
    
    <item>
      <title>Github Action job to post pytest coverage as PR comment</title>
      <link>https://notestoself.dev/posts/github-action-job-pytest-coverage-pr-comment/</link>
      <pubDate>Mon, 23 Sep 2024 18:07:38 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/github-action-job-pytest-coverage-pr-comment/</guid>
      <description>Here&amp;rsquo;s a Github Action job that runs pytest coverage and then posts a summary of the coverage report as a PR comment with Markdown formatting. This is handy as part of a PR workflow to make the test results and coverage summary visible on the PR.
coverage:name:coverageruns-on:ubuntu-lateststeps:- uses:actions/checkout@v4- uses:./.github/actions/setup_python- uses:./.github/actions/job_urlid:job_urlwith:step_name:&amp;#39;pytest&amp;#39;- name:pytestrun:| pytest- name:Testcoveragesummaryreportid:coverage_reportif:always()run:| {echo&amp;#34;COVERAGE_REPORT&amp;lt;&amp;lt;EOF&amp;#34;coveragereport--format=markdown--show-missing--skip-coveredecho&amp;#34;EOF&amp;#34;}&amp;gt;&amp;gt;&amp;#34;$GITHUB_OUTPUT&amp;#34;- name:Removepreviouscoveragecommentsuses:./.github/actions/remove_commentsif:always()with:identifier:coverage- name:TestcoveragePRcommentid:coverage_commentif:always()uses:actions/github-script@v7with:script:| github.rest.issues.createComment({owner:context.repo.owner,repo:context.repo.repo,issue_number:context.issue.number,body:`[coverage](${{steps.job_url.outputs.job_url}})\r\n\r\n${{steps.coverage_report.outputs.COVERAGE_REPORT}}`})Note that this uses separate actions to setup the Python env, get the job URL and to remove previous PR comments.</description>
    </item>
    
    <item>
      <title>Github Action for PR comment with command output on exit status</title>
      <link>https://notestoself.dev/posts/github-action-command-comment-exit-status/</link>
      <pubDate>Mon, 23 Sep 2024 17:50:45 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/github-action-command-comment-exit-status/</guid>
      <description>Here&amp;rsquo;s a Github Action that runs a given bash command, captures the stdout and strderr output, and posts a comment with the output back to the PR if the command has a non-zero exit status.
This is useful for running check commands such as linters against a PR, and making a PR comment with the linter output if there are linting errors.
name:Command&amp;amp;Commentdescription:RunabashcommandandcommentfailureoutputbacktoPRinputs:identifier:description:Singlewordidentifierforthecheckcommandrequired:truebash_command:description:Bashcommandtorunandcommentfromrequired:trueruns:using:&amp;#34;composite&amp;#34;steps:- uses:./.github/actions/job_urlid:job_urlwith:step_name:${{inputs.identifier}}- id:check_commandname:${{inputs.identifier}}shell:bashrun:| set +eecho&amp;#34;COMMAND_OUT&amp;lt;&amp;lt;EOF&amp;#34;|tee&amp;#34;$GITHUB_OUTPUT&amp;#34;${{inputs.bash_command}}2&amp;gt;&amp;amp;1|sed&amp;#39;s/$/\\n/g&amp;#39;|tr-d&amp;#39;\n&amp;#39;|tee-a&amp;#34;$GITHUB_OUTPUT&amp;#34;;COMMAND_STATUS=$?(echo;echo&amp;#34;EOF&amp;#34;)|tee-a&amp;#34;$GITHUB_OUTPUT&amp;#34;exit$COMMAND_STATUS- name:Removepreviouscommandcommentsuses:./.github/actions/remove_commentsif:always()with:identifier:${{inputs.identifier}}- name:PRcommentcommandfailureuses:actions/github-script@v7if:always()&amp;amp;&amp;amp;steps.check_command.outcome==&amp;#39;failure&amp;#39;with:script:| github.rest.issues.createComment({owner:context.repo.owner,repo:context.repo.repo,issue_number:context.issue.number,body:&amp;#39;[${{inputs.identifier}}](${{steps.job_url.outputs.job_url}})\n\n```\n${{steps.check_command.outputs.COMMAND_OUT}}```&amp;#39;})Note that this uses separate actions to get the job URL and to remove previous PR comments.</description>
    </item>
    
    <item>
      <title>Github Action to remove previous job PR comments</title>
      <link>https://notestoself.dev/posts/github-action-remove-previous-job-pr-comments/</link>
      <pubDate>Mon, 23 Sep 2024 14:03:53 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/github-action-remove-previous-job-pr-comments/</guid>
      <description>Here&amp;rsquo;s a Github Action that finds and removes PR comments based on a string identifier. This is useful for Github Action workflows that post comments to PRs, as it clears up out-of-date comments from earlier workflow runs.
name:Command&amp;amp;Commentdescription:RunabashcommandandcommentfailureoutputbacktoPRinputs:identifier:description:Singlewordidentifierforcommentstoremoverequired:trueruns:using:&amp;#34;composite&amp;#34;steps:- name:Removepreviousjobcommentsuses:actions/github-script@v7with:script:| const pr_comments = await github.rest.issues.listComments({owner:context.repo.owner,repo:context.repo.repo,issue_number:context.issue.number,})awaitPromise.all(pr_comments.data.filter(comment=&amp;gt;comment.body?.startsWith(&amp;#34;[${{inputs.identifier}}]&amp;#34;)).map(comment=&amp;gt;github.rest.issues.deleteComment({owner:context.repo.owner,repo:context.repo.repo,issue_number:context.issue.number,comment_id:comment.id,})))It removes comments on the PR it is running against if the comment begins with [{identifier}]. If other comments happen to begin with that then they will also be removed.</description>
    </item>
    
    <item>
      <title>Capturing multiline output and exit status in a Github Action with bash</title>
      <link>https://notestoself.dev/posts/github-action-multiline-output-bash/</link>
      <pubDate>Mon, 23 Sep 2024 13:02:14 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/github-action-multiline-output-bash/</guid>
      <description>You can capture output from a Github Action by echoing a variable name to the file specified by the &amp;quot;$GITHUB_OUTPUT&amp;quot; variable:
echo &amp;#34;foobar=&amp;#39;here is some data&amp;#39;&amp;#34; | tee -a &amp;#34;$GITHUB_OUTPUT&amp;#34; This is a bit trickier with multiline output. You can use the bash EOF syntax for multiline strings:
FOOBAR&amp;lt;&amp;lt;EOF here is some multiline data EOF The easiest way to use bash multiline strings in a Github Action is to use a bash command group with curly braces.</description>
    </item>
    
    <item>
      <title>Github Action to get the current job URL</title>
      <link>https://notestoself.dev/posts/github-action-job-url/</link>
      <pubDate>Mon, 23 Sep 2024 12:13:40 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/github-action-job-url/</guid>
      <description>Here&amp;rsquo;s a Github Action to get the URL to the current job and specific step in the job to open. This is useful for giving users a direct link to specific jobs.
name:GetcurrentjobURLdescription:OutputthecurrentGithubWorkflowJobURLinputs:step_name:description:SpecificjobsteptoincludeintheURLrequired:trueoutputs:job_url:description:&amp;#34;URL to the calling job and specific input step&amp;#34;value:${{steps.get_job_url.outputs.job_url}}runs:using:&amp;#34;composite&amp;#34;steps:- id:get_job_urlenv:GH_TOKEN:${{github.token}}shell:bashrun:| echo &amp;#34;job_url=$(gh run --repo ${{github.repository}} view ${{github.run_id}} \--jsonjobs\--jq&amp;#39;.jobs[] | select(.name == &amp;#34;${{github.job}}&amp;#34;) | .url, ( .steps[] | select(.name == &amp;#34;${{inputs.step_name}}&amp;#34;) | &amp;#34;#step:\(.number):1&amp;#34; )&amp;#39;\|tr-d&amp;#34;\n&amp;#34;)&amp;#34; | tee &amp;#34;$GITHUB_OUTPUT&amp;#34;The key part is this bash script which uses the gh CLI tool&amp;rsquo;s --jq option to extract the URL:</description>
    </item>
    
    <item>
      <title>Ensure Python async generator and context manager closes with contextlib.aclosing</title>
      <link>https://notestoself.dev/posts/python-asyncio-aiofiles-resourcewarning-contextlib-aclosing/</link>
      <pubDate>Wed, 18 Sep 2024 12:42:26 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/python-asyncio-aiofiles-resourcewarning-contextlib-aclosing/</guid>
      <description>I noticed a ResourceWarning like this when using aiofiles to iterate lines of a file in an async context manager from aiofiles.open:
ResourceWarning: unclosed file &amp;lt;_io.TextIOWrapper name=&amp;#39;.../numbers.tmp&amp;#39; mode=&amp;#39;r&amp;#39; encoding=&amp;#39;UTF-8&amp;#39;&amp;gt; This is only a warning, so by default it won&amp;rsquo;t error out of the Python program. I try to have pytest filterwarnings set to error whenever possible, so that any warnings during tests will cause the test to error out. That&amp;rsquo;s how I spotted this issue.</description>
    </item>
    
    <item>
      <title>Python asyncio ensure event loop utility function</title>
      <link>https://notestoself.dev/posts/python-asyncio-ensure-event-loop/</link>
      <pubDate>Sun, 15 Sep 2024 10:51:12 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/python-asyncio-ensure-event-loop/</guid>
      <description>Here&amp;rsquo;s a utility function that safely ensures an event loop is running in a Python asyncio application.
This is useful because of the discrepancy in expectations between the asyncio library and how it ends up being used in the real world in a lot of applications. The asyncio library reasonably expects users to be in control of the event loop for the whole application lifecycle, but this becomes complicated in various situations:</description>
    </item>
    
    <item>
      <title>Doozy</title>
      <link>https://notestoself.dev/work/doozy/</link>
      <pubDate>Sun, 01 Sep 2024 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/doozy/</guid>
      <description>I&amp;rsquo;ve been helping to build Doozy&amp;rsquo;s product as a freelance Software Engineer, using GCP, Firebase and TypeScript.</description>
    </item>
    
    <item>
      <title>ElseWhen</title>
      <link>https://notestoself.dev/work/elsewhen/</link>
      <pubDate>Mon, 15 Jul 2024 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/elsewhen/</guid>
      <description>I worked as a Software Engineer on a contract basis for ElseWhen, using asyncio, CloudBuild, CloudRun, GCP, Gemini, Google Cloud Functions, LangChain, LLMs, OpenAI, PostgreSQL, Python, SQL, Terraform and other technologies.</description>
    </item>
    
    <item>
      <title>Omnipresent</title>
      <link>https://notestoself.dev/work/omnipresent/</link>
      <pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/omnipresent/</guid>
      <description>I worked as a Software Engineer on a contract basis for Omnipresent via YLD, using asyncio, AWS, AWS Lambda, AWS SAM, Docker, ECS, FastAPI, PostgreSQL, Python, SNS, SQL, SQS, Terraform and other technologies.</description>
    </item>
    
    <item>
      <title>Minify JSON clipboard contents back to clipboard with Bash and Python</title>
      <link>https://notestoself.dev/posts/ubuntu-bash-python-minify-json-clipboard-cli/</link>
      <pubDate>Tue, 18 Jul 2023 13:18:51 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/ubuntu-bash-python-minify-json-clipboard-cli/</guid>
      <description>Here&amp;rsquo;s a Bash one-liner to minify JSON in the current contents of the clipboard and replace the clipboard contents with the minified JSON:
xclip -selection clipboard -o | python3 -c &amp;#34;import sys;import json;print(json.dumps(json.loads(sys.stdin.read()),separators=(\&amp;#34;,\&amp;#34;,\&amp;#34;:\&amp;#34;)))&amp;#34; | tee &amp;gt;(xclip -selection clipboard) The equivalent on Mac is:
pbpaste | python3 -c &amp;#34;import sys;import json;print(json.dumps(json.loads(sys.stdin.read()),separators=(\&amp;#34;,\&amp;#34;,\&amp;#34;:\&amp;#34;)))&amp;#34; | pbcopy To set it as an alias in Linux (mcj for &amp;ldquo;minify clipboard json&amp;rdquo;):
alias mcj=&amp;#39;xclip -selection clipboard -o | python3 -c &amp;#34;import sys;import json;print(json.</description>
    </item>
    
    <item>
      <title>JSON encode clipboard contents back to clipboard with Bash and Python</title>
      <link>https://notestoself.dev/posts/ubuntu-bash-python-json-encode-clipboard-cli/</link>
      <pubDate>Tue, 18 Jul 2023 12:38:51 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/ubuntu-bash-python-json-encode-clipboard-cli/</guid>
      <description>Here&amp;rsquo;s a Bash one-liner to JSON-encode the current contents of the clipboard and replace the clipboard contents with the JSON-encoded output:
xclip -o | python -c &amp;#39;import sys;import json;print(json.dumps(sys.stdin.read().strip()))&amp;#39; | tee &amp;gt;(xclip -selection clipboard) The equivalent on Mac is:
pbpaste | python -c &amp;#39;import sys;import json;print(json.dumps(sys.stdin.read().strip()))&amp;#39; | pbcopy To set it as an alias in Linux (jec for &amp;ldquo;json encode clipboard&amp;rdquo;):
alias jec=&amp;#39;xclip -selection clipboard -o | python -c &amp;#34;import sys;import json;print(json.</description>
    </item>
    
    <item>
      <title>KrakenFlex</title>
      <link>https://notestoself.dev/work/krakenflex/</link>
      <pubDate>Thu, 01 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/krakenflex/</guid>
      <description>I worked as a Software Engineer on a contract basis for KrakenFlex, using asyncio, AWS, AWS Lambda, AWS SAM, CloudFormation, DynamoDB, Python, SNS, SQS and other technologies.</description>
    </item>
    
    <item>
      <title>AWS SAM CloudFormation SSM Parameter secure string not supported workaround</title>
      <link>https://notestoself.dev/posts/aws-sam-cloudformation-ssm-parameter-secure-string-not-supported-workaround/</link>
      <pubDate>Tue, 16 May 2023 18:23:42 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/aws-sam-cloudformation-ssm-parameter-secure-string-not-supported-workaround/</guid>
      <description>It&amp;rsquo;s surprisingly difficult to use AWS SSM Parameter secure strings in CloudFormation templates.
If you try and have CloudFormation fetch the value of the SSM Parameter as a AWS::SSM::Parameter::Value&amp;lt;String&amp;gt; type, you&amp;rsquo;ll get this error:
An error occurred (ValidationError) when calling the CreateStack operation: Parameters [/foobar/foo_param] referenced by template have types not supported by CloudFormation. If you try and have a CloudFormation parameter of type AWS::SSM::Parameter::Value&amp;lt;SecureString&amp;gt;, you&amp;rsquo;ll get this error:
An error occurred (ValidationError) when calling the CreateStack operation: Template format error: Unrecognized parameter type: SecureString If you try and use the special resolve function in the CloudFormation template, like this:</description>
    </item>
    
    <item>
      <title>AWS Lambda Python SAM build with container and CodeArtifact poetry auth</title>
      <link>https://notestoself.dev/posts/aws-lambda-python-poetry-codeartifact-auth-sam-container-build/</link>
      <pubDate>Sun, 16 Apr 2023 18:23:42 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/aws-lambda-python-poetry-codeartifact-auth-sam-container-build/</guid>
      <description>It&amp;rsquo;s often beneficial to use Docker to build AWS SAM Lambda Function images. Projects using AWS SAM often also use AWS CodeArtifact to manage private libraries, and poetry to manage Python dependencies.
This combination can make it a little bit tricky to get the index auth working during the Docker build.
Your Dockerfile for building the AWS SAM Lambda Function might look like this:
FROMpublic.ecr.aws/lambda/python:3.12ENV PIP_DISABLE_PIP_VERSION_CHECK=on \  PYTHONDONTWRITEBYTECODE=1 \  PYTHONUNBUFFERED=1 COPY requirements.</description>
    </item>
    
    <item>
      <title>Converting PDF slides to PNG then CSV to bulk import into Anki</title>
      <link>https://notestoself.dev/posts/pdf-png-csv-anki/</link>
      <pubDate>Fri, 17 Mar 2023 10:40:47 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/pdf-png-csv-anki/</guid>
      <description>I had a large slide deck in PDF format and wanted to add the individual slides to Anki to revise from. There is some benefit to the learning process in doing this manually, but it was more practical to study the slides first and then automate the import into Anki.
The final bash command to produce an importable CSV file for Anki looks like this:
pdftoppm my_slides.pdf my_slide \  -progress -png -f 20 -l 923 -rx 60 -ry 60 &amp;amp;&amp;amp; \  cp .</description>
    </item>
    
    <item>
      <title>Unit testing a Python AWS Lambda handler function that uses asyncio with its own event loop</title>
      <link>https://notestoself.dev/posts/python-pytest-asyncio-aws-lambda-handler-event-loop/</link>
      <pubDate>Fri, 17 Feb 2023 10:40:47 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-pytest-asyncio-aws-lambda-handler-event-loop/</guid>
      <description>If you&amp;rsquo;re using asyncio in an AWS Lambda, the Lambda handler function has to manage the asyncio event loop inside the Lambda, as the handler function itself cannot be an async function.
This is fairly easy to do by having the top-level Lambda handler function use asyncio.run to call an asynchronous function that does the actual work:
import asyncio async def foobar_handler() -&amp;gt; str: return &amp;#34;foobar&amp;#34; def lambda_handler(event, context) -&amp;gt; str: return asyncio.</description>
    </item>
    
    <item>
      <title>Python AWS boto moto SNS Invalid Parameter Exception: &#39;not enough values to unpack (expected 6, got 1)&#39;</title>
      <link>https://notestoself.dev/posts/python-aws-boto-moto-sns-invalid-parameter-unpack-6-arn/</link>
      <pubDate>Tue, 17 Jan 2023 10:40:47 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-aws-boto-moto-sns-invalid-parameter-unpack-6-arn/</guid>
      <description>I just spent a long time debugging this error I was getting in a unit test using moto to mock out the boto3 SNS client:
botocore.errorfactory.InvalidParameterException: An error occurred (InvalidParameter) when calling the Publish operation: not enough values to unpack (expected 6, got 1) This error comes from the innards of moto, where it splits an ARN on : and assumes there will be six sections to unpack into Python variables there.</description>
    </item>
    
    <item>
      <title>Hyperobjekt</title>
      <link>https://notestoself.dev/work/hyperobjekt/</link>
      <pubDate>Thu, 01 Sep 2022 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/hyperobjekt/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Pytest fixture with optional parameter change inside test case</title>
      <link>https://notestoself.dev/posts/pytest-fixture-optional-change-parameter-inside-test-case/</link>
      <pubDate>Mon, 18 Apr 2022 12:34:32 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/pytest-fixture-optional-change-parameter-inside-test-case/</guid>
      <description>Here&amp;rsquo;s a way of setting up a pytest fixture that sets up a default value automatically for any test case that uses the fixture, but allows calling the fixture from inside the test case to update that value where necessary.
The example is for mocking out API responses with httpx_mock, but this fixture arrangement can be used for any situation where you want a default fixture value that can optionally be modified from within a single test case.</description>
    </item>
    
    <item>
      <title>Python assert_dict_matches recursive test helper function</title>
      <link>https://notestoself.dev/posts/python-assert_dict_matches-recursive-test-helper-function/</link>
      <pubDate>Fri, 18 Mar 2022 12:34:32 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-assert_dict_matches-recursive-test-helper-function/</guid>
      <description>The Jest library for Node.js has a helpful expect.objectContaining function that makes it easy to assert only on the keys of interest in a particular test.
Python&amp;rsquo;s unittest library does have a similar assertDictEqual method, but it asserts on all keys.
I wanted to do be able to easily assert on only the keys of interest in Python tests, so I put together this quick test helper function to do so.</description>
    </item>
    
    <item>
      <title>Hugo asset pipeline security.exec.allow error</title>
      <link>https://notestoself.dev/posts/hugo-asset-pipeline-security.exec.allow-error/</link>
      <pubDate>Tue, 15 Mar 2022 16:19:31 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/hugo-asset-pipeline-security.exec.allow-error/</guid>
      <description>If you hit this error with recent versions of Hugo:
Error: Error building site: BABEL: failed to transform &amp;#34;js/main.js&amp;#34; (application/javascript): access denied: &amp;#34;babel&amp;#34; is not whitelisted in policy &amp;#34;security.exec.allow&amp;#34;; the current security configuration is: You need to allow babel in Hugo&amp;rsquo;s security rules in the config file, like this:
[security] [security.exec] allow = [&amp;#39;^go$&amp;#39;, &amp;#39;^npx$&amp;#39;, &amp;#39;^postcss$&amp;#39;, &amp;#39;^babel$&amp;#39;] </description>
    </item>
    
    <item>
      <title>Export, clipboard copy and echo a token Bash one-liner</title>
      <link>https://notestoself.dev/posts/export-copy-clipboard-echo-token-bash/</link>
      <pubDate>Tue, 15 Feb 2022 16:19:31 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/export-copy-clipboard-echo-token-bash/</guid>
      <description>Here&amp;rsquo;s a handy Bash one-liner to generate a token or some other kind of command output, and do three things at once with it:
 Export it as an environment variable in the shell. Copy it to the clipboard. Echo it to the terminal.  export FOOBAR_TOKEN=$(foobar-token-command --foobar &amp;#34;foobar&amp;#34;) \  &amp;amp;&amp;amp; echo -n $FOOBAR_TOKEN | tee &amp;gt;(xclip -selection clipboard) I find this is handy for things like security tokens which I often want as an environment variable in the shell as well as being able to paste them into other tools such as HTTP testing apps.</description>
    </item>
    
    <item>
      <title>Hugo new post Bash function</title>
      <link>https://notestoself.dev/posts/hugo-new-post-bash-function/</link>
      <pubDate>Sat, 15 Jan 2022 12:52:06 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/hugo-new-post-bash-function/</guid>
      <description>Hugo has a hugo new helper command that can generate new content files for you. It doesn&amp;rsquo;t quite do what I want, though, so I&amp;rsquo;ve added this quick Bash function to my .profile to make it easier to use:
hnp() { hugo new &amp;#34;${2:-posts}/$(date +%Y)/$(date +%m)/${1}/index.md&amp;#34; } All it does is create a new post file in its own content directory at a date-based path, which is the structure I use for my Hugo sites.</description>
    </item>
    
    <item>
      <title>AWS DocumentDB data source in JetBrains DataGrip</title>
      <link>https://notestoself.dev/posts/jetbrains-datagrip-aws-documentdb-connection/</link>
      <pubDate>Thu, 06 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/jetbrains-datagrip-aws-documentdb-connection/</guid>
      <description>I&amp;rsquo;m currently working on a project for Kensio Software using AWS DocumentDB, and wanted to access the database for development using DataGrip. Here are some quick notes on how to set up a DocumentDB database as a data source in DataGrip.
Getting DocumentDB connection details from AWS console It&amp;rsquo;s probably easiest to get the connection details for the DocumentDB cluster from the AWS Console.
At the time of writing, the URL for the DocumentDB cluster looks like this:</description>
    </item>
    
    <item>
      <title>X2X Media</title>
      <link>https://notestoself.dev/work/x2x-media/</link>
      <pubDate>Sat, 01 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/x2x-media/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Python iterator chunk generator function</title>
      <link>https://notestoself.dev/posts/python-iterator-chunk-generator-function/</link>
      <pubDate>Sat, 23 Oct 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-iterator-chunk-generator-function/</guid>
      <description>Here&amp;rsquo;s a versatile Python function that splits any iterator into chunks of size n as a generator. It can take simple iterators such as lists, but also take a generator and chunk it without unwinding the whole thing up front.
This is quite useful when dealing with a database cursor, for example. You can iterate through the cursor in chunks of size n without having to load all of the objects into memory at once.</description>
    </item>
    
    <item>
      <title>Simple example of a floating point precision error for monetary amounts</title>
      <link>https://notestoself.dev/posts/simple-example-floating-point-precision-error-monetary-amount/</link>
      <pubDate>Sat, 23 Oct 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/simple-example-floating-point-precision-error-monetary-amount/</guid>
      <description>Most software engineers are aware that floating point numbers are not suitable for monetary amounts, or any amount requiring reliable decimal precision.
It&amp;rsquo;s nice to have a quick and simple example of this, so here&amp;rsquo;s an operation that will break with floating point numbers in many languages including JavaScript:
1.01 - 0.42 // → 0.5900000000000001 In JavaScript and TypeScript, the big.js library is good for decimal operations.
In SQL, it&amp;rsquo;s best to store monetary and other decimal amounts as DECIMAL, e.</description>
    </item>
    
    <item>
      <title>Explicit types are a better habit than type inference</title>
      <link>https://notestoself.dev/posts/explicit-types-better-habit-type-inference-typescript/</link>
      <pubDate>Thu, 02 Sep 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/explicit-types-better-habit-type-inference-typescript/</guid>
      <description>TypeScript has a handy type inference feature where the compiler will automatically determine types in most situations.
For example:
function fooBar(): number { return 42; } const x = fooBar(); // x&amp;#39;s type is infered as number This is quite useful and potentially allows for tidier, more readable code. However, I would actually suggest that avoiding type inference and preferring explicit types is a good habit.
For trivial examples like the above, it probably is overkill to declare x as const x: number = 42.</description>
    </item>
    
    <item>
      <title>Google Cloud Platform Logging Query Syntax Exploration Fu</title>
      <link>https://notestoself.dev/posts/gcp-google-cloud-platform-logging-query-syntax-exploration-fu/</link>
      <pubDate>Wed, 04 Aug 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/gcp-google-cloud-platform-logging-query-syntax-exploration-fu/</guid>
      <description>Recently I&amp;rsquo;ve been working in Google Cloud Platform (GCP) in a highly distributed system that is partitioned into many different services with various deployment strategies. A lot of it runs in Google Kubernetes Engine (GKE), a lot of it runs in Google Cloud Functions (GCF), and a lot of it runs in other bits and pieces of GCP infrastructure. Even the areas that are managed via GKE and Cloud Functions are heterogenous, with many idiosyncratic infrastructure configurations.</description>
    </item>
    
    <item>
      <title>My Jest mock cheatsheet for TypeScript</title>
      <link>https://notestoself.dev/posts/jest-mock-cheatsheet-typescript/</link>
      <pubDate>Wed, 23 Jun 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/jest-mock-cheatsheet-typescript/</guid>
      <description>I find myself looking up past projects to remember how to mock various things with Jest in TypeScript, so I&amp;rsquo;ve started writing up notes on this page to gather them in one place.
Mock a function without early referencing Because of the way Jest hoists mocks, it&amp;rsquo;s easy to trigger a footgun where Jest complains about referencing something before initialisation.
You can usually avoid that by wrapping the reference in an outer function call, so that it&amp;rsquo;s not immediately referenced:</description>
    </item>
    
    <item>
      <title>Udemy Course Progress JS Bookmarklet</title>
      <link>https://notestoself.dev/posts/udemy-course-progress-js-bookmarklet/</link>
      <pubDate>Sat, 05 Jun 2021 12:16:20 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/udemy-course-progress-js-bookmarklet/</guid>
      <description>The Udemy video course UI doesn&amp;rsquo;t give you a lot of information about your progress through a course. I find it helpful to be able to see progress and the amount of time remaining in a course, so I made this JavaScript bookmarklet to display some figures about progress through a Udemy course.
You need to be on the Udemy video player page when you trigger the bookmarklet. It will display progress stats in a browser alert.</description>
    </item>
    
    <item>
      <title>Bulb</title>
      <link>https://notestoself.dev/work/bulb/</link>
      <pubDate>Tue, 01 Jun 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/bulb/</guid>
      <description>I worked as a Software Engineer on a contract basis for Bulb via YLD, using Kubernetes, Terraform, GCP, TypeScript, Node.js, Python, MySQL, PostgreSQL and other technologies.</description>
    </item>
    
    <item>
      <title>Sweeping complexity under the infrastructure carpet</title>
      <link>https://notestoself.dev/posts/sweep-complexity-under-infrastructure-carpet/</link>
      <pubDate>Sun, 23 May 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/sweep-complexity-under-infrastructure-carpet/</guid>
      <description>Some projects seem to get into the habit of keeping the application code relatively clean and simple by hiding complexity in the infrastructure layer. My made up term for this is sweeping complexity under the infrastructure carpet. It feels appropriate that this is a bit of a mouthful to say.
This anti-pattern seems to creep in via an attitude that complexity in infrastructure &amp;ldquo;doesn&amp;rsquo;t count&amp;rdquo;, so it&amp;rsquo;s OK to let it grow and fester there while the application code appears to be more healthy.</description>
    </item>
    
    <item>
      <title>MySQL timestamp columns default to ON UPDATE CURRENT TIMESTAMP</title>
      <link>https://notestoself.dev/posts/mysql-timestamp-default-update-current-timestamp/</link>
      <pubDate>Tue, 18 May 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/mysql-timestamp-default-update-current-timestamp/</guid>
      <description>A quick note about a footgun in MySQL with timestamp colummns, which I happened to notice when using migrations in the knex.js library.
TLDR: older versions of MySQL (&amp;lt;= 5.7) default to adding a DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP clause to timestamp columns unless otherwise specified.
Documentation here: https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp
For example, if you create a table like this in older versions of MySQL:
CREATE TABLE `foobar_table` ( `foobar_column` timestamp ) You&amp;rsquo;ll actually get a column like this:</description>
    </item>
    
    <item>
      <title>TypeScript branded string type utility for type safety</title>
      <link>https://notestoself.dev/posts/typescript-branded-string-type-utility-safety/</link>
      <pubDate>Wed, 28 Apr 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/typescript-branded-string-type-utility-safety/</guid>
      <description>Here&amp;rsquo;s an easy way to get branded string types in TypeScript. This allows the compiler to enforce minimal type safety on strings with an identity such as a User ID or Post ID.
export type Brand&amp;lt;K, T extends string&amp;gt; = K &amp;amp; { [P in T]: never }; This can be helpful when you have multiple kinds of IDs that are all strings at runtime, and you want to avoid accidentally passing them in the wrong place.</description>
    </item>
    
    <item>
      <title>Static site full text search with Hugo, S3, Python and JS</title>
      <link>https://notestoself.dev/posts/static-site-full-text-search-hugo-s3-python-js/</link>
      <pubDate>Mon, 26 Apr 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/static-site-full-text-search-hugo-s3-python-js/</guid>
      <description>Static sites are great for their speed and ease of deployment at low cost. One thing they might seem to miss out on is full-text search, but you can actually achieve reasonable full-text search functionality on a static site with a fairly simple solution.
This approach can be expanded in various ways to offer more functionality, but the basic core of it is:
 Use Python to iterate all of the searchable items, and create an index.</description>
    </item>
    
    <item>
      <title>Selecting the best available social meta data image in Hugo</title>
      <link>https://notestoself.dev/posts/hugo-default-page-resource-social-image/</link>
      <pubDate>Fri, 23 Apr 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/hugo-default-page-resource-social-image/</guid>
      <description>Here&amp;rsquo;s a snippet of Hugo template language that I use to try and select the best social meta data image for each page on a static site:
{{ if isset .Params &amp;#34;meta_image&amp;#34; }} &amp;lt;meta property=&amp;#34;og:image&amp;#34; content=&amp;#34;{{ .Params.meta_image | absURL }}&amp;#34;/&amp;gt; &amp;lt;meta name=&amp;#34;twitter:image&amp;#34; content=&amp;#34;{{ .Params.meta_image | absURL }}&amp;#34;&amp;gt; &amp;lt;link rel=&amp;#34;image_src&amp;#34; href=&amp;#34;{{ .Params.meta_image | absURL }}&amp;#34;/&amp;gt; &amp;lt;meta itemprop=&amp;#34;image&amp;#34; content=&amp;#34;{{ .Params.meta_image | absURL }}&amp;#34;&amp;gt; {{ else if (.Resources.ByType &amp;#34;image&amp;#34;) | len }} {{ $firstImage := index (.</description>
    </item>
    
    <item>
      <title>Connecting to an old modp1024 L2TP Ipsec VPN on Ubuntu 21.04</title>
      <link>https://notestoself.dev/posts/ubuntu-21.04-connect-old-modp1024-l2tp-ipsec-vpn/</link>
      <pubDate>Thu, 01 Apr 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/ubuntu-21.04-connect-old-modp1024-l2tp-ipsec-vpn/</guid>
      <description>A client of Kensio Software is using an L2TP VPN with Ipsec that&amp;rsquo;s using an obsolete DH2 (modp1024) algorithm. This algorithm was considered insecure as far back as 2001, and has now been deprecated in encryption libraries such as libreswan.
Most of the permanent engineers at the client are on MacOS, which still supports DH2 / modp1024 algorithm out of the box. I was able to get the VPN connected on a Macbook, but still wanted to try and get it working on Ubuntu as that&amp;rsquo;s my main working OS.</description>
    </item>
    
    <item>
      <title>Beware of SQL averages over dates with empty days</title>
      <link>https://notestoself.dev/posts/beware-sql-average-empty-days/</link>
      <pubDate>Tue, 23 Feb 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/beware-sql-average-empty-days/</guid>
      <description>It&amp;rsquo;s pretty common to use SQL to extract sales summaries from tables that represent sales orders as rows. These kinds of queries often use the AVG() aggregate function to summarise the sales volume. There is a major pitfall when doing this, though!
We might run a sub-query like this to get daily revenue totals from the orders table:
SELECT order_date, SUM(order_total) AS total_revenue FROM orders GROUP BY order_date Those aggregated revenue query results might look something like this:</description>
    </item>
    
    <item>
      <title>Generating a range of dates in MySQL</title>
      <link>https://notestoself.dev/posts/mysql-generate-date-range/</link>
      <pubDate>Sat, 23 Jan 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/mysql-generate-date-range/</guid>
      <description>It&amp;rsquo;s often useful to have a range of contiguous dates when building SQL queries, for example by using a left join to ensure that data from other tables that might have missing dates will get a null value in those rows instead of skipping the rows entirely.
Postgres has its wonderful generate_series function which can solve a lot of these problems. Unfortunately MySQL doesn&amp;rsquo;t have the same function.
You can generate a range of contiguous dates in MySQL with a query like this:</description>
    </item>
    
    <item>
      <title>Simple Boto3 mock in Python with Pytest monkeypatch</title>
      <link>https://notestoself.dev/posts/simple-boto3-mock-pytest-monkeypatch/</link>
      <pubDate>Wed, 20 Jan 2021 17:13:05 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/simple-boto3-mock-pytest-monkeypatch/</guid>
      <description>If you&amp;rsquo;re anything like me, then you might find mocking dependencies in Python tests quite fiddly and unintuitive (it&amp;rsquo;s certainly less straightforward than Jest in TypeScript).
I needed to mock one part of the Python boto3 library for AWS, and I got it working using Pytest&amp;rsquo;s monkeypatch.
This shows a test for fetching a value from a JSON-encoded secret string in AWS Secrets Manager, but you can extrapolate from this roughly how to mock out other boto3 APIs in a similar way.</description>
    </item>
    
    <item>
      <title>Big Sister, Little Sister, Red Sister</title>
      <link>https://notestoself.dev/reading/big-sister-little-sister-red-sister/</link>
      <pubDate>Fri, 01 Jan 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/big-sister-little-sister-red-sister/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Stubborn Attachments</title>
      <link>https://notestoself.dev/reading/stubborn-attachments/</link>
      <pubDate>Fri, 01 Jan 2021 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/stubborn-attachments/</guid>
      <description></description>
    </item>
    
    <item>
      <title>ESLint custom rules are useful</title>
      <link>https://notestoself.dev/posts/eslint-custom-rules/</link>
      <pubDate>Wed, 23 Dec 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/eslint-custom-rules/</guid>
      <description>Here&amp;rsquo;s an example of a custom eslint rule that detects usage of the knex library in a unit test file and reports it as a linter error.
module.exports = { rules: { &amp;#39;no-db-unit-tests&amp;#39;: { create: function (context) { return { ImportDeclaration(node) { const fileName = context.getFilename(); if (node.specifiers) { node.specifiers.map((item) =&amp;gt; { if ( item.local.name === &amp;#39;knex&amp;#39; &amp;amp;&amp;amp; fileName.includes(&amp;#39;unit.spec&amp;#39;) ) { return context.report( node, item.loc, `Do not use ${item.local.name}in unit test file ${fileName}`, ); } return null; }); } }, }; }, }, }, }; </description>
    </item>
    
    <item>
      <title>Where&#39;s My Flying Car?</title>
      <link>https://notestoself.dev/reading/where-s-my-flying-car/</link>
      <pubDate>Tue, 01 Dec 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/where-s-my-flying-car/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Jest mock error &#34;ReferenceError Cannot access before initialization&#34;</title>
      <link>https://notestoself.dev/posts/jest-mock-reference-error-cannot-access-before-initialization/</link>
      <pubDate>Wed, 18 Nov 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/jest-mock-reference-error-cannot-access-before-initialization/</guid>
      <description>When using Jest mocks, it&amp;rsquo;s easy to hit this error:
ReferenceError: Cannot access &amp;#39;...&amp;#39; before initialization This is a bit vague and difficult to figure out if you don&amp;rsquo;t know what the issue is.
This error is due to the way Jest hoists mocks to the top.
For example if you have this in a test file:
const fooServiceFooFunction = jest.fn(); jest.mock(&amp;#34;@foo-provider/some-library&amp;#34;, () =&amp;gt; ({ fooService: () =&amp;gt; ({ foobar: fooServiceFooFunction, }), })); It looks like it should work fine as the fooServiceFooFunction mock function is initialised before jest.</description>
    </item>
    
    <item>
      <title>Ask your interview candidates to ask you an algorithm question in return</title>
      <link>https://notestoself.dev/posts/interview-candidate-should-ask-interviewer-algorithm-question/</link>
      <pubDate>Thu, 12 Nov 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/interview-candidate-should-ask-interviewer-algorithm-question/</guid>
      <description>Here&amp;rsquo;s a potential idea to improve the technical interviews that you run: if you use algorithm questions in interviews, ask candidates to ask you an algorithm question as well.
I think it could be beneficial to tell candidates that they should prepare an algorithm question to pose to their interviewer and work through together in the same way. This could have a similar vibe to the general questions at the end of the interview, when we ask candidates if they have any questions for us.</description>
    </item>
    
    <item>
      <title>Uploading Etsy listing images in PHP with Oauth</title>
      <link>https://notestoself.dev/posts/etsy-api-php-oauth-listing-image-upload/</link>
      <pubDate>Fri, 23 Oct 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/etsy-api-php-oauth-listing-image-upload/</guid>
      <description>After figuring out how to upload Etsy listing images in Python for one project, I needed to do the same thing in PHP for another. Despite Etsy itself being implemented in PHP (as far as I know), and the examples in their documentation being in PHP, this turned out to be tricky.
First up, the example for uploading listing images in Etsy&amp;rsquo;s documentation is plain wrong. It doesn&amp;rsquo;t work. Don&amp;rsquo;t waste any more time trying to get that example to work.</description>
    </item>
    
    <item>
      <title>Easy encrypted git with gcrypt</title>
      <link>https://notestoself.dev/posts/easy-encrypted-git-gcrypt-gnupg/</link>
      <pubDate>Sun, 23 Aug 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/easy-encrypted-git-gcrypt-gnupg/</guid>
      <description>I&amp;rsquo;ve been looking at options for keeping personal notes and diary entries stored securely and synced across devices. An encrypted Git repo in Github seems like a good potential option for this.
It turns out this is pretty straightforward to achieve using git-remote-gcrypt. This sets up a Git remote that is encrypted while the local repository is kept in plaintext and used normally.
Check if you have a GPG key already:</description>
    </item>
    
    <item>
      <title>Refactoring Terraform state into separate files in GCP GCS</title>
      <link>https://notestoself.dev/posts/refactor-terraform-state-files-gcp-gcs/</link>
      <pubDate>Sun, 23 Aug 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/refactor-terraform-state-files-gcp-gcs/</guid>
      <description>Recently I needed to refactor some Terraform configuration, which had grown into quite a large single state file, into smaller separate directories with their own state files.
This is a little bit tricky but the Terraform CLI tool helps a lot.
The first thing is to get the existing single state file JSON:
terraform state pull | tee terraform.tfstate This should work for any storage backend.
Alternatively you can use gsutil to copy the Terraform state file from a GCS bucket:</description>
    </item>
    
    <item>
      <title>The Great Successor</title>
      <link>https://notestoself.dev/reading/the-great-successor/</link>
      <pubDate>Sat, 01 Aug 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-great-successor/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Raw HTML shortcode in Hugo</title>
      <link>https://notestoself.dev/posts/raw-html-shortcode-hugo/</link>
      <pubDate>Tue, 28 Jul 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/raw-html-shortcode-hugo/</guid>
      <description>Here&amp;rsquo;s a little Hugo shortcode for including raw HTML within your Markdown content.
Just create a shortcode file at e.g. layouts/shortcodes/rawhtml.html with this content:
{{.Inner}} Now in your Markdown content you can include whatever raw HTML you want, e.g.:
{{&amp;lt; rawhtml &amp;gt;}} &amp;lt;button class=&amp;#34;buttonFromMarkdown&amp;#34;&amp;gt;Click Me!&amp;lt;/button&amp;gt; &amp;lt;script&amp;gt; document.querySelector(&amp;#34;.buttonFromMarkdown&amp;#34;).onclick = function (event) { console.log(&amp;#34;Clickety click&amp;#34;); event.target.style = &amp;#34;background-color: #77dd77&amp;#34;; }; &amp;lt;/script&amp;gt; &amp;lt;style&amp;gt; .buttonFromMarkdown { padding: 1em; cursor: pointer; } &amp;lt;/style&amp;gt; {{&amp;lt; /rawhtml &amp;gt;}} Which results in:</description>
    </item>
    
    <item>
      <title>How I get more benefit from technical reading with Anki</title>
      <link>https://notestoself.dev/posts/how-benefit-technical-reading-anki/</link>
      <pubDate>Sun, 26 Jul 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/how-benefit-technical-reading-anki/</guid>
      <description>Anki is one of my favourite pieces of software ever, and it&amp;rsquo;s great to see that it seems to be getting more and more attention as a valuable tool.
I often do a kind of learning-and-note-taking exercise with Anki when reading technical content, which I&amp;rsquo;m writing up here.
It&amp;rsquo;s not complicated or dogmatic, and the current version boils down to three tactics:
 Cloze deletion for easy conversion of text into notes.</description>
    </item>
    
    <item>
      <title>Pivot joining 19 million lines of CSV with unix tools</title>
      <link>https://notestoself.dev/posts/pivot-join-19-million-lines-csv-unix-tools/</link>
      <pubDate>Thu, 11 Jun 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/pivot-join-19-million-lines-csv-unix-tools/</guid>
      <description>I&amp;rsquo;m participating in Kodoeba, an open hackathon around the Tatoeba example sentence project.
My plan is to incorporate Mandarin-English example sentence pairs from Tatoeba into the Chinese example sentence search on Chinese Boost, mainly to allow searching them via pinyin with or without tone-marks.
The first challenge was actually to extract the Mandarin-English example sentence pairs from Tatoeba&amp;rsquo;s data files.
The data is structured in three separate TSV files (tab-separated values, equivalent to CSV):</description>
    </item>
    
    <item>
      <title>Productivity with random numbers</title>
      <link>https://notestoself.dev/posts/productivity-random-numbers/</link>
      <pubDate>Mon, 01 Jun 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/productivity-random-numbers/</guid>
      <description>One little productivity trick I use is to select tasks or work with a random number. The simplest form is numbering a list of things you could do, then selecting one randomly.
This can be applied to various things you might want to work on:
 Items on a todo list. Chapters or pages in a textbook. Tickets in a work-tracking system. A list of projects, with sub-lists of work items.</description>
    </item>
    
    <item>
      <title>Simplicity resistance</title>
      <link>https://notestoself.dev/posts/simplicity-resistance/</link>
      <pubDate>Mon, 01 Jun 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/simplicity-resistance/</guid>
      <description>Here&amp;rsquo;s another term that ties in with the ideas in &amp;ldquo;boring considered beneficial&amp;rdquo;, &amp;ldquo;fee justification&amp;rdquo; and &amp;ldquo;problem maximisation&amp;rdquo;: simplicity resistance.
When something is too easy, we dislike the sensation, and try to make it more complicated in order to soothe that. This is a kind of cognitive dissonance around finding solutions to problems. We think &amp;ldquo;it can&amp;rsquo;t be that easy&amp;rdquo;, &amp;ldquo;it&amp;rsquo;s too good to be true&amp;rdquo; and so on, and start looking for the more complicated solution that the problem at hand surely justifies.</description>
    </item>
    
    <item>
      <title>Deploying a Hugo site with Github Actions, S3, Cloudfront and AWS IAM</title>
      <link>https://notestoself.dev/posts/deploy-hugo-site-github-actions-s3-cloudfront-aws-iam/</link>
      <pubDate>Thu, 28 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/deploy-hugo-site-github-actions-s3-cloudfront-aws-iam/</guid>
      <description>GitHub Actions are a nice way to get &amp;ldquo;push to deploy&amp;rdquo; behaviour for a static site. Here&amp;rsquo;s a quick run-through of how I have it working for Hugo sites being deployed to AWS.
This assumes you have created an S3 bucket and Cloudfront distribution for the site, and have pointed the domain name to the Cloudfront distribution.
It&amp;rsquo;s best to have an isolated IAM user for deploying with minimal permissions. Create a new policy in AWS IAM, and give it minimal permissions for deploying the site.</description>
    </item>
    
    <item>
      <title>Does your code read like your conversations?</title>
      <link>https://notestoself.dev/posts/code-like-conversations/</link>
      <pubDate>Thu, 28 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/code-like-conversations/</guid>
      <description>This is a potential rule-of-thumb subjective metric for assessing how well you&amp;rsquo;re directing your software development resources:
 Does your code read like your conversations?
 What this means is, does the code you&amp;rsquo;re writing overlap closely with the conversations you have about the product and the problems it solves?
For example, if you talk about &amp;ldquo;free month of foobar with the XYZ subscription&amp;rdquo;, then it&amp;rsquo;s probably a good sign if those words appear a lot in the code.</description>
    </item>
    
    <item>
      <title>A lack of correlation does imply a lack of causation</title>
      <link>https://notestoself.dev/posts/lack-correlation-no-causation/</link>
      <pubDate>Sat, 23 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/lack-correlation-no-causation/</guid>
      <description>Correlation does not imply causation, but a lack of correlation does imply a lack of causation.
The famous maxim that &amp;ldquo;correlation does not imply causation&amp;rdquo; seems to get over-interpreted into some kind of suggestion that correlation and causation can never be related in any way.
One aspect of that is that a lack of correlation does imply that there is no causation effect going on. If there was a causative relationship between two things, we would expect to see correlation.</description>
    </item>
    
    <item>
      <title>Engineer pain vs user pain</title>
      <link>https://notestoself.dev/posts/engineer-pain-vs-user-pain/</link>
      <pubDate>Mon, 18 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/engineer-pain-vs-user-pain/</guid>
      <description>A lot of software engineering teams talk about &amp;ldquo;pain&amp;rdquo; on a regular basis.
Usually this refers to difficulties around technical debt, design decisions, refactoring and so on. It rarely seems to refer to issues that affect users or the rest of the business.
For example, moving to microservices is often touted as a way to &amp;ldquo;reduce pain&amp;rdquo;. Perhaps we can reduce pain by putting HTTP calls in between different parts of the system, but it&amp;rsquo;s rare that this improves things for users in any way.</description>
    </item>
    
    <item>
      <title>Crystal ball development</title>
      <link>https://notestoself.dev/posts/crystal-ball-development/</link>
      <pubDate>Sat, 16 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/crystal-ball-development/</guid>
      <description>There&amp;rsquo;s a well known software development maxim called YAGNI: &amp;ldquo;You Ain&amp;rsquo;t Gonna Need It&amp;rdquo;. YAGNI is a general rule for deciding whether or not to implement something or add complexity. It&amp;rsquo;s designed to give some resistance against the temptation to spuriously add features or try to solve hypothetical future problems.
Applying a bit of inversion to YAGNI, you get something that might be called &amp;ldquo;crystal ball development&amp;rdquo;. This describes the attempt to make magic predictions about the future and apply them to software development.</description>
    </item>
    
    <item>
      <title>Problem maximisation</title>
      <link>https://notestoself.dev/posts/problem-maximisation/</link>
      <pubDate>Fri, 08 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/problem-maximisation/</guid>
      <description>A common metaphor about the potential dangers of AI is called &amp;ldquo;paperclip maximisation&amp;rdquo;. The idea is that an AI tasked with producing paperclips could figure out that it can produce more paperclips by e.g. taking over the entire galaxy to turn as much matter as possible into paperclips.
Sometimes I wonder if software engineers are prone to a similar issue. We believe we&amp;rsquo;re tasked with solving as many problems as possible, and realise that if we increase the number of problems, then we can increase the number of solutions.</description>
    </item>
    
    <item>
      <title>Python script to automate static Hugo site post images</title>
      <link>https://notestoself.dev/posts/python-script-automate-static-hugo-site-post-images/</link>
      <pubDate>Sun, 03 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-script-automate-static-hugo-site-post-images/</guid>
      <description>It&amp;rsquo;s good to have a general post image on content sites, as it gives more of an identity to each page, and works well for social media sharing images.
While NotesToSelf.dev doesn&amp;rsquo;t tend to have images, my Chinese learning site and satirical tech news site have an image for each post.
Unsplash is a nice source of general royalty-free images that you can use for this if you give attribution.</description>
    </item>
    
    <item>
      <title>Easy social media images and markup with Hugo</title>
      <link>https://notestoself.dev/posts/easy-social-media-images-and-markup-with-hugo/</link>
      <pubDate>Sun, 19 Apr 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/easy-social-media-images-and-markup-with-hugo/</guid>
      <description>Twitter Cards and Facebook&amp;rsquo;s OpenGraph provide semi-standardised markup for describing web content to social media and other aggregators.
This can be quite handy for blogs and other text content, as you can provide default information when your content is shared. This is a nice way to make sure your posts get a reasonable cover image when shared to someone&amp;rsquo;s social timeline.
Here&amp;rsquo;s the Hugo / Go HTML template I&amp;rsquo;m currently using here to add some basic sharing markup to each post as part of the HTML head:</description>
    </item>
    
    <item>
      <title>Abstraction castles</title>
      <link>https://notestoself.dev/posts/abstraction-castles/</link>
      <pubDate>Sat, 18 Apr 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/abstraction-castles/</guid>
      <description>&amp;ldquo;&amp;hellip;some service maintained by one person that&amp;rsquo;s built their little impregnable abstraction castle because nobody else had to work on it so they could just yak-shave and bike-shed with their self.&amp;rdquo; [sic]
 https://news.ycombinator.com/item?id=16201126
That Hacker News comment struck a chord with me, as I think the phrase &amp;ldquo;impregnable abstraction castle&amp;rdquo; perfectly captures something that is commonly encountered in software.
An abstraction castle appears as follows. An area of a codebase seems to develop into a fortification that is resistant to change, apparently designed to defend against all envisioned future scenarios and to keep out every encroachment of the outside world that the designers could imagine.</description>
    </item>
    
    <item>
      <title>Using Laravel signed routes to improve order confirmation security</title>
      <link>https://notestoself.dev/posts/laravel-signed-routes-order-confirmation-security/</link>
      <pubDate>Thu, 16 Apr 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/laravel-signed-routes-order-confirmation-security/</guid>
      <description>The order confirmation page on our 3D cards shop is shown to a customer after they complete checkout, and is also linked to in the confirmation and dispatch emails that they receive.
Importantly, this page is not authenticated; we don&amp;rsquo;t want a heavy sign up / sign in process for customers as that tends to be annoying on other ecommerce systems. I just want to buy some pop-up cards &amp;ndash; why do I need to create an account and remember a password etc?</description>
    </item>
    
    <item>
      <title>Specify city timezones, not timezone offsets</title>
      <link>https://notestoself.dev/posts/specify-city-timezones-not-offsets/</link>
      <pubDate>Sat, 11 Apr 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/specify-city-timezones-not-offsets/</guid>
      <description>Dates and times are notoriously difficult to get right in software. Many of the difficulties come from timezones and how unintuitive they are to think about. This is made worse by timezone offsets that change during the year for the same place, such as British Summer Time.
It&amp;rsquo;s easier to get timezones right when you specify a region/city timezone, such as Europe/London. Trying to think about and specify timezone offsets such as +01:00 is more likely to go wrong.</description>
    </item>
    
    <item>
      <title>A simple, readable, meaningful test style with Jest</title>
      <link>https://notestoself.dev/posts/simple-readable-meaningful-jest-test-style/</link>
      <pubDate>Mon, 06 Apr 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/simple-readable-meaningful-jest-test-style/</guid>
      <description>I usually write tests with a Given, When, Then structure to keep the test focused on documenting and confirming the important behaviour of whatever is being tested. Simple comments are good for this and provide an easy-to-follow visual structure:
test(&amp;#34;some defined behaviour&amp;#34;, () =&amp;gt; { // Given the fandango is combobulated;  // When we discombobulate the fandango;  // Then the fandango should be discombobulated.  }) It&amp;rsquo;s important to keep tests clear and readable.</description>
    </item>
    
    <item>
      <title>Fastmail is a nice multi-domain custom email service</title>
      <link>https://notestoself.dev/posts/fastmail-multi-domain-custom-email-service/</link>
      <pubDate>Sun, 05 Apr 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/fastmail-multi-domain-custom-email-service/</guid>
      <description>I run a few side projects on various domains, and often want to be able to send and receive email on those domains. Over the years I&amp;rsquo;ve used a few different solutions for this: Gmail aliases, Mailgun just for automated sending, AWS SES just for automated sending and AWS Workmail for manual sending and receiving.
AWS Workmail has been fine, but ends up being a bit pricey for this kind of usage at $4 USD / user / month.</description>
    </item>
    
    <item>
      <title>Random element choice in a Hugo HTML template</title>
      <link>https://notestoself.dev/posts/random-choice-hugo-template/</link>
      <pubDate>Fri, 03 Apr 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/random-choice-hugo-template/</guid>
      <description>I run a couple of sites related to my Chinese studies: an East Asian Studies blog, and a Chinese learning resource site.
The content is all free so I have some simple affiliate image links on the pages to cover the bills. These are just straight forward &amp;lt;a&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/a&amp;gt; elements with no tracking or anything obnoxious at all. Even then, I don&amp;rsquo;t want to have too many adverts on a single page as it would still be a bit much.</description>
    </item>
    
    <item>
      <title>(Cross-post) End to end serverless testing with the Driver Pattern</title>
      <link>https://notestoself.dev/posts/cross-post-e2e-serverless-testing-driver-pattern/</link>
      <pubDate>Thu, 02 Apr 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/cross-post-e2e-serverless-testing-driver-pattern/</guid>
      <description>(Cross-posted from https://blog.freetrade.io/end-to-end-serverless-testing-with-the-driver-pattern-bcf2d22bf73a)
In September 2019, Freetrade’s test code consisted of a large number of fast unit tests and a smaller number of end-to-end API tests. These make real HTTP requests against the platform and assert on the results.
The unit tests confirm that individual classes are behaving correctly and improve the design of interfaces, while the API tests confirm that higher-level business logic is working correctly. This is not a bad combination and at the time already gave us decent test coverage of a large proportion of our platform.</description>
    </item>
    
    <item>
      <title>Choosing dates and times in tests</title>
      <link>https://notestoself.dev/posts/choosing-dates-times-tests/</link>
      <pubDate>Mon, 30 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/choosing-dates-times-tests/</guid>
      <description>A little note to avoid bugs in tests, or missing bugs: don&amp;rsquo;t use the current date or time when writing unit tests.
When making up data for tests, it&amp;rsquo;s common to just use today&amp;rsquo;s date as it&amp;rsquo;s easy to think up. This can catch you out when there is actually some hidden dependency on the real time and your test starts failing the next day, month or year. The test is currently passing by coincidence, which is hard to distinguish from a real pass if you&amp;rsquo;re not careful.</description>
    </item>
    
    <item>
      <title>Do software engineers have the same &#34;fee justification&#34; problem as investment managers?</title>
      <link>https://notestoself.dev/posts/software-engineers-investment-managers-fee-justification/</link>
      <pubDate>Thu, 12 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/software-engineers-investment-managers-fee-justification/</guid>
      <description>It&amp;rsquo;s widely known (although somehow still controversial) that investment managers who try to manually pick stocks cannot beat the market the vast majority of the time:
 &amp;ldquo;Active fund managers trail the S&amp;amp;P 500 for the ninth year in a row in triumph for indexing&amp;rdquo; | CNBC &amp;ldquo;Stock-Picking Fund Managers Are Even Worse Than We Thought At Beating the Market&amp;rdquo; | Fortune &amp;ldquo;More evidence that it’s very hard to ‘beat the market’ over time, 95% of finance professionals can’t do it&amp;rdquo; | AEI &amp;ldquo;Your robo-advisor probably won’t be able to beat the stock market.</description>
    </item>
    
    <item>
      <title>Decision fatigue and job interviewing</title>
      <link>https://notestoself.dev/posts/decision-fatigue-and-job-interviewing/</link>
      <pubDate>Mon, 09 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/decision-fatigue-and-job-interviewing/</guid>
      <description>I was reading about decision fatigue and wondering if it accounts for more major events than we give it credit for. The idea is at least spreading a little bit in certain circles, but doesn&amp;rsquo;t get the wider attention it should.
 &amp;ldquo;One research study found that the decisions judges make are strongly influenced by how long it has been since their last break. &amp;ldquo;We find that the percentage of favorable rulings drops gradually from ≈65% to nearly zero within each decision session and returns abruptly to ≈65% after a break.</description>
    </item>
    
    <item>
      <title>SQL Puzzle 2: Absentees</title>
      <link>https://notestoself.dev/posts/sql-puzzles-2-absentees/</link>
      <pubDate>Mon, 02 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/sql-puzzles-2-absentees/</guid>
      <description>The second puzzle in Joe Celko&amp;rsquo;s SQL Puzzles is about tracking employee absenteeism.
The initial table structure is like this:
CREATE TABLE personnel ( emp_id integer PRIMARY KEY ); CREATE TABLE excuse_list ( reason_code varchar(40) UNIQUE ); CREATE TABLE absenteeism ( emp_id integer NOT NULL REFERENCES personnel (emp_id), absent_date date NOT NULL, reason_code varchar(40) NOT NULL REFERENCES excuse_list (reason_code), severity_points integer NOT NULL CONSTRAINT severity_points_range CHECK (severity_points BETWEEN 1 AND 4), PRIMARY KEY (emp_id, absent_date) ); There are already some nice properties to notice in this schema:</description>
    </item>
    
    <item>
      <title>You are not doing Test Driven Development</title>
      <link>https://notestoself.dev/posts/you-are-not-doing-test-driven-development/</link>
      <pubDate>Sun, 01 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/you-are-not-doing-test-driven-development/</guid>
      <description>Test Driven Development can be a business process, not just a development workflow. It can start much earlier in the software engineering process than when implementation starts and code is first written.
Quite a few engineering teams confuse TDD for doing any testing at all, but even teams that know TDD is a stronger process than that still limit its potential by confining it to an implementation workflow detail that is trivial at the level of the business or software team.</description>
    </item>
    
    <item>
      <title>Rendering HTML email version of articles with Hugo</title>
      <link>https://notestoself.dev/posts/rendering-html-email-version-of-articles-with-hugo/</link>
      <pubDate>Wed, 26 Feb 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/rendering-html-email-version-of-articles-with-hugo/</guid>
      <description>I recently started using Mailcoach, mainly for sending emails to the subscriber list for Chinese Boost.
Mailcoach&amp;rsquo;s HTML email templating abilities are pretty basic, but that&amp;rsquo;s fine. I&amp;rsquo;m happy to use simple HTML emails, and found this HTML email template that looks like it will do the job nicely.
As the content on Chinese Boost is managed with Hugo, it would be nice to have Hugo also be responsible for generating HTML email content for each article that can then just be copy-pasted into Mailcoach for sending to subscribers.</description>
    </item>
    
    <item>
      <title>SQL Puzzle 1: Fiscal Year Tables</title>
      <link>https://notestoself.dev/posts/sql-puzzles-1-fiscal-year-tables/</link>
      <pubDate>Wed, 26 Feb 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/sql-puzzles-1-fiscal-year-tables/</guid>
      <description>The first puzzle in SQL Puzzles is about adding strong constraints to ensure correctness of data in a table that looks like this:
CREATE TABLE fiscal_years ( fiscal_year integer, start_date date, end_date date ) Making all the columns NOT NULL is a good default first step as it avoids a large source of errors.
CREATE TABLE fiscal_years ( fiscal_year integer NOT NULL, start_date date NOT NULL, end_date date NOT NULL ) We can prevent other errors by adding UNIQUE constraints to each of the three columns, as we don&amp;rsquo;t want any shared values across rows for them: each fiscal year should only be described once, and no fiscal year can share a start date or end date with another.</description>
    </item>
    
    <item>
      <title>Setting up a Mailcoach app on AWS with Laravel Forge</title>
      <link>https://notestoself.dev/posts/setting-up-a-mailcoach-app-on-aws-with-laravel-forge/</link>
      <pubDate>Sun, 23 Feb 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/setting-up-a-mailcoach-app-on-aws-with-laravel-forge/</guid>
      <description>I&amp;rsquo;ve been looking forward to the release of Mailcoach, which is a self-hosted mailing list manager app. I have a few mailing lists, most of which are tiny, but one of which has over 3000 subscribers. This puts it over the threshold of expensive subscription options in MailChimp. MailChimp wants over 70 USD / month to run this single list, whereas a Mailcoach license is a one-off payment of 99 USD.</description>
    </item>
    
    <item>
      <title>Better alive than right</title>
      <link>https://notestoself.dev/posts/better-alive-than-right/</link>
      <pubDate>Wed, 19 Feb 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/better-alive-than-right/</guid>
      <description>&amp;ldquo;Better alive than right&amp;rdquo; is a kind of proverb I try to remind myself of when cycling in London on a daily basis. A sizeable minority of drivers in London have a disregard for people on bikes and break traffic rules in various ways around them.
A common one is drivers pulling out from a junction in front of someone on a bike, which requires the person cycling to stop to avoid a collision, even though the driver is at fault.</description>
    </item>
    
    <item>
      <title>Eating the recipe</title>
      <link>https://notestoself.dev/posts/eating-the-recipe/</link>
      <pubDate>Mon, 10 Feb 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/eating-the-recipe/</guid>
      <description>&amp;ldquo;Eating the recipe&amp;rdquo; is a term I&amp;rsquo;ve made up to try and describe the kind of mistakes that get made by mixing up levels of abstraction.
Humans do not struggle with the distinction between a recipe and the food that the recipe describes how to make. We would not make the mistake of eating a sheet of paper that describes how to make spaghetti, instead of eating the spaghetti. We do, however, design and use systems that make this kind of mistake a lot, or make it easy for humans to make this kind of mistake.</description>
    </item>
    
    <item>
      <title>Personal bike shedding</title>
      <link>https://notestoself.dev/posts/personal-bike-shedding/</link>
      <pubDate>Sun, 09 Feb 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/personal-bike-shedding/</guid>
      <description>The concept of bike shedding is one of my favourites, as once you&amp;rsquo;ve learnt about it, it won&amp;rsquo;t be long before you see it in the wild at most organisations.
The standard explanation (from Wikipedia), goes like this:
 &amp;ldquo;Parkinson provides the example of a fictional committee whose job was to approve the plans for a nuclear power plant spending the majority of its time on discussions about relatively minor but easy-to-grasp issues, such as what materials to use for the staff bike shed, while neglecting the proposed design of the plant itself, which is far more important and a far more difficult and complex task.</description>
    </item>
    
    <item>
      <title>Write code that is easy to delete</title>
      <link>https://notestoself.dev/posts/write-code-that-is-easy-to-delete/</link>
      <pubDate>Sat, 08 Feb 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/write-code-that-is-easy-to-delete/</guid>
      <description>It&amp;rsquo;s hard to write software that is easy to maintain and adapt to change. There&amp;rsquo;s a multitude of maxims to try and tease out general solutions to this, or at least approaches that can mitigate the problem.
This point did the rounds on Hacker News and elsewhere:
 &amp;ldquo;Write code that is easy to delete, not easy to extend.&amp;rdquo;
 &amp;ndash; programmingisterrible
This is one of the better maxims for producing maintainable and adaptable software.</description>
    </item>
    
    <item>
      <title>Generating business name ideas with Bash and Linux</title>
      <link>https://notestoself.dev/posts/generate-business-name-ideas-bash-linux/</link>
      <pubDate>Tue, 04 Feb 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/generate-business-name-ideas-bash-linux/</guid>
      <description>I&amp;rsquo;m setting up a small side-business selling wall art prints in the UK, and needed to think of a short, reasonably unique name.
After Googling for word lists and name generators, I thought it would actually work best to get Bash to generate a list of name ideas for me, keeping the words to a limited length and randomising them to avoid fatigue when reading the list.
The above can be done with this command on Ubuntu Linux:</description>
    </item>
    
    <item>
      <title>Some easy ways to improve dev workflows</title>
      <link>https://notestoself.dev/posts/dev-workflow-business-process-too/</link>
      <pubDate>Mon, 03 Feb 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/dev-workflow-business-process-too/</guid>
      <description>Your dev workflow is a business process too.
There&amp;rsquo;s a funny phenomenon in a lot of companies that hire software engineers, where people draw a dividing line between &amp;ldquo;engineering&amp;rdquo; and &amp;ldquo;the business&amp;rdquo;. This is even reinforced in a lot of software engineering books and other media, which talk about &amp;ldquo;business people&amp;rdquo; and so on. I always think of engineering as very much a part of this apparently separate &amp;ldquo;business&amp;rdquo;, but that&amp;rsquo;s at least a whole other blog post.</description>
    </item>
    
    <item>
      <title>Simple way to use ts-firebase-driver-testing with a Jest mock</title>
      <link>https://notestoself.dev/posts/firebase-driver-testing-simple-jest-mock/</link>
      <pubDate>Thu, 23 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/firebase-driver-testing-simple-jest-mock/</guid>
      <description>While working at Freetrade, I made the ts-firebase-driver-testing library to allow rapid testing and debugging of Firebase applications (other engineers have contributed to it since then).
The initial plan with the library was to abstract out the Firebase SDK as a set of plain interfaces, and then provide a single-process in-memory implementation of those interfaces. This worked fine and it does let you swap out Firebase when running tests, which was the main goal.</description>
    </item>
    
    <item>
      <title>Extract interfaces, don&#39;t pre-plan them</title>
      <link>https://notestoself.dev/posts/extract-interfaces-dont-pre-plan-them/</link>
      <pubDate>Mon, 20 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/extract-interfaces-dont-pre-plan-them/</guid>
      <description>The Interface Segration Principle is one of the more overlooked parts of SOLID. I think this might be because it gets seen as a restatement of the Single Responsibility Principle, and not as the separate idea that it actually is.
Interface segration is about keeping interfaces lean and specific to the smallest possible purpose, as suggested by the formal definition:
 &amp;ldquo;No client should be forced to depend on methods it does not use.</description>
    </item>
    
    <item>
      <title>The code should be a sequence diagram too</title>
      <link>https://notestoself.dev/posts/code-should-be-sequence-diagram/</link>
      <pubDate>Sat, 18 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/code-should-be-sequence-diagram/</guid>
      <description>I love sequence diagrams and other kinds of visual documentation of a system, especially ones that are committed in the repo alongside the application code.
PlantUML is a nice tool for this as there&amp;rsquo;s a simple mark-up language for creating and editing diagrams which sits nicely in a Git repo. You can commit and review changes to diagrams as you do with the rest of the code.
Diagrams are a kind of comment, though, and so really they should only supplement the code and not be a substitute for readability or clarity of the application code itself.</description>
    </item>
    
    <item>
      <title>Python script to convert an Etsy sales export CSV into a Xero import CSV</title>
      <link>https://notestoself.dev/posts/etsy-sales-csv-xero-import-python-script/</link>
      <pubDate>Sat, 11 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/etsy-sales-csv-xero-import-python-script/</guid>
      <description>We handle the accounting for our pop out cards shop using Xero. There are convenient pre-built integrations that handle Stripe and PayPal data in Xero, but nothing for Etsy, so I had to do a little Python script to generate a Xero import CSV file from the Etsy orders export CSV.
Here&amp;rsquo;s the script for future reference:
#!/usr/bin/python3 # https://www.etsy.com/uk/your/shops/YourShopName/download # Sold Orders # e.g. EtsySoldOrders2020.csv import argparse import csv from datetime import datetime from io import TextIOWrapper from typing import Dict SALES_ACCOUNT_CODE = &amp;#34;200&amp;#34; NOW = datetime.</description>
    </item>
    
    <item>
      <title>Be careful with Node file globbing vs bash globbing</title>
      <link>https://notestoself.dev/posts/be-careful-with-node-file-globbing-vs-bash-globbing/</link>
      <pubDate>Thu, 02 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/be-careful-with-node-file-globbing-vs-bash-globbing/</guid>
      <description>There&amp;rsquo;s a common gotcha when using commandline tools that have their own file globbing, such as Prettier in Nodejs.
This command is supposed to run prettier against all the .ts files in all folders under src recursively;
prettier --write src/**/*.ts What actually happens though is that bash expands the src/**/*.ts first, which will only go one folder deep instead of the intended unlimited recursion.
The fix is simply to quote the globbing express so that bash passes it to prettier unscathed:</description>
    </item>
    
    <item>
      <title>Atlas Shrugged</title>
      <link>https://notestoself.dev/reading/atlas-shrugged/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/atlas-shrugged/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Conversations with Friends</title>
      <link>https://notestoself.dev/reading/conversations-with-friends/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/conversations-with-friends/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Drink</title>
      <link>https://notestoself.dev/reading/drink/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/drink/</guid>
      <description></description>
    </item>
    
    <item>
      <title>How Fascism Works</title>
      <link>https://notestoself.dev/reading/how-fascism-works/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/how-fascism-works/</guid>
      <description></description>
    </item>
    
    <item>
      <title>If This is a Man · The Truce</title>
      <link>https://notestoself.dev/reading/if-this-is-a-man-the-truce/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/if-this-is-a-man-the-truce/</guid>
      <description></description>
    </item>
    
    <item>
      <title>My Brilliant Friend</title>
      <link>https://notestoself.dev/reading/my-brilliant-friend/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/my-brilliant-friend/</guid>
      <description></description>
    </item>
    
    <item>
      <title>One Hundreds Years of Solitude</title>
      <link>https://notestoself.dev/reading/one-hundreds-years-of-solitude/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/one-hundreds-years-of-solitude/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Almanack of Naval Ravikant</title>
      <link>https://notestoself.dev/reading/the-almanack-of-naval-ravikant/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-almanack-of-naval-ravikant/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Chinese Language: Fact and Fantasy</title>
      <link>https://notestoself.dev/reading/the-chinese-language-fact-and-fantasy/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-chinese-language-fact-and-fantasy/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Paper Menagerie</title>
      <link>https://notestoself.dev/reading/the-paper-menagerie/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-paper-menagerie/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Upheaval</title>
      <link>https://notestoself.dev/reading/upheaval/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/upheaval/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Utopia for Realists</title>
      <link>https://notestoself.dev/reading/utopia-for-realists/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/utopia-for-realists/</guid>
      <description></description>
    </item>
    
    <item>
      <title>丰乳肥臀</title>
      <link>https://notestoself.dev/reading/%E4%B8%B0%E4%B9%B3%E8%82%A5%E8%87%80/</link>
      <pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/%E4%B8%B0%E4%B9%B3%E8%82%A5%E8%87%80/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Setting up a Hugo static site inside Laravel on Forge</title>
      <link>https://notestoself.dev/posts/setting-up-a-hugo-static-site-inside-laravel-on-forge/</link>
      <pubDate>Mon, 23 Dec 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/setting-up-a-hugo-static-site-inside-laravel-on-forge/</guid>
      <description>Making a hybrid static + dynamic site can be quite a useful combination. This might sound like an oxymoron, but it simply means serving static pages from a site that also runs a dynamic web application. You can get some of the best of both worlds with this approach.
My Chinese-learning site ChineseBoost uses this combination &amp;ndash; a lot of the content pages, e.g. for Chinese grammar, are statically generated with Hugo, while dynamic parts of the site are served by a Laravel application.</description>
    </item>
    
    <item>
      <title>Song of Solomon</title>
      <link>https://notestoself.dev/reading/song-of-solomon/</link>
      <pubDate>Sun, 01 Dec 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/song-of-solomon/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Easy typed test mocks with Jest and TypeScript</title>
      <link>https://notestoself.dev/posts/easy-typed-test-mocks-with-jest-and-typescript/</link>
      <pubDate>Wed, 20 Nov 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/easy-typed-test-mocks-with-jest-and-typescript/</guid>
      <description>Here&amp;rsquo;s a quick and simple way to create type-safe test mocks with Jest in TypeScript:
const fooMock: jest.Mocked&amp;lt;IFoo&amp;gt; = { fooMethod: jest.fn() } const testSubject = new ClientClass(fooMock); This has a few useful properties:
 The type system will enforce that the dependency is the correct type, i.e. that ClientClass is being passed the instance of IFoo that it requires. The type system will enforce that the mock implements the required interface, i.</description>
    </item>
    
    <item>
      <title>Linking to the previous and next page in a taxonomy term in Hugo</title>
      <link>https://notestoself.dev/posts/hugo-taxonomy-term-next-prev-page-links/</link>
      <pubDate>Sat, 02 Nov 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/hugo-taxonomy-term-next-prev-page-links/</guid>
      <description>Something that can be slightly tricky to achieve within Hugo&amp;rsquo;s taxonomy system is linking to the previous and next page within a particular taxonomy term. For example, if you have a Series taxonomy, you might want to link to the previous and next page in that series on each page in it.
One way to solve this is to loop through all the pages in the taxonomy term and grab the previous and next pages on the way.</description>
    </item>
    
    <item>
      <title>Flexible test model factories in TypeScript</title>
      <link>https://notestoself.dev/posts/flexible-test-model-factories-in-typescript/</link>
      <pubDate>Wed, 23 Oct 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/flexible-test-model-factories-in-typescript/</guid>
      <description>Tests are improved by being more readable, and concise tests are more readable.
It&amp;rsquo;s good when a test has clear given, when, then steps, and the important details are plain to see in the test code.
This is often obstructed by large amounts of set-up and boilerplate code in the test that distracts from the important details.
Test model factories can reduce some of that boilerplate.
For example, if we have a Client model that contains quite a lot of detail, setting one up in a test will be verbose.</description>
    </item>
    
    <item>
      <title>Make a deep object delta diff in TypeScript</title>
      <link>https://notestoself.dev/posts/make-a-deep-object-delta-diff-in-typescript/</link>
      <pubDate>Wed, 25 Sep 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/make-a-deep-object-delta-diff-in-typescript/</guid>
      <description>Building on the object path enumeration, we can also make a deep object diff or delta.
E.g. given these two objects:
{ &amp;#34;a&amp;#34;: { &amp;#34;b&amp;#34;: { &amp;#34;c&amp;#34;: &amp;#34;123&amp;#34; } }, &amp;#34;foo&amp;#34;: &amp;#34;hello&amp;#34; } { &amp;#34;a&amp;#34;: { &amp;#34;b&amp;#34;: { &amp;#34;c&amp;#34;: &amp;#34;123&amp;#34;, &amp;#34;d&amp;#34;: &amp;#34;456&amp;#34; } }, &amp;#34;foo&amp;#34;: &amp;#34;hello&amp;#34; } We would make this delta diff:
{ &amp;#34;a&amp;#34;: { &amp;#34;b&amp;#34;: { &amp;#34;d&amp;#34;: &amp;#34;456&amp;#34; } } } This uses a few utility functions aside from enumeratePaths, but it should be relatively clear what they do here:</description>
    </item>
    
    <item>
      <title>Enumerating all object paths in TypeScript</title>
      <link>https://notestoself.dev/posts/enumerating-all-object-paths-in-typescript/</link>
      <pubDate>Mon, 23 Sep 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/enumerating-all-object-paths-in-typescript/</guid>
      <description>Here&amp;rsquo;s a small recursive TypeScript function to enumerate all the paths through an object.
E.g. given this object:
{ &amp;#34;animals&amp;#34;: { &amp;#34;lion&amp;#34;: { &amp;#34;sound&amp;#34;: &amp;#34;roar&amp;#34; }, &amp;#34;zebra&amp;#34;: { &amp;#34;appearance&amp;#34;: &amp;#34;stripes&amp;#34; }, &amp;#34;elephant&amp;#34;: { &amp;#34;size&amp;#34;: &amp;#34;large&amp;#34;, &amp;#34;features&amp;#34;: { &amp;#34;trunk&amp;#34;: &amp;#34;yes&amp;#34;, &amp;#34;tusks&amp;#34;: &amp;#34;yes&amp;#34;, &amp;#34;wings&amp;#34;: &amp;#34;no&amp;#34; }, }, }, }, It enumerates these paths:
[ &amp;#34;animals&amp;#34;, &amp;#34;animals/lion&amp;#34;, &amp;#34;animals/zebra&amp;#34;, &amp;#34;animals/elephant&amp;#34;, &amp;#34;animals/lion/sound&amp;#34;, &amp;#34;animals/zebra/appearance&amp;#34;, &amp;#34;animals/elephant/size&amp;#34;, &amp;#34;animals/elephant/features&amp;#34;, &amp;#34;animals/elephant/features/trunk&amp;#34;, &amp;#34;animals/elephant/features/tusks&amp;#34;, &amp;#34;animals/elephant/features/wings&amp;#34;, ] The function:
function enumeratePaths(value: any): string[] { let paths: string[] = [] if (value &amp;amp;&amp;amp; typeof value === &amp;#34;object&amp;#34;) { paths = Object.</description>
    </item>
    
    <item>
      <title>Can you have multiple instances of your application in a single process?</title>
      <link>https://notestoself.dev/posts/can-you-have-multiple-instances-of-your-application-in-a-single-process/</link>
      <pubDate>Sun, 15 Sep 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/can-you-have-multiple-instances-of-your-application-in-a-single-process/</guid>
      <description>Here&amp;rsquo;s a question that might give some insight into the quality of your codebase:
 Can you have multiple instances of your application in a single process?
 By that I mean that you could easily create and run parts of the application and/or the whole thing in one process, without this causing bad behaviour. If so, this probably suggests that the design is quite well factored.
It&amp;rsquo;s a similar idea to the questions in the Joel test, but for internal application structure.</description>
    </item>
    
    <item>
      <title>Deepin Screenshot is a nice screenshot tool for Ubuntu Linux</title>
      <link>https://notestoself.dev/posts/deepin-screenshot-ubuntu-linux/</link>
      <pubDate>Sun, 18 Aug 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/deepin-screenshot-ubuntu-linux/</guid>
      <description>If you want a good screenshot tool on Ubuntu, Deepin Screenshot is probably the best one.
It lets you drag-select a rectangular area of the screen, and then re-arrange the area until you&amp;rsquo;re happy with it. You can then annotate the screenshot selection with highlights, arrows, boxes and text. Finally, you can also choose how to save the resulting screenshot image, or copy it to your clipboard.
Deepin made it super easy to make the personalised cards FAQ on our card shop, with a series of clear screenshots showing the process with the key parts highlighted.</description>
    </item>
    
    <item>
      <title>Showing other pages with the same taxonomy term in Hugo</title>
      <link>https://notestoself.dev/posts/hugo-showing-other-pages-taxonomy-term/</link>
      <pubDate>Thu, 01 Aug 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/hugo-showing-other-pages-taxonomy-term/</guid>
      <description>Here&amp;rsquo;s a quick HTML template snippet to do something I find is quite a common task in a Hugo site: show a list of other pages that share a taxonomy term with the current page.
It&amp;rsquo;s probably a good idea to refresh your memory of how taxonomies work in Hugo if you&amp;rsquo;re feeling rusty.
The snippet is just:
{{ if isset .Params &amp;#34;tags&amp;#34; }} {{ $tagTitle := index .Params.tags 0 }} {{ $currentLink := .</description>
    </item>
    
    <item>
      <title>Generating perspective-skewed mock-up images for printed products with Python</title>
      <link>https://notestoself.dev/posts/python-generate-perspective-print-product-images/</link>
      <pubDate>Sun, 21 Jul 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-generate-perspective-print-product-images/</guid>
      <description>For Moment Wall Art (and other printed product sites in future), we need to show the customer not just the main print design itself but also mock-up images demonstrating how it would look in various settings.
Applying a perspective skew to images like this can be handled by the Python Wand library, specifically with the perspective distortion function.
I have a Python script that iterates through a glob of all product files in parallel, and generates these perspective-skewed mock up images where necessary.</description>
    </item>
    
    <item>
      <title>Easier interface segregation in TypeScript (even better than Go!)</title>
      <link>https://notestoself.dev/posts/easier-interface-segregation-in-typescript-even-better-than-go/</link>
      <pubDate>Mon, 15 Jul 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/easier-interface-segregation-in-typescript-even-better-than-go/</guid>
      <description>Preamble: Introduction to interface segregation (Skip this if you&amp;rsquo;re aware of what the Interface Segration Principle is.)
The Interface Segration Principle is the I in the SOLID mnemonic. It&amp;rsquo;s conceptually one of the simplest, but is often overlooked, which is a shame as it&amp;rsquo;s a highly beneficial principle to follow in software design.
The Interface Segration Principle is defined as &amp;ldquo;no client should be forced to depend on methods it does not use&amp;rdquo;.</description>
    </item>
    
    <item>
      <title>Start with &#34;Given, When, Then&#34; tests</title>
      <link>https://notestoself.dev/posts/start-with-given-when-then-tests/</link>
      <pubDate>Sat, 13 Jul 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/start-with-given-when-then-tests/</guid>
      <description>Whether or not you&amp;rsquo;re practicing test-driven development, it&amp;rsquo;s helpful to begin writing tests with the &amp;ldquo;Given, When, Then&amp;rdquo; structure.
// Given we have a queued order; // When we cancel it; // Then the order should be cancelled. Getting the structure in with comments first has several benefits.
Firstly, it lowers the mental effort to required to start. You only need to think about the desired behaviour in the simplest terms.</description>
    </item>
    
    <item>
      <title>The attacker really does know the system when someone leaves your company</title>
      <link>https://notestoself.dev/posts/the-attacker-really-does-know-the-system-when-someone-leaves-your-company/</link>
      <pubDate>Sat, 13 Jul 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/the-attacker-really-does-know-the-system-when-someone-leaves-your-company/</guid>
      <description>A common maxim of computer security is &amp;ldquo;the attacker knows the system&amp;rdquo;. In other words, the system should still be secure even given a hypothetical attacker who knows exactly how it works. It&amp;rsquo;s the flip-side of relying on &amp;ldquo;security by obscurity&amp;rdquo;.
In cryptography, the same concept is referred to as Kerckhoffs&amp;rsquo;s principle, but can be applied to systems in general.
Most engineers are aware of this concept, but it&amp;rsquo;s also common to find a vague attitude that you can still &amp;ldquo;get away with&amp;rdquo; relying on little details that a hypothetical attacker won&amp;rsquo;t actually know, e.</description>
    </item>
    
    <item>
      <title>Fan-in from multiple routes to a single VM with iptables, for FIX on Quickfix</title>
      <link>https://notestoself.dev/posts/fan-in-from-multiple-routes-to-a-single-vm-with-iptables-for-fix-on-quickfix/</link>
      <pubDate>Mon, 08 Jul 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/fan-in-from-multiple-routes-to-a-single-vm-with-iptables-for-fix-on-quickfix/</guid>
      <description>Recently we were trying to set-up some network configuration to route FIX traffic from a single Docker container on a VM instance through four of our VPN gateways, two of the other side&amp;rsquo;s VPN gateways and on to two destination machines on the other side. It&amp;rsquo;s somewhat complicated, but necessary due to some constraints being out of our control.
The ultimate goal is to have failover redundancy in the system so that a different route can be used if one route is down.</description>
    </item>
    
    <item>
      <title>Get to a failing test ASAP</title>
      <link>https://notestoself.dev/posts/get-failing-test-asap/</link>
      <pubDate>Mon, 08 Jul 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/get-failing-test-asap/</guid>
      <description>The &amp;ldquo;Specification driven development&amp;rdquo; post mentions the idea of a &amp;ldquo;a low-resistance workflow&amp;rdquo; that can be achieved with test-driven development.
Overall that post is probably a bit dogmatic and over-eager about the idea of test-driven development, but the point about achieving a low-resistance workflow still seems valuable.
A more trivial way of doing that is to use a simple workflow like this:
 Do you have a failing test? -&amp;gt; Make the test pass.</description>
    </item>
    
    <item>
      <title>Pretty print JSON on the command line with Python</title>
      <link>https://notestoself.dev/posts/pretty-print-json-on-the-command-line-with-python/</link>
      <pubDate>Wed, 26 Jun 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/pretty-print-json-on-the-command-line-with-python/</guid>
      <description>I&amp;rsquo;m often dealing with little bits of JSON, and sometimes it&amp;rsquo;s helpful to pretty-print them for readability.
Python has a handy tool to make that easy on the command line: json.tool.
E.g.
echo &amp;#39;{&amp;#34;foo&amp;#34;: &amp;#34;bar&amp;#34;, &amp;#34;thing&amp;#34;: { &amp;#34;baz&amp;#34;: 42 } }&amp;#39; | python -m json.tool prints:
{ &amp;#34;foo&amp;#34;: &amp;#34;bar&amp;#34;, &amp;#34;thing&amp;#34;: { &amp;#34;baz&amp;#34;: 42 } } If you&amp;rsquo;re on Mac, you can convert JSON in the clipboard to its pretty-printed form with:</description>
    </item>
    
    <item>
      <title>Be careful with truthy promises in TypeScript</title>
      <link>https://notestoself.dev/posts/be-careful-with-truthy-promises-in-typescript/</link>
      <pubDate>Sun, 23 Jun 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/be-careful-with-truthy-promises-in-typescript/</guid>
      <description>There&amp;rsquo;s a type of small &amp;ldquo;gotcha&amp;rdquo; bug that is easy to hit with promises for boolean values in JavaScript.
The simplest example might be this:
async function checkSomeCondition(): Promise&amp;lt;boolean&amp;gt; { return false } if (checkSomeCondition()) { // do something. } The if branch will always be executed, because the promise object returned by checkSomeCondition() will be evaluated as true, regardless of what it would resolve to. It&amp;rsquo;s a typo-level mistake, but is easy to make.</description>
    </item>
    
    <item>
      <title>Loop try catch</title>
      <link>https://notestoself.dev/posts/loop-try-catch/</link>
      <pubDate>Wed, 19 Jun 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/loop-try-catch/</guid>
      <description>A simple cause of failure in software systems is a single problematic item in a set causing the entire set to fail.
Trivially, it looks like this:
for (const item of items) { processItem(item) } When something goes wrong in processItem() and throws an error, either the whole set fails to process, or all the subsequent items fail.
The trivial example has a trivial improvement:
for (const item of items) { try { processItem(item) } catch (err) { // Do something useful with the error.</description>
    </item>
    
    <item>
      <title>(Cross-post) A Day in the Life of a Software Engineer at Freetrade</title>
      <link>https://notestoself.dev/posts/day-life-software-engineer-freetrade/</link>
      <pubDate>Sat, 01 Jun 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/day-life-software-engineer-freetrade/</guid>
      <description>(Cross-posted from https://blog.freetrade.io/a-day-in-the-life-of-a-software-engineer-at-freetrade-a39a590d7ffa)
I joined Freetrade as Senior Software Engineer in October 2018. We’ve got a huge amount done in that time — the company’s moving fast!
But behind the bustle of getting big features shipped, I wanted to give people an insight into what an individual day’s work looks like on the engineering team.
Here’s my typical day as a Software Engineer at Freetrade.
(My) commute I commute by bike 90% of the time.</description>
    </item>
    
    <item>
      <title>A bad mix of bash and sh with [[</title>
      <link>https://notestoself.dev/posts/a-bad-mix-of-bash-and-sh-with/</link>
      <pubDate>Thu, 23 May 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/a-bad-mix-of-bash-and-sh-with/</guid>
      <description>If you accidentally run a bash script with sh, you&amp;rsquo;re probably going to have a bad time.
One little gotcha is the use of [[ (double square brackets) in bash.
E.g.:
if [[ -z &amp;#34;${PROJECT_ID}&amp;#34; ]]; then echo &amp;#39;PROJECT_ID is empty, exiting&amp;#39; exit 1 fi Bash will check the condition as expected. Sh, on the other hand, will reject the double-brackets syntax that it is not aware of with a message like this:</description>
    </item>
    
    <item>
      <title>Generate a UUID on the command line with Python</title>
      <link>https://notestoself.dev/posts/generate-a-uuid-on-the-command-line-with-python/</link>
      <pubDate>Sat, 18 May 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/generate-a-uuid-on-the-command-line-with-python/</guid>
      <description>Sometimes I want to manually generate a UUID for some test or example data.
It&amp;rsquo;s handy to have this Python snippet in Bash history to quickly do that:
python -c &amp;#39;import uuid; print(uuid.uuid4())&amp;#39; </description>
    </item>
    
    <item>
      <title>Python National Insurance Number (NINo) one-line generator</title>
      <link>https://notestoself.dev/posts/python-national-insurance-number-nino-one-line-generator/</link>
      <pubDate>Tue, 23 Apr 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-national-insurance-number-nino-one-line-generator/</guid>
      <description>Here&amp;rsquo;s a Python one-liner for generating a random UK National Insurance Number (aka NINo) on the command line, which is useful for testing apps that use one:
python -c &amp;#39;import random; import string; p=&amp;#34;ABCEGHJKLMNPRSTWXYZ&amp;#34;; print(&amp;#34;&amp;#34;.join([random.choice(p),random.choice(p)]+[random.choice(string.digits) for i in range(6)]+[random.choice(&amp;#34;ABCD&amp;#34;)]))&amp;#39; To make it easier to use:
alias nino=&amp;#34;python -c &amp;#39;import random; import string; p=\&amp;#34;ABCEGHJKLMNPRSTWXYZ\&amp;#34;; print(\&amp;#34;\&amp;#34;.join([random.choice(p),random.choice(p)]+[random.choice(string.digits) for i in range(6)]+[random.choice(\&amp;#34;ABCD\&amp;#34;)]))&amp;#39;&amp;#34; </description>
    </item>
    
    <item>
      <title>Python random string one-liner for CLI</title>
      <link>https://notestoself.dev/posts/python-random-string-one-liner-for-cli/</link>
      <pubDate>Tue, 23 Apr 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-random-string-one-liner-for-cli/</guid>
      <description>Here&amp;rsquo;s a handy Python one-liner for generating a random string of letters and digits on the command line:
python -c &amp;#39;import random; import string; print(&amp;#34;&amp;#34;.join(random.choice(string.ascii_letters + string.digits) for i in range(30)))&amp;#39; To make it easier to use:
alias randomstring=&amp;#34;python -c &amp;#39;import random; import string; print(\&amp;#34;\&amp;#34;.join(random.choice(string.ascii_letters + string.digits) for i in range(30)))&amp;#39;&amp;#34; </description>
    </item>
    
    <item>
      <title>Extract JSON config variables from the Airflow UI</title>
      <link>https://notestoself.dev/posts/extract-json-config-variables-from-the-airflow-ui/</link>
      <pubDate>Mon, 18 Mar 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/extract-json-config-variables-from-the-airflow-ui/</guid>
      <description>One way to configure Apache Airflow is with &amp;ldquo;Variables&amp;rdquo; that can be modified programmatically or via the Airflow UI.
You can import the variables from a JSON file, again via the API or by uploading the file in the UI. Sensibly, Airflow does not provide a way to export these variables back out to a JSON file.
However, if you find yourself in the situation where Airflow holds the correct values and you&amp;rsquo;re not confident you&amp;rsquo;ve got them stored consistently elsewhere, you might end up having to manually copy the variables back out of the Airflow UI.</description>
    </item>
    
    <item>
      <title>(Cross-post) Serverless integration testing at Freetrade</title>
      <link>https://notestoself.dev/posts/serverless-integration-testing-freetrade/</link>
      <pubDate>Mon, 11 Mar 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/serverless-integration-testing-freetrade/</guid>
      <description>(Cross-posted from https://blog.freetrade.io/serverless-integration-testing-at-freetrade-5359fb0b0e57)
Our tech stack at Freetrade is 99% serverless. This means that for the vast majority of our applications, the physical infrastructure, deployment and other operational concerns are largely abstracted away by the cloud provider (Google Cloud in our case).
We focus on building what our users need, and our cloud provider focuses on what we need to keep it running smoothly at scale.
Serverless computing has huge benefits in terms of allowing more resources to be devoted to solving user problems, but it also presents some different challenges.</description>
    </item>
    
    <item>
      <title>qrencode is a nice QR code CLI tool for Ubuntu Linux</title>
      <link>https://notestoself.dev/posts/qrencode-qr-code-cli-tool-ubuntu-linux/</link>
      <pubDate>Sat, 23 Feb 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/qrencode-qr-code-cli-tool-ubuntu-linux/</guid>
      <description>There&amp;rsquo;s something fun about QR codes, and I wanted an easy way generate them as SVGs.
Sure enough there&amp;rsquo;s a tool in the Ubuntu repos to do that:
sudo apt install qrencode It&amp;rsquo;s straightforward to use:
qrencode -t SVG -o qr-code.svg &amp;#39;whatever you want encoded&amp;#39; I wanted to generate a QR code to put on little business cards that we pack with the orders for our pop up card shop. The QR code should point to a URL including a discount coupon that gets automatically applied for customers who have arrived via that URL.</description>
    </item>
    
    <item>
      <title>Promise batch progress meter in TypeScript</title>
      <link>https://notestoself.dev/posts/promise-batch-progress-meter-in-typescript/</link>
      <pubDate>Mon, 18 Feb 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/promise-batch-progress-meter-in-typescript/</guid>
      <description>Nodejs has a handy Promise.all function that awaits an entire batch of promises. However, sometimes you want to see how that batch of promises is progressing before the whole thing has finished.
Here&amp;rsquo;s a rough-and-ready way to print a progress meter to stdout as a batch of promises completes in TypeScript:
const promises = [1, 2, 3, 4, 5].map(async (i: number): Promise&amp;lt;string&amp;gt; =&amp;gt; { return &amp;#39;finished some work&amp;#39; }) let finishedCount = 0 promises.</description>
    </item>
    
    <item>
      <title>Duolingo Fandom Japanese vocab CSV extractor in JS</title>
      <link>https://notestoself.dev/posts/duolingo-fandom-japanese-js-csv-extractor/</link>
      <pubDate>Sat, 16 Feb 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/duolingo-fandom-japanese-js-csv-extractor/</guid>
      <description>I&amp;rsquo;m casually going through the Japanese course in Duolingo, and found this handy wiki series on Duolingo Fandom Japanese with lists of all of the vocab.
I wanted an easier way to import it into Anki, so I put together this little pastable JS script to export a CSV of the vocab on one page:
(function () { const pattern = /^([\p{sc=Han}\p{sc=Hiragana}\p{sc=Katakana}？、，。]+)\s*\(([a-z\p{P}-]+)\)\s*=\s*([a-z\p{P}\(\) ?]+)(\s*\(([\p{sc=Han}\p{sc=Hiragana}\p{sc=Katakana}？、，。]+)\))?$/iu; const csvRows = [&amp;#39;日本語,ふりがな,ローマ字,英語&amp;#39;]; document .querySelectorAll(&amp;#39;h3 ~ ul:not([class]) &amp;gt; li:not([class])&amp;#39;) .</description>
    </item>
    
    <item>
      <title>Creating a Series taxonomy in Hugo</title>
      <link>https://notestoself.dev/posts/hugo-series-taxonomy/</link>
      <pubDate>Wed, 30 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/hugo-series-taxonomy/</guid>
      <description>Hugo&amp;rsquo;s taxonomies feature is very flexible and covers a lot of use cases for organising static content.
One thing I use it for on several static sites is to create a Series taxonomy that allows an easy grouping of posts into a sequence. Readers can see other posts in the series on each post, like a table of contents, they can see next and previous links within the series, and they can view a dedicated list page for the series as well.</description>
    </item>
    
    <item>
      <title>Please comment your code</title>
      <link>https://notestoself.dev/posts/please-comment-your-code/</link>
      <pubDate>Wed, 23 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/please-comment-your-code/</guid>
      <description>&amp;ldquo;Every comment represents a failure to make the code self explanatory.&amp;rdquo;
 &amp;amp;em; Robert C. Martin (Uncle Bob)
One of the better-known sentiments from Uncle Bob&amp;rsquo;s Clean Code is that comments are bad*. More precisely, the idea is that comments are a last resort option to try and mitigate &amp;ldquo;bad code&amp;rdquo;.
Sometimes this seems to lead to a logical fallacy that goes like this: &amp;ldquo;Comments are a sign of bad code, and my code can&amp;rsquo;t be bad, so I won&amp;rsquo;t write comments&amp;rdquo;.</description>
    </item>
    
    <item>
      <title>A Concise Guide to SSL/TLS for DevOps</title>
      <link>https://notestoself.dev/reading/concise-guide-ssl-tls-devops/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/concise-guide-ssl-tls-devops/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Antifragile: Things that Gain from Disorder</title>
      <link>https://notestoself.dev/reading/antifragile-things-gain-disorder/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/antifragile-things-gain-disorder/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Children of God</title>
      <link>https://notestoself.dev/reading/children-of-god/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/children-of-god/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Don Quixote</title>
      <link>https://notestoself.dev/reading/don-quixote/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/don-quixote/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Exploring CQRS and Event Sourcing</title>
      <link>https://notestoself.dev/reading/exploring-cqrs-event-sourcing/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/exploring-cqrs-event-sourcing/</guid>
      <description></description>
    </item>
    
    <item>
      <title>History of the Decline and Fall of the Roman Empire</title>
      <link>https://notestoself.dev/reading/history-of-the-decline-and-fall-of-the-roman-empire/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/history-of-the-decline-and-fall-of-the-roman-empire/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Hyperion (Hyperion Cantos Book 1)</title>
      <link>https://notestoself.dev/reading/hyperion-1/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/hyperion-1/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Jane Eyre</title>
      <link>https://notestoself.dev/reading/jane-eyre/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/jane-eyre/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Origin Story: A Big History of Everything</title>
      <link>https://notestoself.dev/reading/origin-story-big-history-everything/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/origin-story-big-history-everything/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Systemantics: The Systems Bible</title>
      <link>https://notestoself.dev/reading/systems-bible/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/systems-bible/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Confidence Game</title>
      <link>https://notestoself.dev/reading/the-confidence-game/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-confidence-game/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Most Human Human</title>
      <link>https://notestoself.dev/reading/the-most-human-human/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-most-human-human/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The New Silk Roads: The Present and Future of the World</title>
      <link>https://notestoself.dev/reading/new-silk-roads-present-future-world/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/new-silk-roads-present-future-world/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Silk Roads: A New History of the World</title>
      <link>https://notestoself.dev/reading/silk-roads-new-history-world/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/silk-roads-new-history-world/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Three Body Problem</title>
      <link>https://notestoself.dev/reading/the-three-body-problem/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-three-body-problem/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Why We Sleep</title>
      <link>https://notestoself.dev/reading/why-we-sleep/</link>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/why-we-sleep/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Finding the max branch sum of a sequentially represented binary tree in Python</title>
      <link>https://notestoself.dev/posts/finding-the-max-branch-sum-of-a-sequentially-represented-binary-tree-in-python/</link>
      <pubDate>Sun, 23 Dec 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/finding-the-max-branch-sum-of-a-sequentially-represented-binary-tree-in-python/</guid>
      <description>Given a binary tree represented sequentially as a list of integer node values, we want to identify whether the sum of the left branch or the sum of th right branch is larger. In other words we want to identify which branch has the maximum sum.
For example, this binary tree:
···································· ················┌──┐················ 00··············│05│················ ················└──┘················ ··········╱··············╲·········· ········┌──┐············┌──┐········ 01······│04│············│06│········ ········└──┘············└──┘········ ······╱······╲······················ ····┌──┐····┌──┐···················· 02··│16│····│12│···················· ····└──┘····└──┘···················· ····································  Could be represented sequentially as [5, 4, 6, 16, 12].</description>
    </item>
    
    <item>
      <title>(Cross-post) Why we use the Firebase Realtime Database at Freetrade</title>
      <link>https://notestoself.dev/posts/cross-post-coming-to-firebases-nosql-realtime-database-from-a-sql-background/</link>
      <pubDate>Fri, 21 Dec 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/cross-post-coming-to-firebases-nosql-realtime-database-from-a-sql-background/</guid>
      <description>(Cross-posted from https://blog.freetrade.io/coming-to-firebases-nosql-realtime-database-from-a-sql-background-b605b2910c4c)
We use the Firebase Realtime Database at Freetrade, as part of our infrastructure on Google Cloud.
 The Firebase Realtime Database is a cloud-hosted NoSQL database that lets you store and sync data between your users in realtime.
 Firebase Realtime Database has a markedly different development philosophy to the more traditional SQL databases that many developers are used to. Here’s a rundown of what it’s like coming to Firebase Realtime Database from an SQL background.</description>
    </item>
    
    <item>
      <title>Spoonerism generator Python script</title>
      <link>https://notestoself.dev/posts/spoonerism-generator-python-script/</link>
      <pubDate>Thu, 08 Nov 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/spoonerism-generator-python-script/</guid>
      <description>&amp;ldquo;What&amp;rsquo;s the Difference Between&amp;rdquo; is a childish word-play game where you give clues for other players to guess a spoonerism phrase pair. For example:
 Q: &amp;ldquo;What&amp;rsquo;s the difference between stress on a group, and an old locomotive?&amp;rdquo;
A: One is team strain, the other&amp;rsquo;s a steam train.
 I&amp;rsquo;m part of a WhatsApp group that sends these back and forth on a daily basis (usually with less innocent contents than that example).</description>
    </item>
    
    <item>
      <title>Keeping the ElasticSearch service running on an Ubuntu server</title>
      <link>https://notestoself.dev/posts/keep-elasticsearch-service-running-ubuntu-server/</link>
      <pubDate>Sun, 28 Oct 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/keep-elasticsearch-service-running-ubuntu-server/</guid>
      <description>A local ElasticSearch instance powers the Chinese example sentence search feature on Chinese Boost. This works fine, but the ElasticSearch instance has a habit of crashing about once a day.
While I&amp;rsquo;ve been investigating why it crashes and how to prevent that from happening, I also wanted to have the EC2 Ubuntu server it runs on restart the ElasticSearch service automatically as a workaround.
This can be done using systemd.</description>
    </item>
    
    <item>
      <title>Be careful with the S3 origin URL for a Cloudfront distribution</title>
      <link>https://notestoself.dev/posts/careful-cloudfront-s3-origin-url/</link>
      <pubDate>Tue, 23 Oct 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/careful-cloudfront-s3-origin-url/</guid>
      <description>Be careful with the S3 origin URL for a Cloudfront distribution.
When using an S3 bucket with static website hosting enabled as the origin for a Cloudfront distribution, you must use the static website endpoint, and not the S3 bucket URL. These are different.
The S3 bucket URL looks like this (don&amp;rsquo;t use this for the Cloudfront origin):
https://s3.eu-west-2.amazonaws.com/foobar-bucket-name/
The S3 static website hosting URL looks like this (use this for the Cloudfront origin):</description>
    </item>
    
    <item>
      <title>Making a helpful catalogue data reporting command in Laravel</title>
      <link>https://notestoself.dev/posts/laravel-catalogue-data-report-command/</link>
      <pubDate>Tue, 23 Oct 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/laravel-catalogue-data-report-command/</guid>
      <description>The catalogue data model in our 3D pop up cards shop includes Product and Tag entities. Tags are a flat structure with a many-to-many relationship to Products, to allow some simple tagging to help customers find cards with particular elements.
The Tag-Product relationship is managed manually in the backoffice UI, which is fine but can be error prone when we miss Products that should have a particular Tag.
To make this easier to work with, I put together a quick command line tool in Laravel that identifies Products that seem to be missing an appriate Tag.</description>
    </item>
    
    <item>
      <title>Freetrade</title>
      <link>https://notestoself.dev/work/freetrade/</link>
      <pubDate>Mon, 08 Oct 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/freetrade/</guid>
      <description>I was Principal Software Engineer at Freetrade, primarily working on the platform but also some client-side work. Freetrade has a serverless architecture running Google Cloud Functions in Firebase using TypeScript.</description>
    </item>
    
    <item>
      <title>A quick Python script to help me translate classical Chinese poetry</title>
      <link>https://notestoself.dev/posts/chinese-poetry-python-script/</link>
      <pubDate>Sat, 06 Oct 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/chinese-poetry-python-script/</guid>
      <description>One of my pastimes is translating Chinese poetry and other text into English on my East Asia Student blog. The translations I make are more like annotations, as I try to adhere as closely as I can to the structure and literal meaning of the original text. The idea is that the translation is helpful for reading the original Chinese text, rather than being a full rendering into English.
One aspect of this is the inclusion of pinyin and literal glosses for each line of the poem.</description>
    </item>
    
    <item>
      <title>Island counting algorithm in Typescript</title>
      <link>https://notestoself.dev/posts/island-counting-algorithm-typescript/</link>
      <pubDate>Sun, 23 Sep 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/island-counting-algorithm-typescript/</guid>
      <description>A friend mentioned a code screen interview question he likes to ask based on counting the number of discrete &amp;ldquo;islands&amp;rdquo; in a grid:
 Given a square grid of cells, some of which are sea (0) and some of which are land (1), implement an island counter that identifies each contiguous area of land cells (an island) and counts the total number of such islands.
 This seems like quite a fun problem to solve so I had a go at it in TypeScript.</description>
    </item>
    
    <item>
      <title>AlgoDaily 22: Count the Planes</title>
      <link>https://notestoself.dev/posts/algodaily-22-count-the-planes/</link>
      <pubDate>Wed, 05 Sep 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-22-count-the-planes/</guid>
      <description>https://algodaily.com/challenges/count-the-planes
This is the same as the island counting problem.
function* enumerateGrid(grid) { if (!grid || grid.length &amp;lt; 1 || !grid[0].length) { return; } for (let x = 0; x &amp;lt; grid[0].length; x++) { for (let y = 0; y &amp;lt; grid[x].length; y++) { yield {x, y}; } } } function* enumerateNeighbours(grid, x, y) { for (const p of [{x:-1,y:0},{x:0,y:-1},{x:1,y:0},{x:0,y:1}]) { if (grid[x+p.x] &amp;amp;&amp;amp; grid[x+p.x][y+p.y]) { yield {x: x+p.x, y: y+p.</description>
    </item>
    
    <item>
      <title>Unix Fizz-Buzz one-liner</title>
      <link>https://notestoself.dev/posts/unix-fizz-buzz-one-liner/</link>
      <pubDate>Mon, 27 Aug 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/unix-fizz-buzz-one-liner/</guid>
      <description>Given that &amp;ldquo;implement Fizz-Buzz in a language of your choice&amp;rdquo; is quite a common programming interview task, I was wondering if it would be possible to do it in one line on the command line using Unix tools.
Unsurprisingly it is; here&amp;rsquo;s my version:
seq 100 | awk &amp;#39;{ if ($0 % 15 == 0) { print &amp;#34;fizzbuzz&amp;#34; } else if ($0 % 3 == 0) { print &amp;#34;fizz&amp;#34; } else if ($0 % 5 == 0) { print &amp;#34;buzz&amp;#34; } else { print $0 } }&amp;#39; The seq at the beginning generates numbers from 1 to 100, and then the awk command does a basic if-else on each line to decide what to print.</description>
    </item>
    
    <item>
      <title>AlgoDaily 21: Contiguous Subarray Sum</title>
      <link>https://notestoself.dev/posts/algodaily-21-contiguous-subarray-sum/</link>
      <pubDate>Thu, 23 Aug 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-21-contiguous-subarray-sum/</guid>
      <description>https://algodaily.com/challenges/contiguous-subarray-sum
There&amp;rsquo;s a fairly obvious brute-force O(n^2) way to do this by looping through every possible sub-array:
function sum(numbers) { return numbers.reduce((sum, x) =&amp;gt; sum + x, 0); } function* allSubArrays(numbers) { for (let i = 0; i &amp;lt;= numbers.length; i++) { for (let j = 0; j &amp;lt;= numbers.length; j++) { const slice = numbers.slice(i, j); if (slice.length &amp;gt; 0) { yield slice; } } } } function subarraySum(numbers, targetSum) { for (let sub of allSubArrays(numbers)) { if (sum(sub) === targetSum) { return true; } } return false; } This is O(n^2), so not great, but I can&amp;rsquo;t think of a different way to do it.</description>
    </item>
    
    <item>
      <title>AlgoDaily 20: Uniqueness of Arrays</title>
      <link>https://notestoself.dev/posts/algodaily-20-uniqueness-of-arrays/</link>
      <pubDate>Wed, 08 Aug 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-20-uniqueness-of-arrays/</guid>
      <description>https://algodaily.com/challenges/uniqueness-of-arrays
This is pretty trivial with the Set class in ECMAScript 2015.
function uniques(items) { return [...new Set(items)]; } If that seems like cheating, then we can achieve this with a hash map and a new array:
function uniques(items) { const seen = {}; const unique = []; for (let x of items) { if (seen[x] === undefined) { unique.push(x); seen[x] = true; } } return unique; } This is O(n).</description>
    </item>
    
    <item>
      <title>AlgoDaily 19: Fast Minimum In Rotated Array</title>
      <link>https://notestoself.dev/posts/algodaily-19-fast-minimum-in-rotated-array/</link>
      <pubDate>Sun, 05 Aug 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-19-fast-minimum-in-rotated-array/</guid>
      <description>https://algodaily.com/challenges/fast-minimum-in-rotated-array
It seems we have to identify an element that is lower than the preceding one, as this must be the original beginning of the array and thus the lowest. If we get to the end of the array without finding a drop, then it must be the first element of the array, as the pivot was at the end.
This still has a worst case running time of O(n), though, as we might have to iterate through the whole array.</description>
    </item>
    
    <item>
      <title>AlgoDaily 18: Sum All Primes</title>
      <link>https://notestoself.dev/posts/algodaily-18-sum-all-primes/</link>
      <pubDate>Mon, 23 Jul 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-18-sum-all-primes/</guid>
      <description>https://algodaily.com/challenges/sum-all-primes
I remember there&amp;rsquo;s an algorithm called the Sieve of Eratosthenes for finding prime numbers up to a limit, which seems like it would be useful here.
To apply the Sieve of Eratosthenes, you first allocate an object with each integer up to n as the keys.
You then iterate on integers up to $\sqrt{n}$. In other words, keep iterating on integers p until p * p would be more than n.</description>
    </item>
    
    <item>
      <title>Fixing cut off stroke edges of shapes in Inkscape</title>
      <link>https://notestoself.dev/posts/inkscape-shape-stroke-edges-cut-off/</link>
      <pubDate>Mon, 23 Jul 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/inkscape-shape-stroke-edges-cut-off/</guid>
      <description>I got stuck with this problem in Inkscape where the stroke edges of shapes seem to get cut off by the bounding box:
  After a bit of searching, it turned out that this was caused by having a small blur set on the layer containing the shapes:
  Setting that layer blur back to zero restores the full stroke edges of the shapes.
Note that you might have this layer blur set on your default template by mistake, in which case you&amp;rsquo;ll want to save a new default template with the layer blur set to zero.</description>
    </item>
    
    <item>
      <title>AlgoDaily 17: Find Missing Number in Array</title>
      <link>https://notestoself.dev/posts/algodaily-17-find-missing-number-in-array/</link>
      <pubDate>Sun, 08 Jul 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-17-find-missing-number-in-array/</guid>
      <description>https://algodaily.com/challenges/find-missing-number-in-array
Seems like we should iterate on the array, adding an integer to the result array if it&amp;rsquo;s not present in the input. We have to backtrack the iteration by one each time we hit a missing number. This is O(n).
function missingNumbers(sequence) { const missing = []; let expected; for (let i = 0; i &amp;lt; sequence.length; i++) { if (i === 0) { expected = sequence[i] + 1; continue; } if (sequence[i] !</description>
    </item>
    
    <item>
      <title>AlgoDaily 16: Missing Number In Unsorted</title>
      <link>https://notestoself.dev/posts/algodaily-16-missing-number-in-unsorted/</link>
      <pubDate>Thu, 05 Jul 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-16-missing-number-in-unsorted/</guid>
      <description>https://algodaily.com/challenges/missing-number-in-unsorted
The requirement to do this without sorting makes me think of using a hash map to track each number we do see, and then finding the missing integer there by iterating across the expected range. This isn&amp;rsquo;t O(n), though; it&amp;rsquo;s O(m+n).
function missingInUnsorted(numbers, lowerBound, upperBound) { const seen = numbers.reduce((carry, num) =&amp;gt; { carry[num] = true; return carry; }, {}); for (let i = lowerBound; i &amp;lt; upperBound; i++) { if (seen[i] === undefined) { return i; } } return undefined; } Algo Daily has a solution using the Gauss sum formula n(n+1)/2 to determine the sum of a range of numbers, and then subtracting the sum of the given numbers from that to find the single missing integer.</description>
    </item>
    
    <item>
      <title>AlgoDaily 15: Max of Min Pairs</title>
      <link>https://notestoself.dev/posts/algodaily-15-max-of-min-pairs/</link>
      <pubDate>Sat, 23 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-15-max-of-min-pairs/</guid>
      <description>https://algodaily.com/challenges/max-of-min-pairs
This requires a bit of thought. We want to get the highest possible smaller number in each pair.
The first thought is that we need to sort the numbers before we do anything else, so that we can allocate pairs with the largest possible minimum.
To get the largest possible minimum in the pair, you have to &amp;ldquo;waste&amp;rdquo; a larger number to accompany it. This should be the smallest possible number that is larger than the minimum in the pair.</description>
    </item>
    
    <item>
      <title>Simple estimated delivery dates in vanilla JS</title>
      <link>https://notestoself.dev/posts/simple-estimated-delivery-dates-in-vanilla-js/</link>
      <pubDate>Wed, 20 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/simple-estimated-delivery-dates-in-vanilla-js/</guid>
      <description>Displaying estimated delivery dates is a handy little feature in e-commerce that gives useful information to customers and reassures them about the ordering process.
Estimating delivery dates can be quite involved, but it&amp;rsquo;s easy to chuck a very simple estimated delivery range on any page with these vanilla JavaScript functions, no libraries required:
function showEstimatedDelivery() { const estDelEls = document.querySelectorAll(&amp;#34;.estimated-delivery&amp;#34;); if (!estDelEls || !estDelEls.length) { return; } const [minDate, maxDate] = estimatedDeliveryRange(); const [minFormat, maxFormat] = [formatDate(minDate), formatDate(maxDate)]; let estimatedDelivery if (minFormat === maxFormat) { estimatedDelivery = minFormat; } else { estimatedDelivery = `${minFormat}&amp;amp;mdash; ${maxFormat}`; } document.</description>
    </item>
    
    <item>
      <title>Python asyncio concurrent map pool function</title>
      <link>https://notestoself.dev/posts/python-asyncio-concurrent-map-pool/</link>
      <pubDate>Mon, 18 Jun 2018 13:18:51 +0100</pubDate>
      
      <guid>https://notestoself.dev/posts/python-asyncio-concurrent-map-pool/</guid>
      <description>Here is a relatively short Python function to map input items concurrently, with a fixed pool size to limit the maximum concurrency. The work function to map each item is a normal synchronous function, which makes this easy to use in projects that are not otherwise using asyncio.
This function should be OK to run with or without a pre-existing event loop in the process. It uses an existing event loop if there is one, otherwise it creates a new event loop and uses that.</description>
    </item>
    
    <item>
      <title>Clipboard hygiene</title>
      <link>https://notestoself.dev/posts/clipboard-hygiene/</link>
      <pubDate>Tue, 12 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/clipboard-hygiene/</guid>
      <description>After using the excellent keepassx password manager for a while, I&amp;rsquo;ve noticed it has a nice little security feature: once you copy a password to the clipboard, keepassx waits for a short period of time and then clears your clipboard. It also clears the clipboard when you close keepassx.
This is a good idea as it somewhat reduces the chance of some other process being able to steal a password from the clipboard (which any user-level process could do).</description>
    </item>
    
    <item>
      <title>AlgoDaily 14: Find First Non-Repeating Character</title>
      <link>https://notestoself.dev/posts/algodaily-14-find-first-non-repeating-character/</link>
      <pubDate>Fri, 08 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-14-find-first-non-repeating-character/</guid>
      <description>https://algodaily.com/challenges/find-first-non-repeating-character
This shouldn&amp;rsquo;t be too hard with a hash map of characters to counts, relying on JavaScript objects having ordered keys to then find the first character with a single occurrence.
function firstNonRepeat(str) { const counts = {} for (let char of str) { counts[char] = counts[char] ? counts[char] + 1 : 1; } for (let char in counts) { if (counts[char] === 1) { return char; } } return &amp;#34;&amp;#34;; } </description>
    </item>
    
    <item>
      <title>AlgoDaily 13: Dollar Sign Deletion</title>
      <link>https://notestoself.dev/posts/algodaily-13-dollar-sign-deletion/</link>
      <pubDate>Tue, 05 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-13-dollar-sign-deletion/</guid>
      <description>&amp;ldquo;You&amp;rsquo;re given an array of strings containing alphabetical characters and certain $ characters. A $ represents a DELETE action whereby the character before it is deleted.
Each of these strings will be run through a method to operate on the $ DELETE action. We want to find out if the final string is the same for all of them. Let&amp;rsquo;s take an example:&amp;rdquo;
 const input = [&amp;#34;f$st&amp;#34;, &amp;#34;st&amp;#34;] isDollarDeleteEqual(input); // true // true because both become &amp;#34;st&amp;#34;  &amp;ldquo;Given the below function signature, can you find a solution in O(n) time and constant space?</description>
    </item>
    
    <item>
      <title>Extracting product image main colours in Python</title>
      <link>https://notestoself.dev/posts/extracting-product-image-main-colours-python/</link>
      <pubDate>Sat, 19 May 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/extracting-product-image-main-colours-python/</guid>
      <description>The main colours of a product are often quite important data in e-commerce systems, and it&amp;rsquo;s nice to have an automated way to extract these.
Here&amp;rsquo;s a rough summary of what I ended doing in Python to get this data from product images.
It uses three different colour extraction libraries and merges the results from them, which is somewhat questionable but seems to provide better overall results than a single library alone.</description>
    </item>
    
    <item>
      <title>Year name to century name and decade name in Python</title>
      <link>https://notestoself.dev/posts/year-name-century-name-decade-name-python/</link>
      <pubDate>Sat, 19 May 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/year-name-century-name-decade-name-python/</guid>
      <description>Here are some little helper functions to convert an integer year into a century name and decade name, for example 1989 would be &amp;quot;20th Century&amp;quot; and &amp;quot;1980s&amp;quot;.
from math import floor def year_to_century_name(year: int) -&amp;gt; str: century = (floor(int(year) / 100)) + 1 suffix = ordinal_suffix(century) return f&amp;#34;{century}{suffix} Century&amp;#34; def ordinal_suffix(num: int) -&amp;gt; str: return {1: &amp;#34;st&amp;#34;, 2: &amp;#34;nd&amp;#34;, 3: &amp;#34;rd&amp;#34;}.get( 0 if 10 &amp;gt; num &amp;gt; 14 else num % 10, &amp;#34;th&amp;#34; ) def year_to_decade_name(year: int) -&amp;gt; str: decade = floor(int(year) / 10) * 10 return f&amp;#34;{decade}s&amp;#34; We use this in Moment Wall Art to extract the century and decade of each piece, which allows for easy taxonomy pages like 20th Century Wall Art or 2020s Wall Art.</description>
    </item>
    
    <item>
      <title>Allow a Django command to use a file or stdin / stdout</title>
      <link>https://notestoself.dev/posts/django-command-file-stdin-stdout/</link>
      <pubDate>Sun, 13 May 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/django-command-file-stdin-stdout/</guid>
      <description>Here&amp;rsquo;s a little approach I&amp;rsquo;ve started using when writing Python scripts and Django console commands. It allows them to easily handle i/o from the commandline or from a file without much effort.
Rather than using sys.stdin or sys.stdout directly, you define an argparse.FileType argument for the command, and set the default to sys.stdin.
For example, in a Django command:
from sys import stdin, stdout from argparse import FileType from django.core.management.base import BaseCommand class Command(BaseCommand): def add_arguments(self, parser: ArgumentParser): parser.</description>
    </item>
    
    <item>
      <title>Streaming a CSV file from the database in Laravel</title>
      <link>https://notestoself.dev/posts/laravel-stream-csv-database/</link>
      <pubDate>Sun, 13 May 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/laravel-stream-csv-database/</guid>
      <description>Exporting data to CSV is a requirement in almost any business software project. We needed it to get a convenient sales data export in our card shop sideproject recently, and it turned out to be straightforward but slightly weird to do in Laravel.
Sales data export query The sales data export query is about 30 lines of SQL. This was easier to write directly in SQL than wrapping it up in Laravel&amp;rsquo;s ORM, and it&amp;rsquo;s also handy to be able to run this query separately anyway.</description>
    </item>
    
    <item>
      <title>AlgoDaily 12: Detect Substring in String</title>
      <link>https://notestoself.dev/posts/algodaily-12-detect-substring-in-string/</link>
      <pubDate>Tue, 08 May 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-12-detect-substring-in-string/</guid>
      <description>https://algodaily.com/challenges/detect-substring-in-string
 &amp;ldquo;How would you write a function to detect a substring in a string?
If the substring can be found in the string, return the index at which it starts. Otherwise, return -1.&amp;rdquo;
 We can iterate through the string, and increment a counter when we hit that index in the target substring. If the counter gets to the length of the target substring, we can return the iterator index minus the counter.</description>
    </item>
    
    <item>
      <title>AlgoDaily 11: Sum Digits Until One</title>
      <link>https://notestoself.dev/posts/algodaily-11-sum-digits-until-one/</link>
      <pubDate>Sun, 06 May 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-11-sum-digits-until-one/</guid>
      <description>https://algodaily.com/challenges/sum-digits-until-one
 &amp;ldquo;We&amp;rsquo;re provided a positive integer num. Can you write a method to repeatedly add all of its digits until the result has only one digit?
Here&amp;rsquo;s an example: if the input was 49, we&amp;rsquo;d go through the following steps:&amp;rdquo;
 // start with 49 4 + 9 = 13 // move onto 13 1 + 3 = 4 This seems to mostly be an issue of type juggling. If we&amp;rsquo;re careful with types, then all we have to do is keep breaking down the string of the number into single digit characters, sum those as numbers and set the original number to the string of that number.</description>
    </item>
    
    <item>
      <title>AlgoDaily 10: Binary Tree In-order Traversal</title>
      <link>https://notestoself.dev/posts/algodaily-10-binary-tree-in-order-traversal/</link>
      <pubDate>Sat, 05 May 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-10-binary-tree-in-order-traversal/</guid>
      <description>&amp;ldquo;Can you write a function to traverse a binary tree in-order, and print out the value of each node as it passes?&amp;rdquo;
 4 \ 5 / 6  &amp;ldquo;The example would output [4, 6, 5] since there is no left child for 4, and 6 is visited in-order before 5.
The definition of a tree node is as follows:&amp;rdquo;
 function Node(val) { this.val = val; this.left = null; this.</description>
    </item>
    
    <item>
      <title>Django model field choices with an inner class enum</title>
      <link>https://notestoself.dev/posts/django-field-choices-inner-class-enum/</link>
      <pubDate>Wed, 02 May 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/django-field-choices-inner-class-enum/</guid>
      <description>It&amp;rsquo;s common to define the possible choices for a character field on Django models like this (from the docs):
from django.db import models class Student(models.Model): FRESHMAN = &amp;#39;FR&amp;#39; SOPHOMORE = &amp;#39;SO&amp;#39; JUNIOR = &amp;#39;JR&amp;#39; SENIOR = &amp;#39;SR&amp;#39; YEAR_IN_SCHOOL_CHOICES = ( (FRESHMAN, &amp;#39;Freshman&amp;#39;), (SOPHOMORE, &amp;#39;Sophomore&amp;#39;), (JUNIOR, &amp;#39;Junior&amp;#39;), (SENIOR, &amp;#39;Senior&amp;#39;), ) year_in_school = models.CharField( max_length=2, choices=YEAR_IN_SCHOOL_CHOICES, default=FRESHMAN, ) This works fine and it makes constants available for the values, so that elsewhere you can refer to Student.</description>
    </item>
    
    <item>
      <title>uMed</title>
      <link>https://notestoself.dev/work/umed/</link>
      <pubDate>Mon, 16 Apr 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/umed/</guid>
      <description>I was a back-end Software Engineer at uMed in 2018.</description>
    </item>
    
    <item>
      <title>PHP Business Time library v1.0.0</title>
      <link>https://notestoself.dev/posts/php-business-time-library-v1/</link>
      <pubDate>Fri, 13 Apr 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/php-business-time-library-v1/</guid>
      <description>I&amp;rsquo;ve just released v1.0.0 of my [PHP Business Time](https://github.com/hughgrigg /php-business-time) library. I made this to solve my own problem that I had building my 3D pop-up card shop &amp;ndash; calculating estimated delivery dates based on business time constraints.
It would have been simpler to just solve the exact use case I had (probably hard-coding timings and combining that with retrieving holidays from a remote source), but I thought it would be fun to generalise it and make it into a separate library.</description>
    </item>
    
    <item>
      <title>Fixing a Github Action error &#34;botocore.awsrequest.AWSRequest&#34; &#34;exit code 255&#34;</title>
      <link>https://notestoself.dev/posts/github-action-s3-botocore-awsrequest-process-255-error/</link>
      <pubDate>Mon, 09 Apr 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/github-action-s3-botocore-awsrequest-process-255-error/</guid>
      <description>The other day I suddenly started hitting an error in my Github Actions that deploy static Hugo sites to S3:
&amp;lt;botocore.awsrequest.AWSRequest object at 0x7fbfb444e160&amp;gt; Error: Process completed with exit code 255. For some reason setting an env variable called AWS_EC2_METADATA_DISABLED to true fixes this error.
E.g. your Github Action YAML might look like this:
name:BuildandDeployon:pushjobs:build:name:BuildandDeployruns-on:ubuntu-lateststeps:- uses:actions/checkout@v1- name:InstallHugorun:| HUGO_DOWNLOAD=hugo_extended_${HUGO_VERSION}_Linux-64bit.tar.gzwgethttps://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/${HUGO_DOWNLOAD}tarxvzf${HUGO_DOWNLOAD}hugomvhugo$HOME/hugoenv:HUGO_VERSION:0.71.0- name:HugoBuildrun:$HOME/hugo-v- name:DeploytoS3if:github.ref==&amp;#39;refs/heads/master&amp;#39;run:awss3syncpublic/s3://{s3-bucket-name}/--delete--acl=public-read&amp;amp;&amp;amp;awscloudfrontcreate-invalidation--distribution-id={cloudfront-distribution-id}--paths=&amp;#39;/*&amp;#39;env:AWS_ACCESS_KEY_ID:${{secrets.AWS_ACCESS_KEY_ID}}AWS_SECRET_ACCESS_KEY:${{secrets.AWS_SECRET_ACCESS_KEY}}AWS_EC2_METADATA_DISABLED:trueThe key bit for avoiding this error is:</description>
    </item>
    
    <item>
      <title>AlgoDaily 09: Implement a Hash Map</title>
      <link>https://notestoself.dev/posts/algodaily-09-implement-a-hash-map/</link>
      <pubDate>Fri, 06 Apr 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-09-implement-a-hash-map/</guid>
      <description>&amp;ldquo;Can you implement the JS Map class from scratch? Only two methods are necessary &amp;ndash; get and set. Do not use JS Objects ({} notation) for this exercise.
For the two methods you&amp;rsquo;ll define:
get(key: string) should be given a key, and return the value for that key.
set(key: string, val: string) should take a key and a value as parameters, and store the pair.
Additionally, we&amp;rsquo;ve supplied the below hashing function hashStr.</description>
    </item>
    
    <item>
      <title>AlgoDaily 08: Lonely Number</title>
      <link>https://notestoself.dev/posts/algodaily-08-lonely-number/</link>
      <pubDate>Thu, 05 Apr 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-08-lonely-number/</guid>
      <description>https://algodaily.com/challenges/lonely-number
 &amp;ldquo;In a given array of numbers, one element will show up once and the rest will show up twice. Can you find that number in O(n) linear time?&amp;rdquo;
 lonelyNumber([4, 4, 6, 1, 3, 1, 3]) // 6  lonelyNumber([3, 3, 9]) // 9 This seems straightforward.
function lonelyNumber(items) { return Object.keys(items.reduce((seen, x) =&amp;gt; { if (x in seen) { delete seen[x]; return seen; } seen[x] = x; return seen; }, {}))[0]; } We start with an empty hash, then go through the array, removing an item we&amp;rsquo;ve seen before, and adding an item we haven&amp;rsquo;t.</description>
    </item>
    
    <item>
      <title>AlgoDaily 07: Power of Three</title>
      <link>https://notestoself.dev/posts/algodaily-07-power-of-three/</link>
      <pubDate>Tue, 03 Apr 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-07-power-of-three/</guid>
      <description>&amp;ldquo;Given an integer num, write a method to determine if it is a power of 3.&amp;rdquo;
 console.log(powerOfThree(9)); // true To calculate a power of x, we could keep multiplying by x until we hit the exponent.
That means to see if something is a power of x, we can do the reverse and keep diving by x until we hit 1 or a number that is not a multiple of 3.</description>
    </item>
    
    <item>
      <title>URL encoding data Etsy-style in Python</title>
      <link>https://notestoself.dev/posts/url-encode-etsy-style-python/</link>
      <pubDate>Mon, 26 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/url-encode-etsy-style-python/</guid>
      <description>The Etsy API has quite specific requirements for how data should be encoded in requests.
With a bit of trial and error making test requests, I ended up with these Python functions to encode data in the way the Etsy API requires:
from typing import Union, Dict, List from urllib import parse def etsy_urlencoded(data: Dict): return parse.urlencode({k: etsy_urlencoded_value(v) for k, v in data.items()}) def etsy_urlencoded_value(value: Union[Dict, List, str, bool, int, float]): if type(value) is dict: return json.</description>
    </item>
    
    <item>
      <title>Avoid deriving state from props in React</title>
      <link>https://notestoself.dev/posts/react-avoid-derived-state-from-props/</link>
      <pubDate>Fri, 23 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/react-avoid-derived-state-from-props/</guid>
      <description>A common footgun in React projects is derived state, which means basing state on props. This can be directly copying props into state, or deriving state from props with some extra logic.
This often causes trouble because once the state is set based on props, it won&amp;rsquo;t be updated by subsequent updates to props. This is after all the purpose of props and state in React: props are clean and stateless, and state is of course stateful.</description>
    </item>
    
    <item>
      <title>AlgoDaily 06: Majority Element</title>
      <link>https://notestoself.dev/posts/algodaily-06-majority-element/</link>
      <pubDate>Sat, 03 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-06-majority-element/</guid>
      <description>https://algodaily.com/challenges/majority-element
 &amp;ldquo;Suppose we&amp;rsquo;re given an array of numbers like the following:
[4, 2, 4]
Could you find the majority element? A majority is defined as &amp;ldquo;the greater part, or more than half, of the total. It is a subset of a set consisting of more than half of the set&amp;rsquo;s elements.
Let&amp;rsquo;s assume that the array length is always at least one, and that there&amp;rsquo;s always a majority element.</description>
    </item>
    
    <item>
      <title>AlgoDaily 05: Validate Palindrome</title>
      <link>https://notestoself.dev/posts/algodaily-05-validate-palindrome/</link>
      <pubDate>Fri, 02 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-05-validate-palindrome/</guid>
      <description>&amp;ldquo;Given a string str, can you write a method that will return True if is a palindrome and False if it is not? If you&amp;rsquo;ll recall, a palindrome is defined as &amp;ldquo;a word, phrase, or sequence that reads the same backward as forward&amp;rdquo;. For now, assume that we will not have input strings that contain special characters or spaces, so the following examples hold:&amp;rdquo;
 let str = &#39;thisisnotapalindrome&#39;; isPalindrome(str); // false str = &#39;racecar&#39;; isPalindrome(str); // true  &amp;ldquo;For an extra challenge, try to ignore non-alphanumerical characters.</description>
    </item>
    
    <item>
      <title>AlgoDaily 04: Is An Anagram</title>
      <link>https://notestoself.dev/posts/algodaily-04-is-an-anagram/</link>
      <pubDate>Thu, 01 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-04-is-an-anagram/</guid>
      <description>https://algodaily.com/challenges/is-an-anagram
 &amp;ldquo;Here&amp;rsquo;s the definition of an anagram: a word, phrase, or name formed by rearranging the letters of another, such as cinema, formed from iceman.
We are given two strings like &amp;ldquo;cinema&amp;rdquo; and &amp;ldquo;iceman&amp;rdquo; as inputs. Can you write a method isAnagram(str1, str2) that will return true or false depending on whether the strings are anagrams of each other?&amp;rdquo;
 The gotcha with this is being careful with multiple instances of the same letter, i.</description>
    </item>
    
    <item>
      <title>AlgoDaily 03: Reverse Only Alphabetical</title>
      <link>https://notestoself.dev/posts/algodaily-03-reverse-only-alphabetical/</link>
      <pubDate>Tue, 20 Feb 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-03-reverse-only-alphabetical/</guid>
      <description>(https://algodaily.com/challenges/reverse-only-alphabetical)[https://algodaily.com/challenges/reverse-only-alphabetical]
 &amp;ldquo;You are given a string that contains alphabetical characters (a - z, A - Z) and some other characters ($, !, etc.). For example, one input may be:
&amp;lsquo;sea!$hells3&amp;rsquo;
Can you reverse only the alphabetical ones?&amp;rdquo;
 First thought is to use a stack for this: go through the string forwards, pushing only letters on to the stack. Then go through the positions again, popping the stack for a letter position but leaving non-letters alone:</description>
    </item>
    
    <item>
      <title>AlgoDaily 02: Fizz Buzz</title>
      <link>https://notestoself.dev/posts/algodaily-02-fizz-buzz/</link>
      <pubDate>Mon, 19 Feb 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-02-fizz-buzz/</guid>
      <description>&amp;ldquo;We&amp;rsquo;re given a number n.
Write a function that returns the string representation of all numbers from 1 to n based on the following rules:
If it&amp;rsquo;s a multiple of 3, represent it as &amp;ldquo;fizz&amp;rdquo;.
If it&amp;rsquo;s a multiple of 5, represent it as &amp;ldquo;buzz&amp;rdquo;.
If it&amp;rsquo;s a multiple of both 3 and 5, represent it as &amp;ldquo;fizzbuzz&amp;rdquo;.
If it&amp;rsquo;s neither, just return the number itself.
As such, fizzBuzz(15) would result in &amp;lsquo;12fizz4buzzfizz78fizzbuzz11fizz1314fizzbuzz&amp;rsquo;.</description>
    </item>
    
    <item>
      <title>AlgoDaily 01: Array Intersection</title>
      <link>https://notestoself.dev/posts/algodaily-01-array-intersection/</link>
      <pubDate>Sun, 18 Feb 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algodaily-01-array-intersection/</guid>
      <description>I&amp;rsquo;ve signed up to AlgoDaily as it seems like a fun way to get out some small blog posts on small coding tasks.
This is AlgoDaily Day 1: Array Intersection.
 &amp;ldquo;Can you write a function that takes two inputs and returns their intersection? All element in the final result should be unique.&amp;rdquo;
 const nums1 = [1, 2, 2, 1]; const nums2 = [2, 2]; intersection(nums1, nums2); // [2]  &amp;ldquo;Here&amp;rsquo;s another one:&amp;rdquo;</description>
    </item>
    
    <item>
      <title>Varmilo keyboard F-keys and Super Win key mode on Ubuntu</title>
      <link>https://notestoself.dev/posts/ubuntu-varmilo-keyboard-f-super-win-key-mode/</link>
      <pubDate>Thu, 15 Feb 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/ubuntu-varmilo-keyboard-f-super-win-key-mode/</guid>
      <description>I got a Varmilo VA109M keyboard recently (it&amp;rsquo;s great!), which I&amp;rsquo;m using on Ubuntu 20.10.
This is how I got the F-keys working correctly:
echo options hid_apple fnmode=2 | sudo tee -a /etc/modprobe.d/hid_apple.conf sudo update-initramfs -u -k all Then restart.
And to get the Super key (labelled Win on Varmilo PC keyboards) working correctly, make sure your Varmilo keyboard is in PC mode:
Hold down Fn + Esc for 4 seconds.</description>
    </item>
    
    <item>
      <title>Cartesian products in Bash</title>
      <link>https://notestoself.dev/posts/cartesian-product-bash/</link>
      <pubDate>Tue, 23 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/cartesian-product-bash/</guid>
      <description>Here&amp;rsquo;s a handy thing you can do in Bash: calculate the Cartesian product of two sets.
E.g.
echo {A,B,C}{A,B,C} gives
AA AB AC BA BB BC CA CB CC i.e. it&amp;rsquo;s every possible permutation of two elements from the two sets.
A --- A \ / B -x- B / \ C --- C  (Thanks to Bill Hails for the diagram!)
This can be a useful tool for quickly figuring things out in various situations.</description>
    </item>
    
    <item>
      <title>Finding if two rectangles overlap in JavaScript</title>
      <link>https://notestoself.dev/posts/finding-if-two-rectangles-overlap-in-javascript/</link>
      <pubDate>Thu, 18 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/finding-if-two-rectangles-overlap-in-javascript/</guid>
      <description>Given the top-left and bottom-right coordinates of two rectangles, determine if they overlap or not. That is, determine if any part of one rectangle overlaps with any part of the other.
The grid has position (0, 0) at the top left. X increases rightwards and Y increases downwards. This is common in 2D grids in video games.
0,0 ─ x → │ y ↓  You get coordinates for the rectangles as tuples of x and y, e.</description>
    </item>
    
    <item>
      <title>Converting Roman numerals into decimal in JavaScript</title>
      <link>https://notestoself.dev/posts/converting-roman-numerals-into-decimal-in-javascript/</link>
      <pubDate>Mon, 08 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/converting-roman-numerals-into-decimal-in-javascript/</guid>
      <description>Converting Roman numerals into decimal is a fun little problem to solve. Getting the solution is dependent on knowing the rules of Roman numerals, and they&amp;rsquo;re actually simpler than you might think:
 All letters directly map to an integer value. A letter followed by a letter of higher value is subtracted. All other letters are added.  Before I looked this up, I thought that the rules were more complicated, e.</description>
    </item>
    
    <item>
      <title>Identifying a potential palindrome in JavaScript</title>
      <link>https://notestoself.dev/posts/identifying-a-potential-palindrome-in-javascript/</link>
      <pubDate>Fri, 05 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/identifying-a-potential-palindrome-in-javascript/</guid>
      <description>Given an input string, determine whether the string could possibly be a palindrome if rearranged. It is not necessary to return the potential palindrome, just to identify whether one is possible.
Examples:
aabb =&amp;gt; true abc =&amp;gt; false aab =&amp;gt; true ciicv =&amp;gt; true aba =&amp;gt; true abac =&amp;gt; false A palindrome can have any amount of pairs of letters, and optionally one single odd letter with no partner in the middle.</description>
    </item>
    
    <item>
      <title>Bloodchild</title>
      <link>https://notestoself.dev/reading/bloodchild/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/bloodchild/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Dawn</title>
      <link>https://notestoself.dev/reading/dawn/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/dawn/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Dune</title>
      <link>https://notestoself.dev/reading/dune/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/dune/</guid>
      <description></description>
    </item>
    
    <item>
      <title>How to Destroy a Tech Startup</title>
      <link>https://notestoself.dev/reading/how-to-destroy-a-tech-startup/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/how-to-destroy-a-tech-startup/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Rationality: From AI to Zombies</title>
      <link>https://notestoself.dev/reading/rationality-from-ai-to-zombies/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/rationality-from-ai-to-zombies/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Big Short</title>
      <link>https://notestoself.dev/reading/the-big-short/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-big-short/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Daily Stoic</title>
      <link>https://notestoself.dev/reading/the-daily-stoic/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-daily-stoic/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Gervais Principle</title>
      <link>https://notestoself.dev/reading/gervais-principle/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/gervais-principle/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Heart to Start</title>
      <link>https://notestoself.dev/reading/heart-to-start/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/heart-to-start/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Inner Game of Tennis</title>
      <link>https://notestoself.dev/reading/inner-game-of-tennis/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/inner-game-of-tennis/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Spy Who Came In from the Cold</title>
      <link>https://notestoself.dev/reading/spy-who-came-in-from-the-cold/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/spy-who-came-in-from-the-cold/</guid>
      <description></description>
    </item>
    
    <item>
      <title>War is a Racket</title>
      <link>https://notestoself.dev/reading/war-is-a-racket/</link>
      <pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/war-is-a-racket/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Python Tricks</title>
      <link>https://notestoself.dev/reading/python-tricks/</link>
      <pubDate>Sun, 17 Dec 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/python-tricks/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Product Page model abstraction in e-commerce</title>
      <link>https://notestoself.dev/posts/e-commerce-model-product-page-abstraction/</link>
      <pubDate>Fri, 08 Dec 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/e-commerce-model-product-page-abstraction/</guid>
      <description>A lot of e-commerce abstractions get a bit messy around abstracting products, SKUs, product options, product variations and so on. The fact that there doesn&amp;rsquo;t seem to be a standard or obvious abstraction for this suggests it is tricky to get right.
It might be helpful in a lot of systems to extract a Product Page model as an abstraction. I think this avoids a lot of the trouble as it&amp;rsquo;s easy to think about and lets your code match your conversations.</description>
    </item>
    
    <item>
      <title>Balancing address lines for e-commerce in PHP</title>
      <link>https://notestoself.dev/posts/php-balance-address-lines-ecommerce/</link>
      <pubDate>Thu, 07 Dec 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/php-balance-address-lines-ecommerce/</guid>
      <description>Delivery addresses are notoriously difficult to deal with. One issue we&amp;rsquo;ve encountered with our greetings card shop is long lines in an address that would get cut off when printed on the address label, so we have to manually tweak them to get the address to fit.
To reduce some of that manual work, I added a little helper that tries to balance the address lines where possible, i.e. take words off long lines and add them to neighbouring lines that have some space to spare.</description>
    </item>
    
    <item>
      <title>Fluent Python</title>
      <link>https://notestoself.dev/reading/fluent-python/</link>
      <pubDate>Tue, 05 Dec 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/fluent-python/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Drawing sequentially represented binary trees in Python</title>
      <link>https://notestoself.dev/posts/draw-sequential-binary-tree-python/</link>
      <pubDate>Thu, 23 Nov 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/draw-sequential-binary-tree-python/</guid>
      <description>I&amp;rsquo;ve been playing around with sequentially represented binary trees recently, i.e. a binary tree given as a list of integers where the node values are in contiguous sections of the list.
For example, a binary tree where the node values are also their indexes would be sequentially represented like this:
0 | 1, 2 | 3, 4, 5, 6 | 7, 8, 9, 10, 11, 12, 13, 14 Each section in the list represents one level of the binary tree, and has length $2^{l}$ where $l$ is the level.</description>
    </item>
    
    <item>
      <title>Managing Humans</title>
      <link>https://notestoself.dev/reading/managing-humans/</link>
      <pubDate>Thu, 05 Oct 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/managing-humans/</guid>
      <description></description>
    </item>
    
    <item>
      <title>On the Trail of Genghis Kahn</title>
      <link>https://notestoself.dev/reading/on-the-trail-of-genghis-kahn/</link>
      <pubDate>Sun, 01 Oct 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/on-the-trail-of-genghis-kahn/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Dispossessed</title>
      <link>https://notestoself.dev/reading/the-dispossessed/</link>
      <pubDate>Tue, 05 Sep 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-dispossessed/</guid>
      <description>Shevek, a brilliant physicist, decides to take action. he will seek answers, question the unquestionable, and attempt to tear down the walls of hatred that have isolated his planet of anarchists from the rest of the civilized universe. To do this dangerous task will mean giving up his family and possibly his life. Shevek must make the unprecedented journey to the utopian mother planet, Anarres, to challenge the complex structures of life and living, and ignite the fires of change.</description>
    </item>
    
    <item>
      <title>Blackjack CLI game</title>
      <link>https://notestoself.dev/posts/blackjack-cli-game/</link>
      <pubDate>Mon, 28 Aug 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/blackjack-cli-game/</guid>
      <description>The first draft of a blackjack cli game I&amp;rsquo;m making is now finished.
If you have Go installed then you should be able to play the game with:
go get -u github.com/hughgrigg/blackjack blackjack It&amp;rsquo;s a single-deck blackjack betting game written in Go for the command line. I&amp;rsquo;ve only tried it on Linux, but it uses the termui library so I imagine it would be OK on OSX as well. Not sure about Windows.</description>
    </item>
    
    <item>
      <title>Enable PHP assertion errors on Laravel Homestead for development and testing</title>
      <link>https://notestoself.dev/posts/enable-php-assertion-errors-on-laravel-homestead-for-development-and-testing/</link>
      <pubDate>Wed, 23 Aug 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/enable-php-assertion-errors-on-laravel-homestead-for-development-and-testing/</guid>
      <description>Laravel&amp;rsquo;s Homestead vagrant setup is handy to get going quickly on projects and keep them consistent to work on.
I was surprised that it has PHP assertion errors disabled by default. It&amp;rsquo;s good to have these enabled for local development and testing so that you can catch more problems earlier.
To enable PHP assertion errors in Homestead you can run this:
for phpConf in cli fpm; do sudo sed -i &amp;#39;s/zend.</description>
    </item>
    
    <item>
      <title>Doughnut Economics</title>
      <link>https://notestoself.dev/reading/doughnut-economics/</link>
      <pubDate>Sat, 05 Aug 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/doughnut-economics/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Dates for booleans</title>
      <link>https://notestoself.dev/posts/dates-for-booleans/</link>
      <pubDate>Sun, 23 Jul 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/dates-for-booleans/</guid>
      <description>Here&amp;rsquo;s a little maxim for data modeling: use dates instead of booleans. In a lot of situations, this lets you capture more information without losing anything compared to a boolean.
A common example is implementing a status column with a datetime foobared_at column instead of a boolean is_foobar. If it&amp;rsquo;s null, then that row is not foobar. If the column is filled, then you not only know that it is foobar, but also since when.</description>
    </item>
    
    <item>
      <title>Early exit boolean expression parser in Python</title>
      <link>https://notestoself.dev/posts/python-early-exit-boolean-expression-parser/</link>
      <pubDate>Tue, 18 Jul 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-early-exit-boolean-expression-parser/</guid>
      <description>I wanted to have a go at rush-implementing a boolean expression parser with early exit, i.e. that it will not bother evaluating the right hand side of an expression like 0&amp;amp;(1|0) and just return false once it sees 0&amp;amp;.
Here&amp;rsquo;s an attempt in Python. It deals with nested expressions by recursion and &amp;ldquo;collapsing&amp;rdquo; the evaluation into a 0 or 1 token to continue evaluating from there. It uses a similar approach to handle !</description>
    </item>
    
    <item>
      <title>Superforecasting</title>
      <link>https://notestoself.dev/reading/superforecasting/</link>
      <pubDate>Wed, 05 Jul 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/superforecasting/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Nordic APIs OAuth Workshop, Amsterdam June 2017</title>
      <link>https://notestoself.dev/posts/nordic-apis-oauth-workshop/</link>
      <pubDate>Mon, 12 Jun 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/nordic-apis-oauth-workshop/</guid>
      <description>In June I attended [Nordic API&amp;rsquo;s workshop](http://nordicapis.com/events/nordic- apis-workshops-amsterdam/) on OAuth and OpenID in Amsterdam. Here are my notes on the class.
Actors in OAuth  AS: Authorisation Server (OAuth Server), e.g. bank auth server RS: Resource Server, e.g. bank account server RO: Resource Owner, e.g. bank customer C: Client, e.g. mobile app  OAuth is designed for cross-organisational authorisation, i.e. where the client is not trusted (which should always be the case).</description>
    </item>
    
    <item>
      <title>The Corrections</title>
      <link>https://notestoself.dev/reading/the-corrections/</link>
      <pubDate>Mon, 05 Jun 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-corrections/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Auto-printing shipping labels with Laravel, Raspberry Pi and AWS</title>
      <link>https://notestoself.dev/posts/label-print-laravel-raspberry-pi-aws/</link>
      <pubDate>Mon, 29 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/label-print-laravel-raspberry-pi-aws/</guid>
      <description>The most manual part of the e-commerce side project I run with my wife is shipping the orders. We get order notifications by [email and Telegram](https://github.com/hughgrigg/poprobincards/bl ob/a08850cbee203223db309965b6e264e7e951e693/app/Modules/Sales/Notifications/Staf fOrderNotification.php#L52), and have to:
 Find the stock. Package it. Print the shipping label, cut out and attach it. Take the order to the post box.  A lot of that is beyond our means to automate, but we did get a Brother QL-570 label printer to speed up step 3 quite a lot.</description>
    </item>
    
    <item>
      <title>Algorithm Design Exercise 1-29: Finding the Fastest of 25 Horses</title>
      <link>https://notestoself.dev/posts/algorithm-design-exercise-1-29/</link>
      <pubDate>Thu, 25 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algorithm-design-exercise-1-29/</guid>
      <description>This is exercise 1-29 from The Algorithm Design Manual.
 There are 25 horses. At most, 5 horses can race together at a time. You must determine the fastest, second fastest, and third fastest horses. Find the minimum number of races in which this can be done.
 This is quite a famous puzzle question and involves some algorithmic thinking.
The first key to understanding for me was to focus on excluding horses who are slower than at least 3 other horses.</description>
    </item>
    
    <item>
      <title>Survivorship Thinking</title>
      <link>https://notestoself.dev/posts/survivorship-thinking/</link>
      <pubDate>Tue, 23 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/survivorship-thinking/</guid>
      <description>The survivorship bias is one of the most interesting cognitive biases to know about (cliché XKCD link).
It&amp;rsquo;s the idea that our impression of a given set of things is biased towards those that have survived some selection pressure. The classic example is when people say &amp;ldquo;they don&amp;rsquo;t make &amp;lsquo;em like they used to&amp;rdquo;. With a moment&amp;rsquo;s thought, you realise that of course all the old things we see today are well made.</description>
    </item>
    
    <item>
      <title>Rendering a comma-separated list of items in a Hugo template with a dot after the last</title>
      <link>https://notestoself.dev/posts/hugo-template-list-comma-separated-last-full-stop/</link>
      <pubDate>Sat, 13 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/hugo-template-list-comma-separated-last-full-stop/</guid>
      <description>Here&amp;rsquo;s a Hugo template snippet for rendering a list of items separated by a comma and space after all but the last item, and then a dot or full stop.
For example, if you have a tags taxonomy and some tags in a post&amp;rsquo;s front-matter like this:
tags:- Foo- Bar- Baz---You can render a comma-separated list of those tags in a template like this:
{{ with (.GetTerms &amp;#34;tags&amp;#34;) }} &amp;lt;p&amp;gt; {{ range $i, $t := .</description>
    </item>
    
    <item>
      <title>&#34;Permission denied (public key)&#34; error from Laravel Forge after copying ssh key using xclip or pbcopy</title>
      <link>https://notestoself.dev/posts/laravel-forge-ssh-key-xclip-pbcopy-permission-denied-public-key/</link>
      <pubDate>Fri, 12 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/laravel-forge-ssh-key-xclip-pbcopy-permission-denied-public-key/</guid>
      <description>I use Laravel Forge to run several Laravel projects. It has an interface to add SSH keys to a server so that you can SSH into them, but I&amp;rsquo;ve repeatedly hit a little issue when trying to use xclip on Linux or pbcopy on Mac to copy the public key and paste it into the Forge interface.
Somehow the public key is not copied properly and you&amp;rsquo;ll get the generic Permission denied (public key) error when trying to SSH into the server.</description>
    </item>
    
    <item>
      <title>The Every Computer Performance Book</title>
      <link>https://notestoself.dev/reading/the-every-computer-performance-book/</link>
      <pubDate>Mon, 01 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-every-computer-performance-book/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Remains of the Day</title>
      <link>https://notestoself.dev/reading/the-remains-of-the-day/</link>
      <pubDate>Mon, 01 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-remains-of-the-day/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Algorithm Design Exercise 1-28</title>
      <link>https://notestoself.dev/posts/algorithm-design-exercise-1-28/</link>
      <pubDate>Sun, 23 Apr 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algorithm-design-exercise-1-28/</guid>
      <description>This is exercise 1-28 from The Algorithm Design Manual.
 Write a function to perform integer division without using either the / or * operators. Find a fast way to do it.
 Let&amp;rsquo;s add multiplication as well. We could do a very inefficient multiplication algorithm by using one factor as a counter, and incrementing the other factor by itself until the counter reaches its value:
$a \times b$ $i = 0$ $p = 0$ while $i Naive integer multiplication in Python</description>
    </item>
    
    <item>
      <title>Station Eleven</title>
      <link>https://notestoself.dev/reading/station-eleven/</link>
      <pubDate>Sat, 01 Apr 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/station-eleven/</guid>
      <description>An audacious, darkly glittering novel about art, fame, and ambition set in the eerie days of civilization&amp;rsquo;s collapse, from the author of three highly acclaimed previous novels. One snowy night a famous Hollywood actor slumps over and dies onstage during a production of King Lear. Hours later, the world as we know it begins to dissolve. Moving back and forth in time-from the actor&amp;rsquo;s early days as a film star to fifteen years in the future, when a theater troupe known as the Traveling Symphony roams the wasteland of what remains-this suspenseful, elegiac, spellbinding novel charts the strange twists of fate that connect five people: the actor, the man who tried to save him, the actor&amp;rsquo;s first wife, his oldest friend, and a young actress with the TravelingSymphony, caught in the crosshairs of a dangerous self-proclaimed prophet. Sometimes terrifying, sometimes tender, Station Eleven tells a story about the relationships that sustain us, the ephemeral nature of fame, and the beauty of the world as we know it&amp;ndash;</description>
    </item>
    
    <item>
      <title>Stripping unprintable Unicode variation characters in PHP</title>
      <link>https://notestoself.dev/posts/stripping-unprintable-unicode-variation-characters-in-php/</link>
      <pubDate>Fri, 31 Mar 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/stripping-unprintable-unicode-variation-characters-in-php/</guid>
      <description>Emoji characters like 🥳, 🎉 and 💖 can be represented in a string in different ways. The first way is to include a Unicode point as a single character. For example 💖 is Unicode point U+1F496, which can be included in a PHP string either directly (&#39;💖&#39;) or using the Unicode code point escape syntax from PHP 7 onwards: &amp;quot;\u{1F496}&amp;quot;.
You can try these on the PHP REPL (php -a):</description>
    </item>
    
    <item>
      <title>Algorithm Design Exercises 1</title>
      <link>https://notestoself.dev/posts/algorithm-design-1/</link>
      <pubDate>Thu, 23 Mar 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/algorithm-design-1/</guid>
      <description>These are the exercises for chapter 1 of The Algorithm Design Manual.
1-1  Show that $a + b$ can be less than $min(a,b)$
 Where $a,b &amp;lt; 0$, e.g. if $a = -1, b = -2$, then $a + b = -3 &amp;lt; min(a,b) = -5$.
1-2  Show that $a \times b$ can be less than $min(a,b)$
 Where one of $\{a,b\}$ is negative and one is positive, e.</description>
    </item>
    
    <item>
      <title>Cracking the Coding Interview 6thE 10.1 Sorted Merge</title>
      <link>https://notestoself.dev/posts/cracking-coding-interview-6th-10.1-sorted-merge/</link>
      <pubDate>Thu, 23 Mar 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/cracking-coding-interview-6th-10.1-sorted-merge/</guid>
      <description>&amp;ldquo;Sorted Merge: You are given two sorted arrays, A and B, where A has a large enough buffer at the end to hold B. Write a method to merge B into A in sorted order.&amp;rdquo;
 Arrays are already sorted, so we can just do an insertion sort: iterate both, taking the next sorted item from each and insert it into A which has space for both.
It would be tricky to insert at the beginning, though, as we&amp;rsquo;d have to shift the existing elements in A.</description>
    </item>
    
    <item>
      <title>Uploading a listing image to the Etsy API in Python</title>
      <link>https://notestoself.dev/posts/etsy-api-listing-image-upload-python/</link>
      <pubDate>Tue, 21 Mar 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/etsy-api-listing-image-upload-python/</guid>
      <description>It took a bit of messing around, but I was able to upload product listing images via the Etsy API in Python 3 with requests_oauthlib like this (reduced to the essentials):
from requests_oauthlib import OAuth1Session session = OAuth1Session( &amp;#34;{etsy client key}&amp;#34;, client_secret=&amp;#34;{etsy client secret}&amp;#34;, resource_owner_key=&amp;#34;{etsy resource owner key}&amp;#34;, resource_owner_secret=&amp;#34;{etsy resource owner secret}&amp;#34;, ) response = session.post( &amp;#34;/listings/123456789/images&amp;#34;, params={ &amp;#34;listing_id&amp;#34;: 123456789, &amp;#34;rank&amp;#34;: 1, &amp;#34;overwrite&amp;#34;: True, &amp;#34;is_watermarked&amp;#34;: False, }, files={ &amp;#34;image&amp;#34;: ( &amp;#34;image123.</description>
    </item>
    
    <item>
      <title>Getting started with Teach Yourself CS: Algorithms and Data Structures</title>
      <link>https://notestoself.dev/posts/getting-started-teach-yourself-cs-algorithms-data-structures/</link>
      <pubDate>Fri, 17 Mar 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/getting-started-teach-yourself-cs-algorithms-data-structures/</guid>
      <description>The website TeachYourselfCS.com was recently featured on Hacker News, and after reading through I&amp;rsquo;ve decided to embark on a new learning journey by going through one of its subjects as properly as I can.
The subject I&amp;rsquo;ve chosen is Algorithms and Data Structures, as I think that will be most interesting to me personally, and will have the most immediate practical application in the kinds of things I like to do.</description>
    </item>
    
    <item>
      <title>Deep Survival</title>
      <link>https://notestoself.dev/reading/deep-survival/</link>
      <pubDate>Wed, 01 Mar 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/deep-survival/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Algorithms to Live By</title>
      <link>https://notestoself.dev/reading/algorithms-to-live-by/</link>
      <pubDate>Wed, 01 Feb 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/algorithms-to-live-by/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Moment Wall Art</title>
      <link>https://notestoself.dev/work/moment-wall-art/</link>
      <pubDate>Mon, 23 Jan 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/moment-wall-art/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Creating &amp; updating inventory product offerings via the Etsy API in Python</title>
      <link>https://notestoself.dev/posts/etsy-api-create-update-inventory-product-offerings/</link>
      <pubDate>Sun, 08 Jan 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/etsy-api-create-update-inventory-product-offerings/</guid>
      <description>Managing products and product variations in the Etsy API can be a bit tricky, especially when you want to &amp;ldquo;sync&amp;rdquo; them, i.e. either create or update in the Etsy API according to your own product data.
Here&amp;rsquo;s an approach in Python that I got working.
Say your product data looks like this:
my_products = { &amp;#34;sku_123&amp;#34;: {&amp;#34;style&amp;#34;: &amp;#34;foo&amp;#34;, &amp;#34;price&amp;#34;: &amp;#34;15.00&amp;#34;, &amp;#34;quantity&amp;#34;: 30}, &amp;#34;sku_456&amp;#34;: {&amp;#34;style&amp;#34;: &amp;#34;bar&amp;#34;, &amp;#34;price&amp;#34;: &amp;#34;25.00&amp;#34;, &amp;#34;quantity&amp;#34;: 10}, } One approach is to fetch the existing Etsy listing&amp;rsquo;s &amp;ldquo;inventory&amp;rdquo; (which might be empty for a newly created one), update it to match your local data, and then put it back to the Etsy API.</description>
    </item>
    
    <item>
      <title>A History of Modern Britain</title>
      <link>https://notestoself.dev/reading/a-history-of-modern-britain/</link>
      <pubDate>Sun, 01 Jan 2017 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/a-history-of-modern-britain/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Simplicity is a skill</title>
      <link>https://notestoself.dev/posts/simplicity-is-a-skill/</link>
      <pubDate>Sun, 23 Oct 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/simplicity-is-a-skill/</guid>
      <description>After a lot of peer reviews and looking back at my own work recently, I&amp;rsquo;ve been considering the point I was trying to get across in &amp;ldquo;Boring considered beneficial&amp;quot;.
One of the main thoughts there was that approaches and technologies sometimes considered boring or traditional have that perception for a reason: they&amp;rsquo;ve become mature enough to generally work and solve problems in a straightforward way. The community is used to them, so they&amp;rsquo;re not as exciting as more novel and exotic ways of solving the same problems.</description>
    </item>
    
    <item>
      <title>Scalar type declarations in PHP 7</title>
      <link>https://notestoself.dev/posts/scalar-type-declarations-php7/</link>
      <pubDate>Fri, 30 Sep 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/scalar-type-declarations-php7/</guid>
      <description>One of my favourite new features in PHP 7 is [scalar type declarations](http://php.net/manual/en/migration70.new- features.php#migration70.new-features.scalar-type-declarations).
Prior to PHP 7, the language&amp;rsquo;s type declaration abilities were somewhat inconsistent. You could type hint classes, interfaces and arrays, but nothing else (including callable and self in those). To require types for any scalar values (strings, ints, bools and floats), you had to do your own type checking and coercion.
However, it is worth pointing out that it was and is possible to do deeper type checking in PHP using value objects or interfaces.</description>
    </item>
    
    <item>
      <title>Always lint</title>
      <link>https://notestoself.dev/posts/always-lint/</link>
      <pubDate>Tue, 13 Sep 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/always-lint/</guid>
      <description>&amp;ldquo;lint (n): undesirable bits of fiber and fluff found in sheep&amp;rsquo;s wool&amp;rdquo;
 — Wikipedia
Linting is one of the first things I like to set up on a new project. Especially when working with interpreted languages, it&amp;rsquo;s reassuring to develop with a mechnical helper pointing out mistakes, problems and potential improvements.
Linters exist for pretty much every language out there, and are a great addition to the development process.</description>
    </item>
    
    <item>
      <title>Trigger Warning</title>
      <link>https://notestoself.dev/reading/trigger-warning/</link>
      <pubDate>Mon, 05 Sep 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/trigger-warning/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Handmaid&#39;s Tale</title>
      <link>https://notestoself.dev/reading/the-handmaids-tale/</link>
      <pubDate>Fri, 05 Aug 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-handmaids-tale/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Python unique random function result decorator</title>
      <link>https://notestoself.dev/posts/python-unique-random-function-result-decorator/</link>
      <pubDate>Sat, 23 Jul 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-unique-random-function-result-decorator/</guid>
      <description>I like messing around with random generators for productivity and creativity systems. Usually this involves functions that return a random value on each call. Even with quite a large set of possible values, duplicates occur more often than you might expect.
Here&amp;rsquo;s a quick Python decorator that keeps calling the function it decorates until it gets a value we haven&amp;rsquo;t seen so far during the current process:
def unique_results(func: Callable) -&amp;gt; Callable: seen = set() def wrapper(): res = func() for i in range(100): if res not in seen: seen.</description>
    </item>
    
    <item>
      <title>Boring considered beneficial</title>
      <link>https://notestoself.dev/posts/boring-considered-beneficial/</link>
      <pubDate>Mon, 23 May 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/boring-considered-beneficial/</guid>
      <description>Being boring is a good thing in software design.
There is no benefit to being impressive with a design, or to demonstrate one&amp;rsquo;s knowledge in code. Doing that is likely to cause problems and reduce the quality of the software.
One aspect of the programming profession is that there is both an urge and plenty of opportunity to try to show off. We are continually learning and improving, as we should be, and this can lead us to actively seek out opportunities to use the interesting knowledge we have acquired, instead of putting it to use when a concrete need for it arises.</description>
    </item>
    
    <item>
      <title>Simple pipes with PHP generators</title>
      <link>https://notestoself.dev/posts/simple-pipes-php-generators/</link>
      <pubDate>Tue, 17 May 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/simple-pipes-php-generators/</guid>
      <description>Pipes are a great structural pattern that can simplify the design of all kinds of software. They&amp;rsquo;re a central part of the Unix philosophy, allowing users to chain together a series of simple parts to achieve a more complex goal. Similarly, gulpjs brings the benefit of a pipe-based design to asset generation in the nodejs ecosystem.
I haven&amp;rsquo;t seen pipes used so much in the PHP world, but they&amp;rsquo;ve been simple to set up since generators were added to the language in 5.</description>
    </item>
    
    <item>
      <title>Never Let Me Go</title>
      <link>https://notestoself.dev/reading/never-let-me-go/</link>
      <pubDate>Thu, 05 May 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/never-let-me-go/</guid>
      <description>From the Booker Prize-winning author of The Remains of the Day and When We Were Orphans, comes an unforgettable edge-of-your-seat mystery that is at once heartbreakingly tender and morally courageous about what it means to be human. Hailsham seems like a pleasant English boarding school, far from the influences of the city. Its students are well tended and supported, trained in art and literature, and become just the sort of people the world wants them to be. But, curiously, they are taught nothing of the outside world and are allowed little contact with it. Within the grounds of Hailsham, Kathy grows from schoolgirl to young woman, but it&amp;rsquo;s only when she and her friends Ruth and Tommy leave the safe grounds of the school (as they always knew they would) that they realize the full truth of what Hailsham is.</description>
    </item>
    
    <item>
      <title>Using a real facade pattern in Laravel to encapsulate common behaviour</title>
      <link>https://notestoself.dev/posts/real-facade-laravel-encapsulate-common-behaviour/</link>
      <pubDate>Thu, 05 May 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/real-facade-laravel-encapsulate-common-behaviour/</guid>
      <description>Whilst working on my side project Pop Robin Cards (source), one of the PHP-MD rules I have configured flagged an issue:
poprobincards/app/Http/Controllers/Staff/ProductController.php:53 The method __construct has 6 parameters. Consider to reduce parameter number under 5. PHP-MD is quite rightly pointing out that six parameters for a method is excessive, even for a constructor. But what can be done? The six parameters are all things that the ProductController needs to do its job:</description>
    </item>
    
    <item>
      <title>PHP&#39;s list() is asking for a class</title>
      <link>https://notestoself.dev/posts/php-list-asking-for-class/</link>
      <pubDate>Sun, 17 Apr 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/php-list-asking-for-class/</guid>
      <description>PHP has a weird mechanism for achieving multiple return values with the list function. It lets you do things like this:
&amp;lt;?php function severalThings() { return [&amp;#39;thing&amp;#39;, &amp;#39;another thing&amp;#39;, &amp;#39;one more thing&amp;#39;]; } list($foo, $bar, $foobar) = severalThings(); This is quite commonly used in some codebases, but I think it&amp;rsquo;s bad style.
Firstly, because PHP doesn&amp;rsquo;t offer a concrete way to let the user of a function know exactly what it&amp;rsquo;s returning, it may not be clear what you&amp;rsquo;re getting from that array.</description>
    </item>
    
    <item>
      <title>Chinese strftime</title>
      <link>https://notestoself.dev/posts/chinese-strftime/</link>
      <pubDate>Tue, 05 Apr 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/chinese-strftime/</guid>
      <description>I&amp;rsquo;ve always used some variation of the original GNOME desktop: GNOME 2, Cinnamon and currently MATE. All of those desktops feature a similar calendar applet that lets you enter a strftime string to get the date formatted how you like it.
To get a tiny bit more Chinese into my day, I use this format:
%Y年%m月%e号 %A %p%l点%M分%S秒
(The main reason I&amp;rsquo;m writing this post is to keep the format easily accessible (somewhere.</description>
    </item>
    
    <item>
      <title>Optimised image handling with AWS and Laravel</title>
      <link>https://notestoself.dev/posts/optimised-image-handling-aws-laravel/</link>
      <pubDate>Sat, 19 Mar 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/optimised-image-handling-aws-laravel/</guid>
      <description>A central feature in my side project [Pop Robin Cards](https://github.com/hughgrigg /poprobincards) is the management of product images. In the first iteration, images uploaded by the user were simply stored locally on disk and served by nginx. This approach works fine, but has some problems:
 It uses disk space on the EC2 instance hosting the site, where disk space is at a premium. It makes it harder to load balance between multiple application server instances.</description>
    </item>
    
    <item>
      <title>Stranger in a Strange Land</title>
      <link>https://notestoself.dev/reading/stranger-in-a-strange-land/</link>
      <pubDate>Tue, 08 Mar 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/stranger-in-a-strange-land/</guid>
      <description>Stranger in a Strange Land, winner of the 1962 Hugo Award, is the story of Valentine Michael Smith, born during, and the only survivor of, the first manned mission to Mars. Michael is raised by Martians, and he arrives on Earth as a true innocent: he has never seen a woman and has no knowledge of Earth&amp;rsquo;s cultures or religions. But he brings turmoil with him, as he is the legal heir to an enormous financial empire, not to mention de facto owner of the planet Mars. With the irascible popular author Jubal Harshaw to protect him, Michael explores human morality and the meanings of love. He founds his own church, preaching free love and disseminating the psychic talents taught him by the Martians. Ultimately, he confronts the fate reserved for all messiahs. The impact of Stranger in a Strange Land was considerable, leading many children of the 60&amp;rsquo;s to set up households based on Michael&amp;rsquo;s water-brother nests. Heinlein loved to pontificate through the mouths of his characters, so modern readers must be willing to overlook the occasional sour note (Nine times out of ten, if a girl gets raped, it&amp;rsquo;s partly her fault.). That aside, Stranger in a Strange Land is one of the master&amp;rsquo;s best entertainments, provocative as he always loved to be. Can you grok it? &amp;ndash;Brooks Peck</description>
    </item>
    
    <item>
      <title>PyCharm Python script run error &#34;ImportError: attempted relative import with no known parent package&#34;</title>
      <link>https://notestoself.dev/posts/pycharm-python-script-import-error-attempted-relative-import/</link>
      <pubDate>Tue, 23 Feb 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/pycharm-python-script-import-error-attempted-relative-import/</guid>
      <description>When trying to run a Python script in PyCharm via a run configuration, you might hit an error like this:
Traceback (most recent call last): File &amp;#34;.../__main__.py&amp;#34;, line 3, in &amp;lt;module&amp;gt; from ... ImportError: attempted relative import with no known parent package Process finished with exit code 1 You might be able to resolve this by setting up the PyCharm run configuration as a &amp;ldquo;Module name&amp;rdquo; rather than a &amp;ldquo;Script path&amp;rdquo;.</description>
    </item>
    
    <item>
      <title>Pop Robin Cards</title>
      <link>https://notestoself.dev/work/poprobincards/</link>
      <pubDate>Sat, 23 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/poprobincards/</guid>
      <description>Pop Robin Cards is a side project that my wife and I work on in our spare time. It&amp;rsquo;s a custom e-commerce platform selling 3D pop-up cards.
The system allows management of a product catalogue, stock, images and so on, as well as a sales and order tracking system.</description>
    </item>
    
    <item>
      <title>Selecting the last grid row in CSS</title>
      <link>https://notestoself.dev/posts/css-select-last-grid-row/</link>
      <pubDate>Sat, 23 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/css-select-last-grid-row/</guid>
      <description>Recently I had the need to select the last row of elements in a grid using CSS. The selector needed to be able to handle an arbitrary total number of elements in the grid; in other words, in a grid with xcolumns, there could be 0 to xelements in the last row.
With a fixed or predictable number of elements in the grid, this would be quite straightforward. You can select on :nth-last-child(-n+x), with xbeing the known number of elements in the last row.</description>
    </item>
    
    <item>
      <title>23 Things They Dont Tell You About Capitalism</title>
      <link>https://notestoself.dev/reading/23-things-they-dont-tell-you-about-capitalism/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/23-things-they-dont-tell-you-about-capitalism/</guid>
      <description></description>
    </item>
    
    <item>
      <title>59 Seconds</title>
      <link>https://notestoself.dev/reading/59-seconds/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/59-seconds/</guid>
      <description>A psychologist and best-selling author gives us a myth-busting response to the self-help movement, with tips and tricks to improve your life that come straight from the scientific community.Richard Wiseman has been troubled by the realization that the self-help industry often promotes exercises that destroy motivation, damage relationships, and reduce creativity: the opposite of everything it promises. Now, in 59 Seconds, he fights back, bringing together the diverse scientific advice that can help you change your life in under a minute, and guides you toward becoming more decisive, more imaginative, more engaged, and altogether more happy.From mood to memory, persuasion to procrastination, resilience to relationships, Wiseman outlines the research supporting the new science of �rapid change� and, with clarity and infectious enthusiasm, describes how these quirky, sometimes counterintuitive techniques can be effortlessly incorporated into your everyday life. Or, as he likes to say: �Think a little, change a lot.�</description>
    </item>
    
    <item>
      <title>A Bitter Revolution</title>
      <link>https://notestoself.dev/reading/a-bitter-revolution/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/a-bitter-revolution/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Budding Prospects</title>
      <link>https://notestoself.dev/reading/budding-prospects/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/budding-prospects/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Fluent Forever</title>
      <link>https://notestoself.dev/reading/fluent-forever/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/fluent-forever/</guid>
      <description>Presents a series of techniques for acquiring a foreign language, including news ways of training the tongue for pronunciation, use of visual imagery for connecting sounds and spelling, and spaced-repetition methods for learning new vocabulary.</description>
    </item>
    
    <item>
      <title>Fooled by Randomness</title>
      <link>https://notestoself.dev/reading/fooled-by-randomness/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/fooled-by-randomness/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Haunted</title>
      <link>https://notestoself.dev/reading/haunted/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/haunted/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Look to Windward</title>
      <link>https://notestoself.dev/reading/look-to-windward/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/look-to-windward/</guid>
      <description>The Twin Novae battle had been one of the last of the Idiran war, and one of the most horrific: desperate to avert their inevitable defeat, the Idirans had induced not one but two suns to explode, snuffing out worlds and biospheres teeming with sentient life. They were attacks of incredible proportion &amp;ndash; gigadeathcrimes. But the war ended, and life went on. Now, eight hundred years later, light from the first explosion is about to reach the Masaq&amp;rsquo; Orbital, home to the Culture&amp;rsquo;s most adventurous and decadent souls. There it will fall upon Masaq&amp;rsquo;s 50 billion inhabitants, gathered to commemorate the deaths of the innocent and to reflect, if only for a moment, on what some call the Culture&amp;rsquo;s own complicity in the terrible event. Also journeying to Masaq&amp;rsquo; is Major Quilan, an emissary from the war-ravaged world of Chel. In the aftermath of the conflict that split his world apart, most believe he has come to Masaq&amp;rsquo; to bring home Chel&amp;rsquo;s most brilliant star and self-exiled dissident, the honored Composer Ziller. Ziller claims he will do anything to avoid a meeting with Major Quilan, who he suspects has come to murder him. But the Major&amp;rsquo;s true assignment will have far greater consequences than the death of a mere political dissident, as part of a conspiracy more ambitious than even he can know &amp;ndash; a mission his superiors have buried so deeply in his mind that even he cannot remember it. Hailed by SFX magazine as an excellent hopping-on point if you&amp;rsquo;ve never read a Banks SF novel before, Look to Windward is an awe-inspiring immersion into the wildly original, vividly realized civilization that Banks calls the Culture.</description>
    </item>
    
    <item>
      <title>Survivor</title>
      <link>https://notestoself.dev/reading/survivor/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/survivor/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Art of Thinking Clearly</title>
      <link>https://notestoself.dev/reading/the-art-of-thinking-clearly/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-art-of-thinking-clearly/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Brothers Karamazov</title>
      <link>https://notestoself.dev/reading/the-brothers-karamazov/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-brothers-karamazov/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Colour of Magic</title>
      <link>https://notestoself.dev/reading/the-colour-of-magic/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-colour-of-magic/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Left Hand of Darkness</title>
      <link>https://notestoself.dev/reading/the-left-hand-of-darkness/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-left-hand-of-darkness/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Light Fantastic</title>
      <link>https://notestoself.dev/reading/the-light-fantastic/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-light-fantastic/</guid>
      <description>Only one individual can save the world from a disastrous collision. Unfortunately, the hero happens to be the singularly inept wizard Rincewind, who was last seen falling off the edge of the world.</description>
    </item>
    
    <item>
      <title>The Trial</title>
      <link>https://notestoself.dev/reading/the-trial/</link>
      <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-trial/</guid>
      <description>Kafka&amp;rsquo;s classic novel of the terror state, in which profound unease and sinister uncertainty simmer beneath Joseph K.&amp;lsquo;s surface world of desperate normality and security. When arrested, he is spun into a panic and slowly, strangely, a helpless sense of guilt emerges in him.</description>
    </item>
    
    <item>
      <title>Island counting, max island area and max island perimeter in Python</title>
      <link>https://notestoself.dev/posts/python-island-counting-max-area-perimeter-algorithms/</link>
      <pubDate>Wed, 23 Dec 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-island-counting-max-area-perimeter-algorithms/</guid>
      <description>Here are three similar algorithms in Python for operations on &amp;ldquo;island problems&amp;rdquo;. These are problems were the input is a 2D matrix of grid coordinates, where 0 represents sea and 1 represents land.
For example:
[ [1, 0, 1, 0, 1], [1, 0, 1, 0, 0], [0, 0, 1, 1, 0], [0, 0, 0, 0, 0], [1, 1, 0, 0, 1], ] It&amp;rsquo;s slightly hard to see, but there are 5 islands in this grid: one in each corner, and a larger contiguous one in the shape of an L.</description>
    </item>
    
    <item>
      <title>Keep controllers thin</title>
      <link>https://notestoself.dev/posts/keep-controllers-thin/</link>
      <pubDate>Mon, 23 Nov 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/keep-controllers-thin/</guid>
      <description>The world of professional web development is dominated by the MVC pattern. MVC is so common in object oriented web development that it seems fundamental: of course your app has models, views and controllers (although other approaches could be perfectly valid).
One bit of advice that tends to get passed around for implementing with the MVC pattern is &amp;ldquo;thin controllers and fat models&amp;rdquo;, or some variation of that. I think this maxim misses the point.</description>
    </item>
    
    <item>
      <title>Validating URLs with the DOM</title>
      <link>https://notestoself.dev/posts/validating-urls-with-the-dom/</link>
      <pubDate>Mon, 05 Oct 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/validating-urls-with-the-dom/</guid>
      <description>URL validation is a common task in Web development, and thankfully a lot of cases can now be handled by the browser with the url input type:
&amp;lt;input type=&amp;#34;url&amp;#34; name=&amp;#34;gimme-a-link&amp;#34;&amp;gt;&amp;lt;/input&amp;gt; That&amp;rsquo;s great and gives you a lot of functionality and good user experience on a range of devices. However, a couple of issues remain once you&amp;rsquo;ve got your url type input in place:
 The browser&amp;rsquo;s URL validation is fast and accurate, but can be too permissive.</description>
    </item>
    
    <item>
      <title>Specification driven development</title>
      <link>https://notestoself.dev/posts/specification-driven-development/</link>
      <pubDate>Wed, 23 Sep 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/specification-driven-development/</guid>
      <description>There&amp;rsquo;s a great free ebook on test driven development by Grzegorz Gałęzowski. I read a lot of books related to programming, and this one stood out as particularly worthwhile.
Aside from a range of useful approaches and gems of understanding I got from the book, the biggest gain for me was an appreciation that test driven development isn&amp;rsquo;t really about testing as such. It could instead be named &amp;ldquo;specification- driven development&amp;rdquo;, which to me gives a better sense of what its main benefits are (and is what TDD actually means).</description>
    </item>
    
    <item>
      <title>Making random memorable passwords with bash</title>
      <link>https://notestoself.dev/posts/random-memorable-passwords-bash/</link>
      <pubDate>Thu, 17 Sep 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/random-memorable-passwords-bash/</guid>
      <description>Making secure passwords is quite a common task, and there are plenty of random password generators around (I like the one in keepassx). Making memorable passwords can be a bit trickier, though.
A nice way to make random but memorable passwords is to use a sequence of words and perhaps a short number (obligatory link to xkcd). That kind of password is easy for a human to remember and generally hard to brute-force, so long as the pool of possible words is large enough.</description>
    </item>
    
    <item>
      <title>Environment variables in the constructor are a code smell</title>
      <link>https://notestoself.dev/posts/code-smell-env-vars-in-constructor/</link>
      <pubDate>Sun, 06 Sep 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/code-smell-env-vars-in-constructor/</guid>
      <description>TLDR: don&amp;rsquo;t use env vars in class constructors.
Seeing environment variables in a class constructor is a code smell and an anti-pattern.
For example in TypeScript for Node.js, you might see something like this:
Anti-pattern:
class Foobar { private niceInjectableDependency: IDependency; private importantConfig: string; constructor(niceInjectableDependency: IDependency) { this.niceInjectableDependency = niceInjectableDependency; this.importantConfig = process.env.IMPORTANT_CONFIG; // code smell 	} } This might seem reasonable, as we&amp;rsquo;re still kind of able to inject the importantConfig into the class, just not via the constructor.</description>
    </item>
    
    <item>
      <title>Improving Unix-fu with Anki</title>
      <link>https://notestoself.dev/posts/unix-fu-anki/</link>
      <pubDate>Wed, 01 Jul 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/unix-fu-anki/</guid>
      <description>Anki has helped me maintain and improve my Chinese for years, but I&amp;rsquo;ve always been intrigued by using it to develop other kinds of knowledge. At university I used it heavily for East Asian history, our modern Chinese literature course and classical Chinese. I&amp;rsquo;ve now started using it to improve my Unix command-line fluency, and the results have been quite dramatic.
It&amp;rsquo;s not really possible to learn programming with an SRS system like Anki, but you can certainly use it to reinforce tokenized knowledge (like [this](https://developer.</description>
    </item>
    
    <item>
      <title>Minimum required lecture rooms scheduling in Python</title>
      <link>https://notestoself.dev/posts/minimum-required-lecture-rooms-scheduling-python/</link>
      <pubDate>Tue, 23 Jun 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/minimum-required-lecture-rooms-scheduling-python/</guid>
      <description>Given a schedule of lectures represented as a list of tuples of start and end times like [(15, 16), (13, 15), (14, 15)], return the minimum number of lecture rooms required to run all of the lectures.
We can solve this in $O(n\log_2n)$, due to the need to sort the lecture times.
The overall steps are:
 Convert time tuples into &amp;ldquo;lecture event&amp;rdquo; tuples of time and type, i.e. start or end.</description>
    </item>
    
    <item>
      <title>Using Wget for recursive downloads</title>
      <link>https://notestoself.dev/posts/wget-recursive-downloads/</link>
      <pubDate>Sun, 31 May 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/wget-recursive-downloads/</guid>
      <description>I discovered the VimCasts website recently and have been watching a few of their videos. Due to my unreliable internet connection, I prefer to download whole video files and then watch them (this also allows watching them on portable devices).
VimCasts allow directory listing on their storage server, which I believe is permission to bulk download their content. The only issue is that each episode is stored in its own subdirectory and in two formats.</description>
    </item>
    
    <item>
      <title>That code is ugly for a reason</title>
      <link>https://notestoself.dev/posts/ugly-code/</link>
      <pubDate>Wed, 27 May 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/ugly-code/</guid>
      <description>Before I really got into development, I went through the entire archive of Joel on Software reading every single article. I like to think that it set me up with good foundational knowledge for professional software development, but that&amp;rsquo;s pretty hard to prove.
Recently I re-read the article Things You Should Never Do, Part I. With a lot more development experience under my belt, the article spoke to me much more strongly this time around.</description>
    </item>
    
    <item>
      <title>Using Gulp asset versioning with Hugo data files</title>
      <link>https://notestoself.dev/posts/gulp-asset-versioning-hugo-data-files/</link>
      <pubDate>Sat, 23 May 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/gulp-asset-versioning-hugo-data-files/</guid>
      <description>I recently finished converting four of the websites I run from Wordpress to Hugo (my original blog, a Chinese learning blog, a Chinese grammar site and this blog), and hosting them with S3 and CloudFront.
It&amp;rsquo;s been a great change to make and has improved everything from my workflow to loading times and hosting costs. I&amp;rsquo;d recommend anyone running blogs and other text-focused sites to switch away from Wordpress and use a static site generator instead.</description>
    </item>
    
    <item>
      <title>Avoid naive string indexing in PHP</title>
      <link>https://notestoself.dev/posts/avoid-naive-string-indexing-php/</link>
      <pubDate>Thu, 23 Apr 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/avoid-naive-string-indexing-php/</guid>
      <description>PHP allows treating a string as an array, so that you can use indexing syntax to get or set a single position in the string:
&amp;lt;?php echo &amp;#39;hello&amp;#39;[0]; // h  $foo = &amp;#39;bar&amp;#39;; $foo[2] = &amp;#39;z&amp;#39;; echo $foo; // baz This seems handy but you should never do it.
The reason to avoid string indexing like this in PHP is that PHP strings are not multibyte character strings, they&amp;rsquo;re just bytes.</description>
    </item>
    
    <item>
      <title>Typing arrow and quote characters in Ubuntu with AltGr</title>
      <link>https://notestoself.dev/posts/ubuntu-altgr-type-arrow-quote-characters/</link>
      <pubDate>Mon, 23 Feb 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/ubuntu-altgr-type-arrow-quote-characters/</guid>
      <description>A quick Ubuntu Linux tip: you can use the AltGr key to easily type various arrow and quote characters.
The ones you might commonly want to use are:
AltGr + y ← Left arrow	AltGr + u ↓ Down arrow AltGr + i → Right arrow AltGr + Shift + u ↑ Up arrow AltGr + z « Left double angle quote AltGr + x » Right double angle quote AltGr + v “ Left double quotation mark AltGr + b ” Right double quotation mark </description>
    </item>
    
    <item>
      <title>Topological course prerequisites in Python</title>
      <link>https://notestoself.dev/posts/python-topological-course-prerequisites/</link>
      <pubDate>Fri, 23 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/python-topological-course-prerequisites/</guid>
      <description>We want to figure out the sequence we can take university courses in, and some of the courses may have prerequisite courses. In other words, we want a topological sort of the courses.
For this example, we can just ignore any courses that are impossible to take because they depend on non-existent courses, or have a circular dependency.
The input data is like this:
{&amp;#39;C3&amp;#39;: {&amp;#39;C2&amp;#39;, &amp;#39;C1&amp;#39;}, &amp;#39;C2&amp;#39;: {&amp;#39;C1&amp;#39;}, &amp;#39;C1&amp;#39;: {}} And the desired output is like this:</description>
    </item>
    
    <item>
      <title>Alexs Adventures in  Numberland</title>
      <link>https://notestoself.dev/reading/alexs-adventures-in-numberland/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/alexs-adventures-in-numberland/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Bash Cookbook</title>
      <link>https://notestoself.dev/reading/bash-cookbook/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/bash-cookbook/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Blink: The Power of Thinking Without Thinking</title>
      <link>https://notestoself.dev/reading/blink/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/blink/</guid>
      <description>Blink is about the first two seconds of looking&amp;ndash;the decisive glance that knows in an instant. Gladwell, the best-selling author of The Tipping Point, campaigns for snap judgments and mind reading with a gift for translating research into splendid storytelling. Building his case with scenes from a marriage, heart attack triage, speed dating, choking on the golf course, selling cars, and military maneuvers, he persuades readers to think small and focus on the meaning of thin slices of behavior. The key is to rely on our adaptive unconscious&amp;ndash;a 24/7 mental valet&amp;ndash;that provides us with instant and sophisticated information to warn of danger, read a stranger, or react to a new idea. Gladwell includes caveats about leaping to conclusions: marketers can manipulate our first impressions, high arousal moments make us mind blind, focusing on the wrong cue leaves us vulnerable to the Warren Harding Effect (i.e., voting for a handsome but hapless president). In a provocative chapter that exposes the dark side of blink, he illuminates the failure of rapid cognition in the tragic stakeout and murder of Amadou Diallo in the Bronx. He underlines studies about autism, facial reading and cardio uptick to urge training that enhances high-stakes decision-making. In this brilliant, cage-rattling book, one can only wish for a thicker slice of Gladwell&amp;rsquo;s ideas about what Blink Camp might look like. &amp;ndash;Barbara Mackoff</description>
    </item>
    
    <item>
      <title>Consider Phlebas</title>
      <link>https://notestoself.dev/reading/consider-phlebas/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/consider-phlebas/</guid>
      <description></description>
    </item>
    
    <item>
      <title>David and Goliath</title>
      <link>https://notestoself.dev/reading/david-and-goliath/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/david-and-goliath/</guid>
      <description>In the past decade, Malcolm Gladwell has written three books that have radically changed how we understand our world and ourselves: The Tipping Point, Blink, and Outliers. Regarded by many as the most gifted and influential author and journalist in America today, Gladwell&amp;rsquo;s rare ability to connect with audiences of such varied interests has ensured that each title become a phenomenal bestseller with more than ten million copies in print combined. Now, Gladwell&amp;rsquo;s landmark investigations into the world around us are collected together for the first time. Beautifully repackaged and redesigned, including for the first time illustrations throughout each book, MALCOLM GLADWELL: COMPLETE is a perfect treasury of prose and provocation for Gladwell fans old and new.</description>
    </item>
    
    <item>
      <title>Dead Air</title>
      <link>https://notestoself.dev/reading/dead-air/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/dead-air/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Distrust That Particular Flavour</title>
      <link>https://notestoself.dev/reading/distrust-that-particular-flavour/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/distrust-that-particular-flavour/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Effective Programming: More Than Writing Code</title>
      <link>https://notestoself.dev/reading/effective-programming-more-than-writing-code/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/effective-programming-more-than-writing-code/</guid>
      <description>ABOUT THE BOOK Jeff Atwood began the Coding Horror blog in 2004, and is convinced that it changed his life. He needed a way to keep track of software development over time - whatever he was thinking about or working on. He researched subjects he found interesting, then documented his research with a public blog post, which he could easily find and refer to later. Over time, increasing numbers of blog visitors found the posts helpful, relevant and interesting. Now, approximately 100,000 readers visit the blog per day and nearly as many comment and interact on the site. Effective Programming: More Than Writing Code is your one-stop shop for all things programming. Jeff writes with humor and understanding, allowing for both seasoned programmers and newbies to appreciate the depth of his research. From such posts as The Programmer&amp;rsquo;s Bill of Rights and Why Cant Programmers&amp;hellip; Program? to Working With the Chaos Monkey, this book introduces the importance of writing responsible code, the logistics involved, and how people should view it more as a lifestyle than a career. TABLE OF CONTENTS - Introduction - The Art of Getting Shit Done - Principles of Good Programming - Hiring Programmers the Right Way - Getting Your Team to Work Together - The Batcave: Effective Workspaces for Programmers - Designing With the User in Mind - Security Basics: Protecting Your Users&amp;rsquo; Data - Testing Your Code, So it Doesn&amp;rsquo;t Suck More Than it Has To - Building, Managing and Benefiting from a Community - Marketing Weasels and How Not to Be One - Keeping Your Priorities Straight EXCERPT FROM THE BOOK As a software developer, you are your own worst enemy. The sooner you realize that, the better off you&amp;rsquo;ll be.I know you have the best of intentions. We all do. We&amp;rsquo;re software developers; we love writing code. It&amp;rsquo;s what we do. We never met a problem we couldn&amp;rsquo;t solve with some duct tape, a jury-rigged coat hanger and a pinch of code. But Wil Shipley argues that we should rein in our natural tendencies to write lots of code: The fundamental nature of coding is that our task, as programmers, is to recognize that every decision we make is a trade-off. To be a master programmer is to understand the nature of these trade-offs, and be conscious of them in everything we write.In coding, you have many dimensions in which you can rate code: Brevity of codeFeaturefulnessSpeed of executionTime spent codingRobustnessFlexibility Now, remember, these dimensions are all in opposition to one another. You can spend three days writing a routine which is really beautiful and fast, so you&amp;rsquo;ve gotten two of your dimensions up, but you&amp;rsquo;ve spent three days, so the time spent coding dimension is way down.So, when is this worth it? How do we make these decisions? The answer turns out to be very sane, very simple, and also the one nobody, ever, listens to: Start with brevity. Increase the other dimensions as required by testing. I couldn&amp;rsquo;t agree more. I&amp;rsquo;ve given similar advice when I exhorted developers to Code Smaller. And I&amp;rsquo;m not talking about a reductio ad absurdum contest where we use up all the clever tricks in our books to make the code fit into less physical space. I&amp;rsquo;m talking about practical, sensible strategies to reduce the volume of code an individual programmer has to read to understand how a program works. Here&amp;rsquo;s a trivial little example of what I&amp;rsquo;m talking about: if (s == String.Empty)if (s == ) It seems obvious to me that the latter case is&amp;hellip; &amp;hellip;buy the book to read more!</description>
    </item>
    
    <item>
      <title>Everything I Know</title>
      <link>https://notestoself.dev/reading/everything-i-know/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/everything-i-know/</guid>
      <description>The authors provide the new teacher with guidance and advice that is full of encouragement, humor, and practical ideas, all based on real first-year experiences.</description>
    </item>
    
    <item>
      <title>Excession</title>
      <link>https://notestoself.dev/reading/excession/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/excession/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Head First Design Patterns</title>
      <link>https://notestoself.dev/reading/head-first-design-patterns/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/head-first-design-patterns/</guid>
      <description></description>
    </item>
    
    <item>
      <title>High Performance MySql</title>
      <link>https://notestoself.dev/reading/high-performance-mysql/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/high-performance-mysql/</guid>
      <description></description>
    </item>
    
    <item>
      <title>How to Stop Sucking and Be Awesome Instead</title>
      <link>https://notestoself.dev/reading/how-to-stop-sucking-and-be-awesome-instead/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/how-to-stop-sucking-and-be-awesome-instead/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Inviting Disaster</title>
      <link>https://notestoself.dev/reading/inviting-disaster/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/inviting-disaster/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Learning PHP Design Patterns</title>
      <link>https://notestoself.dev/reading/learning-php-design-patterns/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/learning-php-design-patterns/</guid>
      <description>With the design patterns in this book, you�ll be able to build PHP applications much more efficiently. Learning PHP Design Patterns shows you how to use patterns through simple examples, and then demonstrates many of them in full-fledged working applications. You�ll learn patterns that help you connect PHP and MySQL, as well as several pattern categories that encapsulate object-oriented programming practices and concepts, such as polymorphism.These patterns allow you to adopt a more sophisticated programming style, focusing on language improvements introduced in PHP 5. You�ll also learn patterns that help you avoid common programming problems.</description>
    </item>
    
    <item>
      <title>Matter</title>
      <link>https://notestoself.dev/reading/matter/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/matter/</guid>
      <description>In a world renowned even within a galaxy full of wonders, a crime within a war. For one man it means a desperate flight, and a search for the one - maybe two - people who could clear his name. For his brother it means a life lived under constant threat of treachery and murder. And for their sister, even without knowing the full truth, it means returning to a place she&amp;rsquo;d thought abandoned forever.Only the sister is not what she once was; Djan Seriy Anaplian has changed almost beyond recognition to become an agent of the Culture&amp;rsquo;s Special Circumstances section, charged with high-level interference in civilizations throughout the greater galaxy.Concealing her new identity - and her particular set of abilities - might be a dangerous strategy, however. In the world to which Anaplian returns, nothing is quite as it seems; and determining the appropriate level of interference in someone else&amp;rsquo;s war is never a simple matter.MATTER is a novel of dazzling wit and serious purpose. An extraordinary feat of storytelling and breathtaking invention on a grand scale, it is a tour de force from a writer who has turned science fiction on its head.</description>
    </item>
    
    <item>
      <title>Never Let Me Go</title>
      <link>https://notestoself.dev/reading/never-let-me-go/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/never-let-me-go/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Predictably Irrational</title>
      <link>https://notestoself.dev/reading/predictably-irrational/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/predictably-irrational/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Algebraist</title>
      <link>https://notestoself.dev/reading/the-algebraist/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-algebraist/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Emperor Far Away</title>
      <link>https://notestoself.dev/reading/the-emperor-far-away/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-emperor-far-away/</guid>
      <description>For more than two thousand years, as capital of the powerful Nan Yue kingdom to its present position as the largest city in southern China, Canton has been one of Asia&amp;rsquo;s most important commercial, political, and cultural centres. Arab merchants first arrived in the Tang dynasty, trading spices, amber, and ivory for silk, ceramics, and precious stones. During the eighteen century, for the next hundred years, it was the only port in China where the emperor allowed merchants from the West to do business. With trade came the interchange of ideas. Missionaries, educators, political leaders, and modernizers, Chinese and non-Chinese alike, all tapped Canton&amp;rsquo;s energy in their efforts to direct the course of China&amp;rsquo;s cultural and political development.</description>
    </item>
    
    <item>
      <title>The Establishment, and How They Get Away With It</title>
      <link>https://notestoself.dev/reading/the-establishment/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-establishment/</guid>
      <description>Why they cheat&amp;hellip; Presidents, religious leaders, CEOs, school teachers, moms, dads, young, old&amp;hellip;they all do it and it&amp;rsquo;s all too common even in what many of us would consider &amp;lsquo;great&amp;rsquo; relationships-but why? One recent survey showed that nearly 70% of respondents in self-described monogamous relationships have confessed they&amp;rsquo;ve either thought about cheating, have cheated, or are currently cheating&amp;hellip;and their partner has no idea. In Why They Cheat and How They Get Away With It, J.L. Ford explains why almost every relationship is at risk and exposes that aspect of human nature that compels otherwise responsible men and women to act on the impulse to cheat-regardless of the personal or professional consequences.</description>
    </item>
    
    <item>
      <title>The Hydrogen Sonata</title>
      <link>https://notestoself.dev/reading/the-hydrogen-sonata/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-hydrogen-sonata/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Player of Games</title>
      <link>https://notestoself.dev/reading/the-player-of-games/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-player-of-games/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Pragmatic Programmer</title>
      <link>https://notestoself.dev/reading/the-pragmatic-programmer/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-pragmatic-programmer/</guid>
      <description>Straight from the programming frenches, The Pragmatic Programmer cuts through the increasing specialization and technicalities of modern software development to examine the core process - taking a requirement and producing working, maintainable code that delights its users. It covers topics ranging from personal responsibility and career development to architectural techniques for keeping your code flexible and easy to adapt and reuse.&amp;ndash;BOOK JACKET.</description>
    </item>
    
    <item>
      <title>The State of the Art</title>
      <link>https://notestoself.dev/reading/the-state-of-the-art/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-state-of-the-art/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Undercover Economist</title>
      <link>https://notestoself.dev/reading/the-undercover-economist/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-undercover-economist/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Way to Go</title>
      <link>https://notestoself.dev/reading/the-way-to-go/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-way-to-go/</guid>
      <description>This book provides the reader with a comprehensive overview of the new open source programming language Go (in its first stable and maintained release Go 1) from Google. The language is devised with Java / C#-like syntax so as to feel familiar to the bulk of programmers today, but Go code is much cleaner and simpler to read, thus increasing the productivity of developers. You will see how Go: simplifies programming with slices, maps, structs and interfaces incorporates functional programming makes error-handling easy and secure simplifies concurrent and parallel programming with goroutines and channels And you will learn how to: make use of Go&amp;rsquo;s excellent standard library program Go the idiomatic way using patterns and best practices in over 225 working examples and 135 exercises This book focuses on the aspects that the reader needs to take part in the coming software revolution using Go.</description>
    </item>
    
    <item>
      <title>Thinking Statistically</title>
      <link>https://notestoself.dev/reading/thinking-statistically/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/thinking-statistically/</guid>
      <description>The paperback printing of Thinking Statistically includes all the material from the Kindle original plus innovative new segments giving graphical representations of statistics concepts. Thinking Statistically is the book that shows you how to think like a statistician, without worrying about formal statistical techniques. Along the way we learn how selection bias can explain why your boss doesn�t know he sucks (even when everyone else does); how to use Bayes� Theorem to decide if your partner is cheating on you; and why Mark Zuckerberg should never be used as an example for anything. See the world in a whole new light, and make better decisions and judgements without ever going near a t-test. Think. Think Statistically.</description>
    </item>
    
    <item>
      <title>三分鍾小說</title>
      <link>https://notestoself.dev/reading/%E4%B8%89%E5%88%86%E9%8D%BE%E5%B0%8F%E8%AA%AA/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/%E4%B8%89%E5%88%86%E9%8D%BE%E5%B0%8F%E8%AA%AA/</guid>
      <description></description>
    </item>
    
    <item>
      <title>遊戲玩家</title>
      <link>https://notestoself.dev/reading/%E9%81%8A%E6%88%B2%E7%8E%A9%E5%AE%B6/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/%E9%81%8A%E6%88%B2%E7%8E%A9%E5%AE%B6/</guid>
      <description></description>
    </item>
    
    <item>
      <title>駱駝祥子</title>
      <link>https://notestoself.dev/reading/%E9%A7%B1%E9%A7%9D%E7%A5%A5%E5%AD%90/</link>
      <pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/%E9%A7%B1%E9%A7%9D%E7%A5%A5%E5%AD%90/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Straightforward recursive number-to-words algorithm in TypeScript</title>
      <link>https://notestoself.dev/posts/straightforward-recursive-number-to-words-function-typescript/</link>
      <pubDate>Fri, 26 Dec 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/straightforward-recursive-number-to-words-function-typescript/</guid>
      <description>This is a recursive number-to-words algorithm in TypeScript. It tries to be straightforward and just uses if statements to make the logic a bit easier to follow.
The key ideas are:
 Let the recursion do the work. Handle &amp;ldquo;and&amp;rdquo; carefully. Don&amp;rsquo;t move up in factors of ten; move up in word groups.  function numberToWords(num: number): string { if (num &amp;lt; 0) { return `minus ${numberToWords(-num)}`; } if (num in DIRECT) { return DIRECT[num]; } if (num &amp;lt; 100) { const units = num % 10; const tens = num - units; return `${DIRECT[tens]}${numberToWords(units)}`; } if (num &amp;lt; 1000) { const sub_hundreds = num % 100; const hundreds = (num - sub_hundreds) / 100; let words = `${numberToWords(hundreds)}hundred`; if (sub_hundreds) { words += ` and ${numberToWords(sub_hundreds)}`; } return words; } if (num &amp;lt; 1000000) { const sub_thous = num % 1000; const thousands = (num - sub_thous) / 1000; let words = `${numberToWords(thousands)}thousand`; if (sub_thous) { if (sub_thous &amp;lt; 100) { words += &amp;#34; and&amp;#34;; } words += ` ${numberToWords(sub_thous)}`; } return words; } if (num &amp;lt; 1000000000) { const sub_mills = num % 1000000; const millions = (num - sub_mills) / 1000000; let words = `${numberToWords(millions)}million`; if (sub_mills) { if (sub_mills &amp;lt; 100) { words += &amp;#34; and&amp;#34;; } words += ` ${numberToWords(sub_mills)}`; } return words; } throw new Error(&amp;#34;I can&amp;#39;t count that high sorry&amp;#34;); } Jest tests:</description>
    </item>
    
    <item>
      <title>TypeScript recursive power function</title>
      <link>https://notestoself.dev/posts/typescript-recursive-array-product-function-algorithm/</link>
      <pubDate>Fri, 26 Dec 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/typescript-recursive-array-product-function-algorithm/</guid>
      <description>This is a productOfArray(numbers) function in TypeScript that uses recursion.
function productOfArray(numbers: number[]): number { if (numbers.length === 0) { return 0; } if (numbers.length === 1) { return numbers[0]; } return numbers[0] * productOfArray(numbers.slice(1)); } Jest tests:
describe(productOfArray.name, () =&amp;gt; { test(&amp;#34;multiplies array elements together&amp;#34;, () =&amp;gt; { expect(productOfArray([])).toBe(0); expect(productOfArray([5])).toBe(5); expect(productOfArray([1, 2, 3])).toBe(6); expect(productOfArray([5, 5, 5])).toBe(125); expect(productOfArray([8, 8])).toBe(64); expect(productOfArray([1, 2, 3, 4, 5, 6, 7, 8, 9])).toBe(362880); }); }); </description>
    </item>
    
    <item>
      <title>TypeScript recursive factorial function</title>
      <link>https://notestoself.dev/posts/typescript-recursive-factorial-function-algorithm/</link>
      <pubDate>Thu, 25 Dec 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/typescript-recursive-factorial-function-algorithm/</guid>
      <description>This is a factorial(factor) function in TypeScript that uses recursion.
function factorial(factor: number): number { if (factor &amp;lt;= 1) { return 1; } return factor * factorial(factor - 1); } Jest tests:
describe(factorial.name, () =&amp;gt; { test(&amp;#34;recursive factorial&amp;#34;, () =&amp;gt; { expect(factorial(0)).toBe(1); expect(factorial(1)).toBe(1); expect(factorial(2)).toBe(2); expect(factorial(3)).toBe(6); expect(factorial(4)).toBe(24); expect(factorial(5)).toBe(120); expect(factorial(6)).toBe(720); expect(factorial(7)).toBe(5040); }) }); </description>
    </item>
    
    <item>
      <title>TypeScript recursive power function</title>
      <link>https://notestoself.dev/posts/typescript-recursive-power-function-algorithm/</link>
      <pubDate>Wed, 24 Dec 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/typescript-recursive-power-function-algorithm/</guid>
      <description>This is a power(base, exponent) function in TypeScript that uses recursion instead of the Math.pow() function.
function power(base: number, exponent: number): number { if (exponent === 0) { return 1; } return base * power(base, exponent - 1); } Jest tests:
describe(&amp;#34;power&amp;#34;, () =&amp;gt; { test(&amp;#34;2^x&amp;#34;, () =&amp;gt; { expect(power(2, 0)).toBe(1); expect(power(2, 1)).toBe(2); expect(power(2, 2)).toBe(4); expect(power(2, 3)).toBe(8); expect(power(2, 4)).toBe(16); }) test(&amp;#34;3^x&amp;#34;, () =&amp;gt; { expect(power(3, 0)).toBe(1); expect(power(3, 1)).toBe(3); expect(power(3, 2)).toBe(9); expect(power(3, 3)).</description>
    </item>
    
    <item>
      <title>A simple string obscure function in PHP</title>
      <link>https://notestoself.dev/posts/php-string-obscure-function/</link>
      <pubDate>Tue, 23 Dec 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/php-string-obscure-function/</guid>
      <description>It&amp;rsquo;s quite common to need to obscure sensitive information when displaying it to a client. For example, it&amp;rsquo;s safer to display a user&amp;rsquo;s email address as h***.g****@g****.c**. If someone views the page, the user&amp;rsquo;s personal information isn&amp;rsquo;t fully revealed, but the user can still confirm that it&amp;rsquo;s correct.
This post is just a note of a helper function to do this in PHP.
&amp;lt;?php /** * @param string|string[] $plain * @param int $revealStart * @param int $revealEnd * @param string $obscuration * * @return string|string[] */ function obscure( $plain, int $revealStart = 1, int $revealEnd = 0, string $obscuration = &amp;#39;*&amp;#39; ) { if (is_array($plain)) { return array_map( function ($plainPart) use ($revealStart, $revealEnd, $obscuration) { return obscure($plainPart, $revealStart, $revealEnd, $obscuration); }, $plain ); } $plain = (string) $plain; return mb_substr($plain, 0, $revealStart) .</description>
    </item>
    
    <item>
      <title>The observer pattern and side effects</title>
      <link>https://notestoself.dev/posts/observer-pattern-side-effects/</link>
      <pubDate>Wed, 05 Nov 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/observer-pattern-side-effects/</guid>
      <description>The observer pattern is extremely common in a lot of software, particularly in web apps. Most web frameworks, ORMs and other boilerplate systems have a built in structure for events and observers, making it easy to listen to various built-in events and to fire customised events of your own.
The observer pattern is one of the classic design patterns &amp;ndash; it&amp;rsquo;s number 7 in the Gang of Four &amp;ndash; and is one of the patterns that I deal with most commonly day to day.</description>
    </item>
    
    <item>
      <title>Initial commit</title>
      <link>https://notestoself.dev/posts/initial-commit/</link>
      <pubDate>Thu, 23 Oct 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/initial-commit/</guid>
      <description>This is the first post on my new blog. I&amp;rsquo;ll be writing about various unrelated things that happen to interest me at the time. This blog used to be on WordPress, then Jekyll hosted on Github pages, and this iteration uses Hugo and AWS for hosting. I believe I&amp;rsquo;ll stick with Hugo and AWS for some time as I&amp;rsquo;ve never enjoyed a CMS and hosting setup as much as this.</description>
    </item>
    
    <item>
      <title>Learning vocabulary on the command line</title>
      <link>https://notestoself.dev/posts/learning-vocabulary-on-the-commandline/</link>
      <pubDate>Thu, 23 Oct 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/learning-vocabulary-on-the-commandline/</guid>
      <description>I like the idea of adding little bits of learning to my day that don&amp;rsquo;t take much effort once I&amp;rsquo;ve set them up. They&amp;rsquo;re not the most effective way to learn, but they certainly don&amp;rsquo;t hurt and every now and then you do get something worthwhile out of them.
One of the things I&amp;rsquo;ve set up recently is displaying random Chinese vocabulary items at the start of every command line session in Zsh.</description>
    </item>
    
    <item>
      <title>LoveCrafts</title>
      <link>https://notestoself.dev/work/lovecrafts/</link>
      <pubDate>Mon, 22 Sep 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/lovecrafts/</guid>
      <description>In 2014-2015, I worked on the ecommerce platform, which is based on a heavily customised Magento EE installation with integrations to other business systems.
I then worked on the Social API team through to 2018, building a central APIs and microservices for the LoveCrafts social network using code-generation techniques.</description>
    </item>
    
    <item>
      <title>China Checkup</title>
      <link>https://notestoself.dev/work/china-checkup/</link>
      <pubDate>Thu, 23 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/china-checkup/</guid>
      <description>I was the sole developer on ChinaCheckup.com from 2013 to 2014, handling all front-end and back-end development, as well as managing the server.
China Checkup sells complex business analysis services to clients outside of China. The site is a custom ecommerce solution based on the Laravel framework. It automates much of the workflow in creating, modifying and selling these services to clients.
Some time after I left the company, China Checkup migrated to the Shopify platform.</description>
    </item>
    
    <item>
      <title>Longest distinct substring algorithm in JavaScript</title>
      <link>https://notestoself.dev/posts/longest-distinct-substring-algorithm-javascript/</link>
      <pubDate>Thu, 09 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/longest-distinct-substring-algorithm-javascript/</guid>
      <description>Given a string, find the longest substring of distinct characters.
function findLongestDistinctSubstring(text) { const lastPos = {}; let maxLength = 0; let left = 0; for (let right = 0; right &amp;lt; text.length; right++) { const char = text[right]; if (char in lastPos) { // Can&amp;#39;t go backwards, so use max() to ensure  left = Math.max(left, lastPos[char] + 1); } lastPos[char] = right; maxLength = Math.max(maxLength, (right - left) + 1); } return maxLength; } This solution has $O(n)$ time complexity because we iterate the input string once, and $O(n)$ space complexity because we build up a mapping of input characters to positions.</description>
    </item>
    
    <item>
      <title>Min subarray length algorithm in JavaScript</title>
      <link>https://notestoself.dev/posts/min-sub-array-length-algorithm-javascript/</link>
      <pubDate>Wed, 08 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/min-sub-array-length-algorithm-javascript/</guid>
      <description>Given an array of positive intergers and a positive integer target, return the length of the shortest possible contiguos sub-array with a sum greater than or equal to the target.
function minSubArrayLen(items, targ) { let left = 0; let right = 0; let currSum = 0; let minLen = Infinity; while (left &amp;lt; items.length) { if (currSum &amp;lt; targ) { if (right === items.length) { break; } currSum += items[right]; right++; } else { minLen = Math.</description>
    </item>
    
    <item>
      <title>Max sub array sum algorithm in JavaScript</title>
      <link>https://notestoself.dev/posts/max-sub-array-sum-algorithm-javascript/</link>
      <pubDate>Tue, 07 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/max-sub-array-sum-algorithm-javascript/</guid>
      <description>Given an array of numbers and an integer &amp;ldquo;width&amp;rdquo;, find the max sum of a contiguous sub-array with that width in positions.
function maxSubArraySum(numbers, width) { if (width &amp;gt;= numbers.length) { return numbers.reduce((sum, num) =&amp;gt; sum + num, 0) } let subArraySum = numbers.slice(0, width).reduce((sum, num) =&amp;gt; sum + num, 0); let maxSubArraySum = subArraySum; for (let t = 0, h = width; h &amp;lt; numbers.length; t++, h++) { subArraySum -= numbers[t]; subArraySum += numbers[h]; maxSubArraySum = Math.</description>
    </item>
    
    <item>
      <title>String is subsequence algorithm in JavaScript</title>
      <link>https://notestoself.dev/posts/string-is-subsequence-algorithm-javascript/</link>
      <pubDate>Mon, 06 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/string-is-subsequence-algorithm-javascript/</guid>
      <description>Given two strings needle and haystack, check if needle is a subsequence (not necessarily a substring) of haystack at any point. That means that the letters in needle must appear in haystack in the same order, but not necessarily contiguously.
function isSubsequence(needle, haystack) { if (!needle) { return true; } if (haystack.length &amp;lt; needle.length) { return true; } if (haystack === needle) { return true; } let ni = 0; let hi = 0; while (hi &amp;lt; haystack.</description>
    </item>
    
    <item>
      <title>Check for target average pair in sorted array algorithm in JavaScript</title>
      <link>https://notestoself.dev/posts/sorted-array-average-pair-algorithm-javascript/</link>
      <pubDate>Sun, 05 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/sorted-array-average-pair-algorithm-javascript/</guid>
      <description>Given a sorted array of numbers and a target average value, return whether or not it is possible to achieve the target average value with a pair of numbers from the array.
function canMakeAveragePair(items, target) { let t = 0; let h = items.length - 1; while (t &amp;lt; h) { const avg = (items[t] + items[h]) / 2; if (avg === target) { return true; } else if (avg &amp;lt; target) { t++; } else if (avg &amp;gt; target) { h--; } } return false; } This algorithm is $O(n)$ time and $O(1)$ space.</description>
    </item>
    
    <item>
      <title>Duplicate detection algorithm in JavaScript</title>
      <link>https://notestoself.dev/posts/numbers-digits-same-frequency-algorithm-javascript/</link>
      <pubDate>Sat, 04 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/numbers-digits-same-frequency-algorithm-javascript/</guid>
      <description>Here&amp;rsquo;s a super simple duplicate detection algorithm in JavaScript:
function hasDuplicate(items) { const seen = {}; for (item of items) { if (item in seen) { return true; } seen[item] = true; } return false; } This algorithm is $O(n)$ time and $O(n)$ space.</description>
    </item>
    
    <item>
      <title>Count unique values algorithm in JavaScript</title>
      <link>https://notestoself.dev/posts/count-unique-values-algorithm-javascript/</link>
      <pubDate>Fri, 03 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/count-unique-values-algorithm-javascript/</guid>
      <description>Given a sorted array of values like [1, 1, 2, 3, 3, 4, 5, 5, 5], count the number of unique values in $O(n)$ time and $O(1)$ space.
If there wasn&amp;rsquo;t a requirement to do this in $O(1)$ space, the most straightforward way to do it would be to use a Set(). We could iterate the input array adding each element to the set, then return set.size. This would be $O(n)$ time but $O(n)$ space, so it wouldn&amp;rsquo;t meet the space requirement.</description>
    </item>
    
    <item>
      <title>Valid anagram algorithm in JavaScript</title>
      <link>https://notestoself.dev/posts/valid-anagram-algorithm-javascript/</link>
      <pubDate>Thu, 02 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/valid-anagram-algorithm-javascript/</guid>
      <description>Given two strings such as &amp;quot;listen&amp;quot; and &amp;quot;silent&amp;quot;, return whether or not they are valid anagrams of each other. That is, the second word uses the same letters as the first but potentially in a different order.
One approach is to make a frequency count of the first string, and then iterate the second string &amp;ldquo;checking off&amp;rdquo; each letter from the frequency count of the first. If we hit a missing letter or reduce the corresponding frequency count to zero, we know it&amp;rsquo;s not a valid anagram.</description>
    </item>
    
    <item>
      <title>Number digits same frequency algorithm in JavaScript</title>
      <link>https://notestoself.dev/posts/numbers-digits-same-frequency-algorithm-javascript/</link>
      <pubDate>Wed, 01 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/posts/numbers-digits-same-frequency-algorithm-javascript/</guid>
      <description>Given two numbers such as 1987456 and 5796184, check if the two numbers have the same frequency of digits. That is, they have the same digits the same number of times. Another way of putting this would be to check if the digits of the two numbers are anagrams of each other or not.
function sameFrequency(a, b) { a = String(a); b = String(b); if (a.length !== b.length) { return false; } const freq_a = {}; const freq_b = {}; for (let i = 0; i &amp;lt; a.</description>
    </item>
    
    <item>
      <title>Personal blog</title>
      <link>https://notestoself.dev/work/hugh-grigg-com/</link>
      <pubDate>Wed, 23 Oct 2013 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/hugh-grigg-com/</guid>
      <description>The site you&amp;rsquo;re on now :-)</description>
    </item>
    
    <item>
      <title>Chinese Boost</title>
      <link>https://notestoself.dev/work/chinese-boost/</link>
      <pubDate>Thu, 23 May 2013 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/chinese-boost/</guid>
      <description>As East Asia Student got increasingly eclectic and unfocused, I decided to branch off the &amp;lsquo;how to learn Chinese&amp;rsquo; content to this new site. Like my other text-based sites, Chinese Boost is built using Hugo and hosted with AWS.
I get quite a lot of nice feedback from people saying how useful they find Chinese Boost.</description>
    </item>
    
    <item>
      <title>East Asia Student</title>
      <link>https://notestoself.dev/work/east-asia-student/</link>
      <pubDate>Wed, 05 May 2010 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/work/east-asia-student/</guid>
      <description>East Asia Student was more active whilst I was at university, but I still add new content from time to time. The site is now built using Hugo and hosted with AWS.</description>
    </item>
    
    <item>
      <title>1440 Daily Digest</title>
      <link>https://notestoself.dev/reading/1440-daily-digest/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/1440-daily-digest/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Aeon</title>
      <link>https://notestoself.dev/reading/aeon/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/aeon/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Book Freak</title>
      <link>https://notestoself.dev/reading/book-freak/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/book-freak/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Delancey Place</title>
      <link>https://notestoself.dev/reading/delancey-place/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/delancey-place/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Freek.dev</title>
      <link>https://notestoself.dev/reading/freek.dev/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/freek.dev/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Hacker News Digest</title>
      <link>https://notestoself.dev/reading/hacker-news-digest/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/hacker-news-digest/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Hacker Newsletter</title>
      <link>https://notestoself.dev/reading/hacker-newsletter/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/hacker-newsletter/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Hire Me</title>
      <link>https://notestoself.dev/hire-me/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/hire-me/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Inkstone</title>
      <link>https://notestoself.dev/reading/inkstone/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/inkstone/</guid>
      <description></description>
    </item>
    
    <item>
      <title>James Clear 3-2-1</title>
      <link>https://notestoself.dev/reading/james-clear-3-2-1/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/james-clear-3-2-1/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Laravel News</title>
      <link>https://notestoself.dev/reading/laravel-news/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/laravel-news/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Marginal Revolution</title>
      <link>https://notestoself.dev/reading/marginal-revolution/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/marginal-revolution/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Monevator</title>
      <link>https://notestoself.dev/reading/monevator/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/monevator/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Morning Brew</title>
      <link>https://notestoself.dev/reading/morning-brew/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/morning-brew/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Mr Money Mustache</title>
      <link>https://notestoself.dev/reading/mr-money-mustache/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/mr-money-mustache/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Nautilus</title>
      <link>https://notestoself.dev/reading/nautilus/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/nautilus/</guid>
      <description></description>
    </item>
    
    <item>
      <title>News Refinery</title>
      <link>https://notestoself.dev/reading/news-refinery/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/news-refinery/</guid>
      <description></description>
    </item>
    
    <item>
      <title>NextDraft</title>
      <link>https://notestoself.dev/reading/next-draft/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/next-draft/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Ribbonfarm</title>
      <link>https://notestoself.dev/reading/ribbonfarm/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/ribbonfarm/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Sinocism</title>
      <link>https://notestoself.dev/reading/sinocism/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/sinocism/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Slate Star Codex</title>
      <link>https://notestoself.dev/reading/slate-star-codex/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/slate-star-codex/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Economist</title>
      <link>https://notestoself.dev/reading/the-economist/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-economist/</guid>
      <description></description>
    </item>
    
    <item>
      <title>The Week</title>
      <link>https://notestoself.dev/reading/the-week/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/the-week/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Tim Harford</title>
      <link>https://notestoself.dev/reading/tim-harford/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/tim-harford/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Typescript Weekly</title>
      <link>https://notestoself.dev/reading/typescript-weekly/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/typescript-weekly/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Unintended Consequences</title>
      <link>https://notestoself.dev/reading/unintended-consequences/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://notestoself.dev/reading/unintended-consequences/</guid>
      <description></description>
    </item>
    
  </channel>
</rss>