<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Coding with Jesse</title>
        <link>https://codingwithjesse.com/</link>
        <description>Svelte, JavaScript, CSS, HTML, and anything else of interest to standards-loving web designers and developers</description>
        <lastBuildDate>Wed, 29 May 2024 20:12:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Coding with Jesse</title>
            <url>https://www.codingwithjesse.com/images/me_umbrella.jpg</url>
            <link>https://codingwithjesse.com/</link>
        </image>
        <copyright>Copyright 2024, Jesse Skinner</copyright>
        <item>
            <title><![CDATA[If an error is logged in the cloud, does it make a sound?]]></title>
            <link>https://www.codingwithjesse.com/blog/if-an-error-is-logged-in-the-cloud/</link>
            <guid>https://www.codingwithjesse.com/blog/if-an-error-is-logged-in-the-cloud/</guid>
            <pubDate>Wed, 29 May 2024 20:12:00 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2024-05-29-if-an-error-is-logged.jpg" width="800" height="400">

<p>If a user sees an error message on your web server, how do you find out?</p>
<p>Does the user report it directly, if they&#39;re friendly enough?
Do you read about it on social media, if they&#39;re frustrated enough?
Or do you receive a notification directly from the server?</p>
<p>I remember one of my first jobs at Strato, a web hosting company in Germany.
When we deployed a new version of our content management system, we&#39;d log onto the web server and <a href="https://www.codingwithjesse.com/blog/watching-logs-with-tail/">tail the logs</a> to see if any errors appeared.
We wanted to see if we broke anything.</p>
<p>I remember scrolling through this web server error log.
I&#39;d see hundreds of different errors from before we deployed.
When I asked the team about them, they weren&#39;t really sure about them.
&quot;Nobody has complained about these things,&quot; they said.</p>
<p>Seeing errors and warnings in logs made me uncomfortable.
Something was broken, and we weren&#39;t doing anything about it.
I made it my mission and took a few days to go and fix every error I could.
I felt that unhandled errors were unacceptable.
I wanted the error log to be empty.</p>
<p>Ever since then, I&#39;ve always set up error monitoring on any server I manage.
Whenever an error is logged, I want it emailed to me immediately.
I usually run a cronjob that scans the error log and sends out emails any new entries every minute.
It might strip out some common, unavoidable networking errors.
But for any unexpected errors, I want to be the first to know.</p>
<p>Whenever a user trips over an obscure bug, I know before the user has time to tell me.
When things break badly, and the error log starts filling up, I know immediately.</p>
<p>Whether you&#39;re running Apache, Nginx or serverless functions, have you looked at your error logs lately?
If not, go take a look.
You might be surprised what you find.</p>
<p>Make it your team&#39;s goal to get those errors down to zero.
Set up a cron job so those errors get sent to your inbox.
Or, sign up for a log monitoring service that makes this easier for you.</p>
<p>Don&#39;t wait for your users to complain.</p>
<hr/><p>Interested in web development? <a href="https://www.codingwithjesse.com/newsletter">Subscribe to the Coding with Jesse newsletter!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[You don't need permission]]></title>
            <link>https://www.codingwithjesse.com/blog/you-dont-need-permission/</link>
            <guid>https://www.codingwithjesse.com/blog/you-dont-need-permission/</guid>
            <pubDate>Wed, 22 May 2024 17:46:33 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2024-05-22-you-dont-need-permission.jpg" width="800" height="400" alt="Watercolour illustration of a construction worker building a structure"/>

<p>You don&#39;t need permission to write the highest quality code you can.
You don&#39;t need permission to design a reliable server architecture that won&#39;t crash.
You don&#39;t need permission to develop a suite of tests to ensure bugs are caught early.
You don&#39;t need permission to upgrade your dependencies, to ensure your system stays secure and modern.</p>
<p>Your boss, manager or client will never ask you to take time to refactor your code.
They&#39;ll never ask you to set up a test suite for the code you wrote.
They&#39;ll never ask you to upgrade your framework.</p>
<p>It&#39;s not that they don&#39;t want you to do things to improve the quality of the code.
It&#39;s because they expect you to write high quality code from the start.
They believe you to know what it takes to design a reliable system.
They trust you to build <a href="https://www.codingwithjesse.com/blog/web-apps-that-last/">web apps that last</a>.</p>
<p>They&#39;ll never ask you to make sure your code has no bugs.
They&#39;ll never ask you to make sure the new system doesn&#39;t crash.
They&#39;ll never ask you to make sure your code will be understood by other developers.
It all goes without saying.</p>
<p>So, don&#39;t put these things off for later.
Don&#39;t put writing tests on the backlog.
Don&#39;t put refactoring on your list of nice-to-haves.
Don&#39;t put improving the reliability of your servers on the wishlist.
Don&#39;t wait for permission.</p>
<p>Do these things every day.
Make them a part of your process.
You don&#39;t need permission.</p>
<hr/><p>Interested in web development? <a href="https://www.codingwithjesse.com/newsletter">Subscribe to the Coding with Jesse newsletter!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Coding with ChatGPT]]></title>
            <link>https://www.codingwithjesse.com/blog/coding-with-chatgpt/</link>
            <guid>https://www.codingwithjesse.com/blog/coding-with-chatgpt/</guid>
            <pubDate>Sun, 23 Apr 2023 18:24:08 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2023-04-23-coding-with-chatgpt.png" alt="Cartoon of a happy laptop surrounded by snippets of code" width="800" height="400">

<p>I started using ChatGPT when it came out a few months ago. It was mind blowing to chat with a computer and have it feel almost like a real person.</p>
<p>Some people are talking about how it&#39;s going to replace all sorts of jobs, including software developers. I&#39;m not sure about that. But I have found some ways that it can definitely make our jobs easier, if we understand how it works and use it cautiously.</p>
<h2 id="understanding-the-limitations">Understanding the limitations</h2>
<p>Like all Large Language Models (LLMs), ChatGPT has been trained on a massive quantity of text from the Internet. Its basically a function which takes a context as input, including your prompt and the rest of your chat log, up to a limit of roughly 2,000 words. Based on that context, it is trying to make an educated <em>guess</em> of what should come next. Specifically, it&#39;s trying to predict what the people who trained it would have voted as the best response.</p>
<p>So when you&#39;re using it for coding, or anything else, always keep in mind that it is a <em>guessing machine</em>. Yes, it has been trained with a large amount of information, but not all that information is correct or up to date, and, most importantly, it&#39;s very good at completely making things up while exuding confidence.</p>
<p>It&#39;s amazing that ChatGPT can run some statistical analysis on words written by humans, and suddenly people are wondering if this thing is going to take over the world (no), take our jobs (maybe), or become self aware (definitely not). Statistically analyzing text becomes an excellent way to imitate humans, and come up with text that looks extremely <em>plausible</em>. GPT probably stands for &quot;Guessing Plausible Text&quot; (j/k).</p>
<p>Unfortunately, in programming, plausible doesn&#39;t cut it. We need things to be precisely accurate. It can quickly become frustrating to use an LLM as a tool to help you with programming, because it&#39;s wrong so often.</p>
<p>There&#39;s still hope. I&#39;ve found it can still be a very powerful and helpful tool, even for developers.</p>
<h2 id="researching-with-chatgpt">Researching with ChatGPT</h2>
<p>I think a very natural but dangerous way to use ChatGPT is as a search engine, to ask it factual questions. The problem here is that it&#39;s like playing &quot;two truths and a lie&quot;. A lot of what it says is certainly true, but there&#39;s absolutely no way to know which parts are completely made up.</p>
<p>Even knowing this, I find myself using it this way anyway, but with a caveat. You need to treat ChatGPT as if it&#39;s your know-it-all friend who will go on and on confidently about any topic, even ones he is actually clueless about. I&#39;ve learned about lots of new tools and features with ChatGPT, and some of them really did exist!</p>
<p>One trick is to ask for references. This is as simple as adding &quot;Give references.&quot; to your prompts, or asking for them after. For coding topics, ChatGPT will usually be able to give you URLs you can click on to specific official documentation, and that is very useful.</p>
<p>Clicking those links to follow-up is absolutely critical here, because very often ChatGPT has told me how to do something using some specific API or function, and it has turned out to have been making it up. These situations did <em>not</em> save me any time, it actually wasted my time.</p>
<p>All that said, I love how ChatGPT can introduce me to all sorts of things I&#39;ve never heard of before. Searching on Google would have required me clicking on dozens of semi-related pages and skimming through. ChatGPT is excellent at summarizing content, so you can take advantage of that.</p>
<p>Here&#39;s where ChatGPT can really shine: Let&#39;s say you have some specific software architectural challenge in front of you and you&#39;re not sure how to approach it. Open up ChatGPT and write it out in as much detail as you can.</p>
<p>&quot;I need to build on online web-based chat interface. There will be a small number of users, and I&#39;m not sure which database to use to manage this. I&#39;m using AWS for web hosting and I&#39;m hoping to find a serverless solution to save money. I&#39;m familiar with JavaScript and Python. What are some tech stacks I could use for this? Provide references.&quot;</p>
<p>Seconds later, you&#39;ll have a list of options, some of which you may not have heard of, and links to read more about each one. If there&#39;s one you like, or if you have any follow up questions, you can just say &quot;Tell me more about #2&quot;. Or you can provide more detail with your specific requirements to refine it&#39;s suggestions.</p>
<p>You always need to be careful, because I find that the more specific you get, the more likely you&#39;re going to encourage it to make up something that doesn&#39;t exist. Always ask for references, and don&#39;t make a decision until you&#39;ve followed up on other websites to verify what ChatGPT says.</p>
<h2 id="transforming-code-and-text">Transforming code and text</h2>
<p>There are some low-risk and highly effective uses of ChatGPT, and transforming content is one of them. You can paste in some code or text, and ask it to rewrite it in some specific way. In these cases, it seems much less likely to make an error, and if it does make a mistake, you should be able to recognize it and refine your request quickly.</p>
<p>I&#39;ve pasted in a JavaScript file with two dozen constant strings defined, and asked to convert all the variable names to uppercase. At first it converted both the variable names and the string contents to uppercase, so I had to be more specific and tell it to leave the strings alone. Then it completed it quickly and accurately, saving me a few mindless minutes.</p>
<p>I&#39;ve pasted in an email from a client with a list of described menu options, plus a snippet of Svelte code with a few placeholders in the menu, and asked ChatGPT to add all the menu options into the code. It handled this very well.</p>
<p>You can ask it to rewrite a short function from JavaScript to Python, and it will do a good job of this as well, though it can make some mistakes depending on the complexity or the length of the code.</p>
<p>If you ever have these sorts of straightforward boring text transformation jobs in front of you, and your IDE isn&#39;t up to the job, try asking ChatGPT to do it for you, and save the headache.</p>
<h2 id="understanding--improving-code">Understanding &amp; improving code</h2>
<p>ChatGPT is excellent at summarizing any type of content, and that includes code.</p>
<p>Just paste in a chunk of code and it&#39;ll be able to tell you what the code does.
You can ask it to add inline comments to the code for you too, though Copilot is quite good at this too.</p>
<p>If you get a weird error message, ChatGPT might be able to give you an explanation of why the error might have happened, and some possible ways to fix the error.
Unlike a webpage, you can ask follow up questions in realtime and get feedback to help you find a solution.</p>
<p>I&#39;ve also had success pasting in a freshly written function or module, and asking ChatGPT to suggest improvements.
It&#39;s told me ways to improve error handling, or some cases I hadn&#39;t thought of where things might break.
It&#39;s even found a few bugs in my code, and showed me how to fix them.
If you work alone, it&#39;s nice to use ChatGPT for feedback and review, and maybe you&#39;ll learn something new too.</p>
<h2 id="coding-with-chatgpt">Coding with ChatGPT</h2>
<p>ChatGPT is very capable of writing code.
However, like everything else it does, it often makes mistakes.</p>
<p>In my experience, the code written by ChatGPT is rarely perfect on the first try.
Very often, the code will try to do something that isn&#39;t possible, or misunderstand what was being asked of it.
I guess that&#39;s true of code written by humans too.</p>
<p>When you&#39;re asking ChatGPT to write code for you, it&#39;s up to you to run the code and paste back any error messages or problems into the chat, asking for fixes.
In a way, it&#39;s like the roles are reversed.
You&#39;re no longer the programmer, but stuck between the AI and the compiler.
I have to say, this is not a very fun place to be.
I would much rather just make changes to the code myself, than to try different prompts until ChatGPT is able to generate the right code.
Often it&#39;s faster to type the code you specifically want and need than to type some prompts and wait to see if ChatGPT has made it correct.</p>
<p>It&#39;s almost like working with a junior developer, except that a junior developer is capable of learning and improving and eventually becoming a senior developer.
ChatGPT, on the other hand, isn&#39;t learning anything from you over the long term.
It might learn from you in the short term, but remember, the context of an LLM is limited, and that means that it will soon forget the suggestions you made for improvement.</p>
<p>If, on the other hand, you&#39;re new to programming, then ChatGPT is going to be extremely helpful and time saving.
I&#39;ve seen lots of new developers have great success using ChatGPT in this way, to do things they don&#39;t know how to do.
I believe ChatGPT and similar tools will enable a lot more people to get into coding, and that&#39;s really exciting.</p>
<p>Even as an experienced developer, we&#39;re always learning new things.
Having ChatGPT lead the way and provide feedback in a new programming language or library can be extremely helpful.
Just be wary that it&#39;s very likely to make mistakes, so you still need to understand what the code is doing.
Never trust code written by an AI, just as you wouldn&#39;t trust any code you find on the Internet.
Ultimately, code generated by an LLM is coming from code from the Internet, security issues and all.</p>
<p>Fortunately, ChatGPT makes some of this easier for you.
As mentioned above, you can ask ChatGPT to explain the code it&#39;s written, or look for bugs.
Sometimes it&#39;s worth doing this with the code it just generated.
It&#39;s kind of funny how that&#39;s possible.
Since it generates a word at a time, it can&#39;t often go back and fix its own mistakes during generation.
So if you ask it if it made any mistakes, sometimes it&#39;ll be able to spot the mistake right away and write a better version.</p>
<h2 id="ask-for-small-simple-code-snippets">Ask for small, simple code snippets</h2>
<p>To be honest, I haven&#39;t enjoyed having ChatGPT generate large amounts of code for me.
It hasn&#39;t seemed to saved me much time, it just changed how I spent my time.
I&#39;ve had more success asking it to do smaller, more limited things.</p>
<p>It&#39;s really good at writing SQL queries for you.
Paste in the table schema and tell it what you&#39;re looking to query.
You can also be specific about which programming language and library you&#39;re using to connect to the database.
I think this will be very helpful to a lot of people.</p>
<p>It can also generate things like regular expressions, or other complex code, based on your description. More detail is always better here, including specific examples of edge cases.</p>
<p>Ask it to generate some boilerplate code for you, to give you a head start.
Or, paste in the specifications from your manager and have it attempt a first draft for you to use as a starting point.
Depending on your skill level, you might prefer to move into your editor and do the rest of the coding from here.</p>
<p>It&#39;s important that you&#39;re able to quickly test what it generated and verify that it works as expected.
You can even paste in some code and ask ChatGPT to generate some unit tests for you.
You can use it with Test Driven Development, pasting in some unit tests and ask it to write the code.
You can even ask ChatGPT to generate some test code alongside any other code it generates, by including in your prompt something like &quot;Write tests for the code too.&quot;</p>
<h2 id="comparison-to-copilot">Comparison to Copilot</h2>
<p>As I&#39;ve written about before, I really enjoy using GitHub Copilot, and <a href="https://www.codingwithjesse.com/blog/how-i-use-github-copilot-to-be-more-productive/">it helps me to be more productive</a>.
Copilot also uses GPT, but it&#39;s doing so in a more focused way that automatically takes your code into its context.
It&#39;s very good at suggesting code while you&#39;re writing it, suggesting comments for your code, or generating code based on your comments.
ChatGPT hasn&#39;t at all replaced my use of Copilot.
If anything, it has made me appreciate Copilot more, and encouraged me to use Copilot in more creative ways.
I&#39;ve found myself bringing up the Copilot suggestions panel more often, to see the variety of suggestions available, and very often there are some better and more useful snippets available in here.</p>
<p>For some reason, using Copilot is less misleading.
When Copilot makes a wrong suggestion, it doesn&#39;t bother me.
Perhaps it&#39;s because there&#39;s no confidence here, everything is just a &quot;suggestion&quot;.</p>
<p>ChatGPT is of course better at discussing and explaining things in plain language.
Microsoft is already planning to integrate a chat interface into Copilot, so-called &quot;GitHub Copilot X&quot;.
You can <a href="https://github.com/github-copilot/chat_waitlist_signup">sign up for the beta</a> if you want to get early access to Copilot chat.
I&#39;m really looking forward to this, as it&#39;ll likely be a lot more useful for coding than ChatGPT currently is.</p>
<h2 id="its-not-a-human">It&#39;s not a human</h2>
<p>It&#39;s very important to keep in mind that ChatGPT is not a person.
It&#39;s a statistically-driven guessing machine.</p>
<p>Like a human, it makes mistakes, but it won&#39;t tell you how sure or unsure it is about being right.</p>
<p>Like a human, it&#39;s trying to generate responses it thinks you&#39;ll like, but it has no feelings and will never be your friend.</p>
<p>Like a human, it has biases that it&#39;s not aware of and can&#39;t articulate, but it&#39;s incapable of growing and learning from you over time.</p>
<p>It can be hard to talk to a machine like this without all the baggage we&#39;ve picked up from talking to real humans.
I find myself saying &quot;please&quot; and &quot;thank you&quot; when I really don&#39;t need to.</p>
<p>I think we need to create a new place in our brains for interacting with things like this.</p>
<p>It&#39;s ok to be a bit blunt and succinct.
Often it&#39;s necessary to be extra explicit, and state things that might otherwise seem obvious.
You don&#39;t need to spare the feelings of these guessing machines.
You need to tell it whenever it&#39;s wrong and ask it to fix its mistakes.
You can tell it to &quot;be succinct&quot;, to &quot;skip unnecessary phrases&quot; and &quot;just output the code&quot; and other commands which speed it up and tailor the output to your preferences.
You may need to repeat these phrases regularly, and you may likely find some new patterns that work well for you.</p>
<h2 id="try-it-for-yourself-have-fun">Try it for yourself, have fun</h2>
<p>I&#39;ve outlined some of the approaches that have worked for me, but I suggest you try it out yourself and see what works for you.
I think it&#39;s worth experimenting and finding a way for ChatGPT and other AI tools to help you out in your work.</p>
<p>These tools should make your life better, and make work more fun.
The goal isn&#39;t just to save time, but to enjoy the process.</p>
<p>When you&#39;re feeling stuck, you can use ChatGPT as a mentor to help you get unstuck.
When you want to bounce some ideas off someone, ChatGPT can give you helpful suggestions.</p>
<p>Save the fun coding stuff for yourself, and leave the boring parts for ChatGPT.</p>
<hr/><p>Interested in web development? <a href="https://www.codingwithjesse.com/newsletter">Subscribe to the Coding with Jesse newsletter!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What I learned from wearing a Continuous Glucose Monitor for two weeks]]></title>
            <link>https://www.codingwithjesse.com/blog/continuous-glucose-monitor/</link>
            <guid>https://www.codingwithjesse.com/blog/continuous-glucose-monitor/</guid>
            <pubDate>Sun, 02 Apr 2023 13:50:47 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2023-04-02-freestyle-libre-2-sensor.jpg" width="770" height="537" alt="FreeStyle Libre 2 sensor in an arm"/>

<p>A couple of weeks ago, I bought one of those Continuous Glucose Monitors (CGM). My nutritionist suggested I do this, even though I&#39;m not diabetic or pre-diabetic, just to learn something about myself and how food affects me. I&#39;m always excited about new technology and gadgets, so of course I went out and bought one right away.</p>
<p>I bought the FreeStyle Libre 2 monitor, the only brand available in Canada (as far as I know). It cost $119 CAD, and I was able to buy it from the pharmacy without a prescription.</p>
<p>The monitor itself is a small thin disc with a tiny needle in the middle. You install it into the back of your arm with an applicator. I was nervous, but it never hurt at all. It has a strong adhesive, like a bandage, so there&#39;s very little chance of it falling out accidentally. It stayed put for the full 14 days, though I felt I had to be a bit careful with it.</p>
<p>Once installed, you can sync and activate it with your smartphone, by installing the app. The monitor syncs to your phone with NFC, so basically you can tap it as often as you want to get your current blood sugar. You&#39;ll also see a line chart of what happened through the day on a minute-by-minute basis.</p>
<p>After having kept a close eye on it for the last two weeks, I now have a way better understanding of what my body feels like when the glucose changes, and how to keep it level. Here are a few things I learned from it.</p>
<p>Note: I&#39;m fairly healthy and don&#39;t have diabetes or pre-diabetes, and these are just hunches, so may not be true or apply to you. Factors like sleep, exercise, hormones, and your overall health can have a big influence on your blood sugar. The only way to learn about your own body is to try this yourself. Still, I&#39;m hopeful some of this will be at least interesting to some of you.</p>
<ol>
<li><p>Eating a sugary or high carb meal (pizza, oatmeal, half a bag of doritos) makes my glucose quickly spike up high and then drop sharply, dropping lower than my average. My understanding is this is because insulin was released, which allows the glucose to move from the blood into the cells, and excess glucose is converted into fat.</p>
</li>
<li><p>Right after a high spike, I often drop too low and feel like snacking shortly after eating even though my body just had an excess of energy. So I&#39;m probably storing that glucose as fat, and then end up eating more than I need. The takeaway here is that I&#39;m going to try to limit half my meal to carbs, and add in a salad or some protein to balance things out.</p>
</li>
<li><p>Foods with a balance of carbs, fat, protein and fibre will make the glucose go up slower, not as high, stay up longer, and come down slower, ending up closer to my average, so I&#39;m not hungry afterwards. By limiting the level of refined carbs or sugar in a meal, there&#39;s a better chance I&#39;ll feel fuller for longer, and be able to go longer without snacking afterwards.</p>
</li>
<li><p>Whenever I&#39;m feeling hungry, I&#39;ve probably dropped below 5.0 mmol/L. It&#39;s interesting how clearly blood sugar coincides with the desire to eat.</p>
</li>
<li><p>Whenever I&#39;m feeling starving or woozy, I&#39;ve probably dropped below 4.0 mmol/L. This happened a few times when I exercised about an hour after eating a high-carb meal. While I&#39;m crashing from the spike, my insulin and physical activity are both drawing down glucose quickly at the same time, and it ends up going too low. I now avoid eating a high carb or sugary meal (eg. oatmeal)  before exercising.</p>
</li>
<li><p>I never felt any of my sugar spikes. The highest I got to was 11 mmol/L after eating pizza. Usually my spikes are around 9 mmol/L. Hyperglycemia starts around 10 mmol/L. Hyperglycemia can damage cells and increase insulin resistance, and over time can contribute to developing long-term health problems such as cardiovascular disease and type-2 diabetes.</p>
</li>
<li><p>Meals with fewer carbs might barely go up at all. Even a single slice of bread or pizza, or a handful of chips seems to have very little impact. This shows how important portion control is. By avoiding carbs altogether, it was possible for me to stay basically flat all day, though I don&#39;t know how necessary or healthy that is.</p>
</li>
<li><p>It&#39;s really interesting how a lot of advice I hear all the time now makes more sense in a tangible way. Eat smaller meals (shorter spike). Try intermittent fasting (more time between spikes). Avoid sugar, especially sugary drinks (very sharp spike). Eat light before exercising (so you&#39;re not crashing after a spike and dip into danger zone). Etc.</p>
</li>
<li><p>I now suspect that a lot of times over the years when I&#39;ve felt really off without explanation were due to crashing below 4 after a spike.</p>
</li>
<li><p>As a test one night, I ate a tremendous amount of pasta for dinner. Surprisingly, this didn&#39;t make me spike or crash sharply! But what it seemed to do was keep me at a rather high baseline. Somehow, I was at the highest hours later, right before bed (8.3 mmol/L). When I woke up the next morning it was still quite high (6.6 mmol/L, in the pre-diabetic range for a fasting glucose)! This was just a one-off so there could have been other factors at play (exercise, stress, etc.) but I feel like it was probably mostly the pasta.</p>
</li>
</ol>
<p>Overall, I was really glad I tried this out. I think it&#39;s absolutely worth the cost, because it has given me tangible first-hand experience with a lot of things I already knew in the abstract. I see it as a one-time educational thing that&#39;ll ideally pay benefits over the next decades. I believe it will help me make better choices and hopefully avoid problems with my health over the long run.</p>
<p>I might try it again down the road to see how I&#39;ve changed. Or maybe I&#39;ll get one of those finger prick glucose monitors to spot check when I&#39;m feeling strange.</p>
<p>The biggest change I&#39;ve made after all this: <em>I don&#39;t put sugar in my coffee anymore</em>. This was adding two sharp glucose spikes early in my day every day, so cutting those out was a quick win. Now, I tend to stay relatively flat through the day with a moderate, slower increase after dinner.</p>
<hr>
<p><em>Here are two line graphs, one from my best day, and the other from my worst day (the first spike was oatmeal, and the dinner spike is from pizza, and apparently it even broke the monitor!):</em></p>
<img src="https://www.codingwithjesse.com/images/2023-04-02-my-chart.jpg" width="800" height="232" alt="My blood sugar charts from two days"/>

<p><em>For comparison, here are two sample days sent to me from <a href="https://www.me.uk">RevK</a> who is a diabetic:</em></p>
<img src="https://www.codingwithjesse.com/images/2023-04-02-revk-chart.jpg" width="800" height="384" alt="My blood sugar charts from two days"/>
<hr/><p>Interested in web development? <a href="https://www.codingwithjesse.com/newsletter">Subscribe to the Coding with Jesse newsletter!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deploying a static site to Cloudflare Pages]]></title>
            <link>https://www.codingwithjesse.com/blog/deploying-a-static-site-to-cloudflare-pages/</link>
            <guid>https://www.codingwithjesse.com/blog/deploying-a-static-site-to-cloudflare-pages/</guid>
            <pubDate>Sat, 04 Mar 2023 18:50:12 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2023-03-04-cloudflare-pages.jpg" width="800" height="400" alt="A stack of paper in a field blowing away in the wind"/>

<p>I moved codingwithjesse.com to <a href="https://pages.cloudflare.com/">Cloudflare Pages</a> this week!</p>
<p>I was having some intermittent outages on my website in the hours before I wanted to publish my <a href="https://www.codingwithjesse.com/blog/debugging-a-slow-web-app/">last blog post</a>. Since I wanted to publish immediately, but didn&#39;t want to send people to a broken site, I decided I&#39;d finally try out hosting my website on Cloudflare Pages. It went so smoothly, I was able to get it working in under an hour and publish the blog post!</p>
<p>First of all, you should know that Cloudflare Pages works easiest with a static website. It&#39;s very possible to run websites with server-side code using Cloudflare Workers, but my website is static, so I didn&#39;t need to worry about that. (I plan to move some other sites to Cloudflare Pages and Workers later, so I&#39;ll probably do a write-up of that when I do!)</p>
<p>At the time of writing this, Cloudflare Pages is free, with unlimited sites, unlimited requests, and unlimited bandwidth, with a limit of 500 builds per month, but you should double check the <a href="https://www.cloudflare.com/plans/developer-platform/">pricing page</a> as it may change in the future.</p>
<h2 id="setting-up-cloudflare-pages-in-an-emergency">Setting up Cloudflare Pages in an emergency</h2>
<p>I didn&#39;t have time to figure out the best way to use Cloudflare Pages, and wasn&#39;t totally sure I&#39;d want to stick with it, so I did the easiest possible thing I could find.</p>
<p>All I wanted to do was somehow upload a zip file of my website and have it be hosted on there. I didn&#39;t know if that was possible, but I was eager to figure it out. Here&#39;s how I pulled it off in record time.</p>
<ol>
<li><p>I logged on to the Cloudflare dashboard and clicked Pages in the side nav. I clicked the &quot;Create a project&quot; button, and chose the &quot;Direct upload&quot; option. Perfect!</p>
</li>
<li><p>Cloudflare asked me to create a name for my project. I chose &quot;codingwithjesse&quot; and clicked &quot;Create Project&quot;.</p>
</li>
<li><p>I clicked &quot;select from computer&quot; and chose &quot;Upload zip&quot; to browse to my zip file and upload it. Easy!</p>
</li>
<li><p>After a while (I have 600+ pages on my site, and it took a few minutes), it was ready, and I could click &quot;Deploy site&quot;. Success!</p>
</li>
<li><p>I was able to see my new site at codingwithjesse.pages.dev and verify that everything looked good. I did have to wait a few minutes for the DNS to propagate and the subdomain to show up, but when it did, it looked perfect.</p>
</li>
<li><p>Returning to the newly created project, I had to click on the &quot;Custom domains&quot; tab and the &quot;Set up a custom domain&quot; button, so that I could map <a href="http://www.codingwithjesse.com">www.codingwithjesse.com</a> to this new subdomain.</p>
</li>
<li><p>Since I already had my domain on Cloudflare, all I had to do was confirm the new DNS records and it was ready to go!</p>
</li>
</ol>
<p>If you&#39;re new to Cloudflare, there will obviously be other things you have to do to get set up here. But it&#39;s also possible to use Cloudflare Pages without using Cloudflare DNS - you&#39;ll just have to manually set up the CNAME records in your DNS provider. Don&#39;t worry, Cloudflare walks you through that process.</p>
<h2 id="deploying-to-cloudflare-pages-the-easier-way">Deploying to Cloudflare Pages the easier way</h2>
<p>The zip file approach worked great as a first test, and I actually used the same zip upload method a dozen more times as I made small edits to the site. But that got tiring, so I wanted to figure out how to deploy my changes automatically and programmatically from my command line. Turned out this approach was just as easy as using the dashboard.</p>
<p>Cloudflare&#39;s command line tool is called <a href="https://developers.cloudflare.com/workers/wrangler/">Wrangler</a>. This tool is how you can easily interact with Cloudflare and deploy to Cloudflare Pages.</p>
<p>To get it working, I needed to have two things in environment variables: an API key, and my Account ID.</p>
<p>I went and set up an API key that only has access to Pages on my Cloudflare account. I went to the <a href="https://dash.cloudflare.com/profile/api-tokens">API Tokens</a> section of the Cloudflare dashboard, and created a new token. I added only one permission to the token: Account &gt; Cloudflare Pages &gt; Edit.</p>
<p>I also copied the account ID from my dashboard to use in the environment variable.</p>
<p>I had to run <code>CLOUDFLARE_ACCOUNT_ID=theaccountid CLOUDFLARE_API_TOKEN=thisisthetoken npx wrangler pages publish ./build</code>, telling it to upload all the files in my <code>build</code> directory. It asked me if I wanted to create a new project or use an existing project. I chose &quot;Use an existing project&quot;, and was able to see my &quot;codingwithjesse&quot; project right there to select it.</p>
<p>It uploaded the files, and Success! It gave me a temporary deployment subdomain where I could verify that the changes I wanted are correct. Uploading this way was much faster, as it only had to upload the files that had changed.</p>
<p>This actually didn&#39;t update my production site. To push directly to production, and to skip the question about which project to use, I had to run <code>CLOUDFLARE_ACCOUNT_ID=theaccountid CLOUDFLARE_API_TOKEN=thisisthetoken npx wrangler pages publish ./build --project-name codingwithjesse --branch main</code></p>
<h2 id="making-a-private-bash-script">Making a private bash script</h2>
<p>You shouldn&#39;t put your API key in your git repo, so make sure you don&#39;t put it into your package.json or commit it anywhere by accident.</p>
<p>To avoid this, I usually create a simple bash script <code>push.sh</code> which is in the root of a lot of my projects. I add <code>push.sh</code> to my <code>.gitignore</code> so it won&#39;t be committed by accident. The contents are simply like this:</p>
<pre><code class="language-bash">#!/bin/bash

npm run build

CLOUDFLARE_ACCOUNT_ID=theaccountid CLOUDFLARE_API_TOKEN=thisisthetoken npx wrangler pages publish ./build --project-name codingwithjesse --branch main</code></pre>
<p>You&#39;ll have to run <code>chmod +x ./push.sh</code> to allow it to execute. After that, you can build and push the site just by running <code>./push.sh</code>.</p>
<p>There are other ways to manage your environment variables and secrets, but this is the approach that works well for me for a lot of projects.</p>
<h2 id="lots-of-possibilities">Lots of possibilities</h2>
<p>Cloudflare Pages can integrate into your GitHub repo and other deployment pipelines, so that whenever you push your changes live, it&#39;ll build automatically. This doesn&#39;t work for me for this blog, because the content is in a database and doesn&#39;t live in my git repo, but might be a good option for your project.</p>
<p>If you&#39;re interested in learning more, check out the <a href="https://developers.cloudflare.com/pages/">Cloudflare Pages documentation</a>. There are examples for pretty much every framework out there, so you should have no problem figuring out the best way to deploy your static site.</p>
<hr/><p>Interested in web development? <a href="https://www.codingwithjesse.com/newsletter">Subscribe to the Coding with Jesse newsletter!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Debugging a slow web app]]></title>
            <link>https://www.codingwithjesse.com/blog/debugging-a-slow-web-app/</link>
            <guid>https://www.codingwithjesse.com/blog/debugging-a-slow-web-app/</guid>
            <pubDate>Fri, 24 Feb 2023 02:26:23 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2023-02-23-debugging-a-slow-web-app.jpg" width="800" height="380" alt="A watercolour illustration of a robot slowly wading through a swamp"/>

<p>I got an email today from one of my clients, letting me know that one of his web apps was down. He said he was getting an error and asked me to take a look.</p>
<p>In the end, I was able to fix it and get it running faster than ever. What caused it turned out to be a huge surprise to me. I thought I&#39;d outline the steps I went through here, to try to help others trying to solve similar problems in the future.</p>
<h2 id="see-for-yourself">See for yourself</h2>
<p>The obvious first step is to go see for myself. I loaded up the web site, wondering what kind of error I would find. The site initially loaded fine, but then a spinner would appear, and seemed to get stuck. After a long while, the main content of the site failed to load, and an error appeared about failing to parse JSON.</p>
<p>I opened up dev tools and refreshed, keeping an eye on the network tab. Most things were loading fine, including static assets. I noticed that there were some fetches to the web site API that were taking a long time. They eventually failed with a 504 gateway timeout error.</p>
<p>The web site is behind a load balancer, and I know load balancers generally have a timeout limit around one minute. So all I knew is that these API calls were taking longer than that. I could only assume they would eventually succeed and were simply slow, but I wasn&#39;t totally sure.</p>
<h2 id="try-to-reproduce">Try to reproduce</h2>
<p>Fortunately, I already had a dev environment for the site set up locally. I didn&#39;t write the whole application, but I had recently made some performance improvements to parts of the site. I wasn&#39;t very familiar with the API side of things though.</p>
<p>Sure enough, it started up fine, and the data all loaded correctly. So I figured it probably wasn&#39;t an issue with the code itself.</p>
<p>I started to wonder what could have happened to break the site all of a sudden, when it had worked fine in the past. Did the server auto-update some dependency that was breaking something? Was the server out of disk space? Was the database out of memory?</p>
<h2 id="getting-close-to-the-metal">Getting close to the metal</h2>
<p>My next step was to actually <code>ssh</code> into one of the servers to try and see what&#39;s going on there. Everything seemed ok. I ran <code>free -m</code> to check on the memory, but the RAM usage was fine. I ran <code>df -h</code> to check on disk usage, but none of the disks were full. Running <code>top</code>, the CPU usage looked fine as well. I was a bit stumped.</p>
<p>I turned to look at the database. This site is running on AWS, so I logged on to the RDS admin in the console and checked the graphs in the monitoring tab. Everything seemed fine there too. CPU wasn&#39;t too high, there were barely any connections, and the database wasn&#39;t out of space.</p>
<p>Still, I knew these API requests were hanging on something. I went back to the code and looked at the API endpoints in question, and all they did was make a database query. At this point I was pretty sure it was database-related somehow.</p>
<h2 id="going-into-the-database">Going into the database</h2>
<p>I decided to log in to the production database using the <code>mysql</code> command-line tool. The database is not accessible to the public, so only the production web server has access. I&#39;d never gone into there before, so I looked at the config file for the server-side application to find the credentials (hostname, username, password and database name).</p>
<p>This is a MySQL database (MariaDB actually), so once I got in, the first thing I ran was <code>SHOW PROCESSLIST</code> to see if there was anything there. There were a ton of queries in there, many of them had been running for more than a minute. One had been sitting there for almost an hour!</p>
<h2 id="optimizing-queries">Optimizing queries</h2>
<p>Finally, I found the problem. All the slow queries were working with a single table. There was a mix of <code>SELECT</code> and <code>UPDATE</code> statements, so I figured the table was probably missing indices or something, something to make the queries run slowly.</p>
<p>I called <code>SHOW CREATE TABLE xyz</code> on the table, to see the structure of it. I was wrong, there were lots of keys on the table. Knowing that MySQL will only use one key per query, my first guess was that the problem was that maybe there were actually too many keys on the table, and the table would benefit by having fewer keys with multiple columns in it instead, targeted at these particularly queries.</p>
<p>I tested my theory by hand writing a simple query to see how slow it would be. It was slow, but it only took about one second, not a minute. So it wasn&#39;t that.</p>
<p>I wondered if I was missing something. Calling <code>SHOW PROCESSLIST</code> shows a summary of queries, but it cuts them off. A quick DuckDuckGo search later, and I found out you can call <code>SHOW FULL PROCESSLIST</code> to see the entire query.</p>
<p>It was then that I discovered what the problem was. The query was written exclusively using <code>LIKE</code> statements instead of <code>=</code>, eg.:</p>
<pre><code class="language-sql">SELECT *
FROM xyz
WHERE thing_id LIKE &#39;12345678&#39;
AND status LIKE &#39;ok&#39;</code></pre>
<p>Even the update statements used <code>LIKE</code>:</p>
<pre><code class="language-sql">UPDATE xyz
SET status = &#39;ok&#39;
WHERE id LIKE &#39;12345678&#39;</code></pre>
<p>I found it unconventional to say the least. But was that really causing the problem?</p>
<p>I changed my hand-written query to use <code>LIKE</code> instead of <code>=</code>, and sure enough, I had to Ctrl+C to abort it after a long time of waiting.</p>
<p>I realised that yes, of course, this would slow things down. MySQL must be scanning the entire table, converting the IDs from numbers to strings, and doing pattern matching on each one. No wonder it was running so slowly!</p>
<h2 id="closer-to-a-solution">Closer to a solution</h2>
<p>I searched the code base for &quot;LIKE&quot; and found the cause. Buried in a custom query builder a past developer had assembled, it would only use either <code>LIKE</code> or <code>IN</code> for all query parameters, including in <code>UPDATE</code> statements.</p>
<p>I&#39;m not totally sure what the developer was thinking here. Were they making it so you could search on any field easily? I&#39;m not sure, because I wasn&#39;t able to find an example where fields were actually searched on anywhere. We may never know.</p>
<h2 id="the-problem-was-in-the-code-base">The problem was in the code base</h2>
<p>I was surprised the problem actually was in the code base itself. It made sense to me, this isn&#39;t the kind of problem that would have shown up locally, or even when the site first launched. It would have grown slowly as the table size grew, and apparently only became a major issue once the table had over 750k records in it.</p>
<p>The solution seemed straightforward to me. I modified the API endpoints that used this table, and rewrote the queries directly instead of using this query builder code. (Side note: I&#39;ve never liked query builders, and this is an excellent example of why!)</p>
<p>I would have liked to modify the query builder to replace <code>LIKE</code> with <code>=</code>, but because I&#39;m not sure if that functionality was needed elsewhere, I thought it best to leave it alone, and migrate away from the query builder instead.</p>
<h2 id="ship-it">Ship it!</h2>
<p>Last step was to commit and push the code, and rolled out an updated version of the system. Shortly after the new version went live, I logged in to the database again and ran <code>SHOW PROCESSLIST</code>. Nothing but a bunch of idle connections! Perfect!</p>
<p>I went over to the AWS admin panel, and sure enough, the &quot;Read IOPS/second&quot; chart had dropped from a steady 100 down to 0! That was a nice reassurance that things were massively improved.</p>
<p>The site wasn&#39;t just working again, it was faster than ever!</p>
<h2 id="lessons-learned">Lessons learned</h2>
<p>There are always lessons to be learned from any outage. Here are a few I learned today:</p>
<ol>
<li><p>You should definitely not use <code>LIKE</code> in your database queries to match numeric, indexed IDs. I&#39;ve never done this, but now the fact has been ingrained deep in my brain.</p>
</li>
<li><p>You probably shouldn&#39;t write your own query builders. Again, I&#39;ve never liked query builders so this just reaffirmed my belief.</p>
</li>
<li><p>You should maybe think about testing your web apps with a very large amount of dummy data in the database sometimes. I&#39;ve never done this, as it&#39;s slow and seems a bit excessive, but I think I may start trying this in the future, particularly on systems where I expect the tables to grow enormously over time.</p>
</li>
<li><p><code>SHOW FULL PROCESSLIST</code> is a thing. Okay, that&#39;s not so much a lesson as it is something I learned that wasn&#39;t already in my tool belt.</p>
</li>
</ol>
<p>All in all, the whole process took about an hour, and I&#39;m glad I was able to get things back up and running quickly. Beyond the lessons learned, I got to know the system a little better, and have a lot of new ideas of ways I can improve and speed things up in the future, so it was definitely a worthwhile experience!</p>
<hr/><p>Interested in web development? <a href="https://www.codingwithjesse.com/newsletter">Subscribe to the Coding with Jesse newsletter!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Web apps that last]]></title>
            <link>https://www.codingwithjesse.com/blog/web-apps-that-last/</link>
            <guid>https://www.codingwithjesse.com/blog/web-apps-that-last/</guid>
            <pubDate>Sun, 19 Feb 2023 21:23:42 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2023-02-19-web-apps-that-last.jpg" width="800" height="400" alt="A castle made of sand">

<p>When you&#39;re building a new web application, or even a new feature, how can you ensure that you&#39;re not creating a nightmare code base that will need to be rewritten completely in a few years?</p>
<p>Some people will say it&#39;s hopeless to even try and write code that will last. I&#39;ve even heard people suggest that you should aim to rewrite all your code every few years. That sounds like a very expensive, wasteful strategy.</p>
<p>In two decades of building web apps, I&#39;ve seen many codebases start as a shiny new prototype and grow into a huge system. In some cases, they&#39;ve become old, ugly, painful legacy systems that teams are begging to rewrite and replace. (And often those rewrites will themselves grow into ugly, painful legacy systems!) But sometimes a codebase will remain more or less unchanged a decade later, running smoothly as ever.</p>
<p>I believe there are some decisions you can make when writing code that will help it to last longer, and withstand the test of time.</p>
<h2 id="change-is-inevitable">Change is inevitable</h2>
<p>Probably the one thing you can be sure of is that change will come. The goals of a business will change, and the people within a business will change. There will inevitably be features added, and existing features will evolve and be repurposed. The names of the products will almost surely change. So is it even possible to write code that doesn&#39;t need to change?</p>
<p>I think the key is in the phrase &quot;If it ain&#39;t broke, don&#39;t fix it&quot;. Code that is fulfilling its task, that is doing what it&#39;s supposed to do, and is bug-free, is code that will last a long time.</p>
<h2 id="nightmare-code">Nightmare code</h2>
<p>To understand how to write code that will last, let&#39;s think about the opposite: a nightmare codebase that demands to be rewritten. The worst I&#39;ve seen is a web server written as a giant single file with thousands of lines of code. A system built like a house of cards, where changing one thing will break everything. Code that is very difficult to read or understand. Code that literally gives developers nightmares.</p>
<p>Unfortunately, this is often the kind of code that comes out of throwing together a quick prototype. A hero developer stays up late one night and churns out a first draft like a stream-of-consciousness. The next morning, the business owner is delighted to see their dreams come to life. Everyone&#39;s happy.</p>
<p>Then, they ask to change just one thing. Add this little feature. And this other feature. And now this user needs this other thing. And could you just change that other thing quick?</p>
<p>Months later, and this rough draft has accidentally become the foundation for a web application that continues to grow, held together with digital duct tape.</p>
<p>So how do you prevent this nightmare from unfolding?</p>
<h2 id="do-one-thing-and-do-it-well">Do one thing, and do it well</h2>
<p>Modularity is extremely important in writing code that will last. And a good module is a piece of code that does one thing, and does it well. That one thing might be interfacing with a single database table. Or it could be handling HTTP calls on a single URL and passing the data to and from other modules that talk to the database.</p>
<p>I find generally that it works best when each module has zero, one or two major dependencies. With zero dependencies, you have a set of functions that receive input data, process it in some way, and return results. With one dependency, you have a set of functions that act as an abstraction or interface to that dependency. With two dependencies, you&#39;re writing code that bridges the gap between the two, acting as an adapter or controller.</p>
<p>More than two major dependencies, and you should ask yourself if there&#39;s any way to split things up into smaller pieces that are responsible for fewer things.</p>
<p>A dependency might not always be a program you have to install. Another module in your system is also a dependency. So is your business logic. I think about dependencies as anything that your module &quot;knows about&quot;. This could even be the shape of certain data structures that might not have explicit type definitions.</p>
<p>The fewer things a module knows about, the more likely the module will be able to persist unchanged over time, because there will be fewer reasons to change it.</p>
<p>When your web application is built with small, independent modules that only do one thing, the chances are much, much lower that any of those pieces will need to be rewritten. And the chance of the whole application needing to be rewritten all at once drops to nearly zero. Even if you later want to do a major redesign, you&#39;ll find it easier to copy over lots of these older, simple modules to reuse in the new system.</p>
<h2 id="finally-a-tangible-example">Finally, a tangible example</h2>
<p>Let&#39;s say you need to send out a Forgot Password email. You could do the whole thing in one file, but I would prefer to split it up like this:</p>
<ol>
<li><p>A module that knows how to actually send an email using AWS SES or something, but doesn&#39;t know the recipient, subject or body of the email. <code>function sendEmail(toAddress, subject, body)</code>, for example.</p>
</li>
<li><p>A module that knows about the subject and body of the Forgot Password email, but doesn&#39;t know who it&#39;s sending to or what the reset URL will be. <code>function sendForgotPasswordEmail(toAddress, resetUrl)</code></p>
</li>
<li><p>A module for the user table in the database, that has a function to generate a reset code, but doesn&#39;t know how the reset code will be used or even whether an email will be sent out. <code>function createResetCode(userEmail)</code></p>
</li>
<li><p>A module that knows about the URL structure of the site, and has a function that can generate a password reset link from a reset code. <code>function getResetUrlFromCode(code)</code></p>
</li>
<li><p>A module that ties everything together. It takes an email address, calls <code>createResetCode</code>, uses that to call <code>getResetUrlFromCode</code>, passes that to <code>getForgotPasswordEmail</code>, and sends the recipient address and email body to <code>sendForgotPasswordEmail</code>. <code>forgotPassword(email)</code></p>
</li>
<li><p>A user interface widget with a form, a text field and a button, so the user can type in their email address and click <code>Send password reset link</code>. When the form is submitted, it tells the user to go wait for the email.</p>
</li>
<li><p>A module that is responsible for the server-side password reset part of the system. It receives the form submission, pulls the email address from the form data, calls <code>forgotPassword</code>, and then sends a <code>success</code> status back to the browser.</p>
</li>
</ol>
<p>Here, only a few modules are likely to change. You&#39;ll probably see changes to the <code>sendForgotPasswordEmail</code> function, as well as the user interface widget. All the other modules I&#39;ve outlined are very reusable, and highly unlikely to change, unless you change your email sending provider, or your database software, or something else major. Even in those situations, the code that needs to change is very isolated and easy to replace without affecting anything else.</p>
<p>You can even improve on this further, by having the contents of the email be database-driven, so that non-technical staff members can change the email templates themselves through an admin interface. But an architecture like this is a good starting point that makes those sorts of changes simpler to make.</p>
<h2 id="a-good-start">A good start</h2>
<p>If you get in the habit of writing more modular code, and splitting things up as early as possible, then the next time you&#39;re throwing together a quick prototype, you&#39;ll be able to lean on those principals in the process.</p>
<p>Instead of a giant ball of tangled dependencies and logic, you&#39;ll be building smaller, simpler, reusable components that can be used as solid building blocks. Some of these will be so useful and generic that you&#39;ll even be able to reuse them in completely different systems without changing them at all.</p>
<hr/><p>Interested in web development? <a href="https://www.codingwithjesse.com/newsletter">Subscribe to the Coding with Jesse newsletter!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Trying to decide what to do next? Follow the light.]]></title>
            <link>https://www.codingwithjesse.com/blog/follow-the-light/</link>
            <guid>https://www.codingwithjesse.com/blog/follow-the-light/</guid>
            <pubDate>Sun, 01 Jan 2023 18:27:01 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2023-01-01-follow-the-light.png" width="800" height="533" alt="A tree growing towards the light">

<p>Happy New Year! I&#39;ve been trying to come up with a New Year&#39;s resolution, and it got me thinking about setting goals, finding and following your purpose, and how this ties into some books I read this year.</p>
<p>TL;DR: If you&#39;re trying to decide what to do next in your life, in which direction you should expand and grow, maybe it helps to think like a tree and go where the sunshine is.</p>
<h2 id="the-purpose-of-life">The purpose of life</h2>
<p>The most interesting book I read in 2022 was <a href="https://www.theromanceofreality.com/">The Romance of Reality</a>, where Bobby Azarian does an amazing job applying Darwinism to the universe.</p>
<p>He says that the universe itself is a self-organizing system, with a bias towards increasing order, complexity and awareness. The idea is that the process of evolution was around before life emerged. Life formed at the bottom of the ocean at thermal vents, where tremendous amounts of extreme heat energy met extreme coldness. Eventually, the first forms of life emerged here to capture that wasted energy and put it to use.</p>
<p>Fast forward to the present, and now we have complex life everywhere we look, actively consuming any and all food and energy available and using it to maintain the structures of our bodies, our systems and our species.</p>
<p>As lifeforms, humans are sentient agents of the universe whose purpose is to use our awareness and intelligence in order to optimise the conversion of available energy into complexity and order.</p>
<p>It&#39;s not hard to see how true this is. So much of what we do boils down to consuming energy (food, fuel, heat) so that we can create more order (clean homes, growing families, bigger cities, information, content creation). Pretty much every job is related either directly or tangentially to this process, or optimizing the process.</p>
<p>We&#39;ve even dug towards the centre of the earth and the centre of the atom in order to unlock and consume more and more available energy and use it to create increasingly complex systems and structures.</p>
<p>So how do you fit into all this? And how can you use this perspective and knowledge to live a good life?</p>
<h2 id="following-your-dreams">Following your dreams</h2>
<p>I just finished reading <a href="https://www.goodreads.com/book/show/18144590-the-alchemist">The Alchemist</a> by Paulo Coelho. It&#39;s a story about following your dreams. It&#39;s about a shepherd boy in Spain who dreams about finding treasure at the pyramids in Egypt. Following the guidance of those he meets along the way, he goes on a quest to literally follow his dream and see where it leads.</p>
<p>I&#39;ve always liked stories about following your dreams. I&#39;ve always tried to follow my own dreams. Once upon a time, I was stuck in the proverbial office job, and dreamed of the day I could be working from home, setting my own hours, choosing work I found interesting. I dreamed about buying a house, getting married and having children.</p>
<p>I followed those dreams, and soon started freelancing. Several years later, I bought a house, got married, and now have the family and life I&#39;d always dreamed about.</p>
<p>So now I&#39;m looking to the future, wondering where to go from here. There are so many possibilities that it&#39;s hard to focus and hard to decide.</p>
<h2 id="thinking-like-a-tree">Thinking like a tree</h2>
<p>We&#39;re all in search of our potential. It&#39;s not just about finding happiness, but also finding activities that won&#39;t burn us out, that are sustainable in every sense of the word.</p>
<p>A tree will put more energy into the branches that get more sunshine, because opportunity creates a void that must be filled. Nature abhors a vacuum.</p>
<p>Thinking about our careers, interests and opportunities, it&#39;s as if we are trees with branches growing out in many directions, and we are trying to decide whether we should grow in this direction or that.</p>
<p>Like a tree, we can feel the energy coming from each branch to decide whether it&#39;s getting more sunshine or less. Sometimes this process is described as market research, trying to establish whether there is demand for a particular enterprise. Is it time to hire an employee? Or write a book? Or start a non-profit? Or teach? Or make a video game? Or take time off and travel? Or just hunker down and work harder doing the same things as ever?</p>
<p>Energy can take the form of light, or heat, or money, but also inspiration, joy, excitement and motivation. Which activities will give you a turbo boost and allow you to grow and expand further? Which are a dead end? You can also see which of your branches are already giving you more energy back. And you can expand carefully, incrementally, to get more feedback, to see if these directions are the right directions for you.</p>
<p>They need not expand forever in any given direction. Maybe there&#39;s a ton of energy available to move in the direction of, for example, publishing one small video game. But maybe that&#39;s also where it stops, and going all-in on video game development would be a terrible mistake. Or, maybe it opens up a new opportunity, one nobody could see or feel from here?</p>
<h2 id="follow-the-light">Follow the light</h2>
<p>Follow your passions? What does that even mean? Instead, follow the light.</p>
<p>What is shining brightest to you right now? Where are your branches expanding to, and which of those branches are shouting &quot;Go this way!!&quot; Optimize for excitement. Learn how to convert some of that sunshine into food, by bringing joy to others (aka &quot;providing value&quot;) such that others will be happy to give you sunshine tokens (aka &quot;money&quot;) for the joy you bring.</p>
<p>If something excites you, it&#39;ll likely excite others. Because you do not live alone in a desert. If you can capture a bit of that sunshine out of the air, you can make it available to the whole world. And when you do, more energy will flow back from the world to you, as if to say &quot;Yes, keep going!&quot;</p>
<p>Follow the light. Capture excitement out of the air and share it with the world.</p>
<hr/><p>Interested in web development? <a href="https://www.codingwithjesse.com/newsletter">Subscribe to the Coding with Jesse newsletter!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Add Mastodon replies to your blog]]></title>
            <link>https://www.codingwithjesse.com/blog/add-mastodon-replies-to-your-blog/</link>
            <guid>https://www.codingwithjesse.com/blog/add-mastodon-replies-to-your-blog/</guid>
            <pubDate>Tue, 27 Dec 2022 15:53:37 GMT</pubDate>
            <content:encoded><![CDATA[<p>You can now comment on blog posts on Coding with Jesse! I turned off comments years ago, because I was getting tons of spam. But recently, with <a href="https://www.codingwithjesse.com/blog/why-i-love-mastodon">my return to social media</a>, I decided to integrate Mastodon to give people a way to comment on and interact with my articles.</p>
<p>Initially, I wasn&#39;t sure how I would accomplish this. Mastodon has a ton of servers, and Mastodon search can only search for hashtags, so how would I know whether someone commented on my article? And how would I integrate it into my website?</p>
<p>I looked around at how some other blogs were handling this, and came across Webmentions, and particularly, <a href="https://webmention.io/">Webmention.io</a>. It&#39;s a web standard for communicating interactions across web servers! Perfect!</p>
<p>I naively assumed that Mastodon would automatically hit my server with notifications any time someone favourited, boosted or replied to a post that contained a link to my site. But alas, <a href="https://github.com/mastodon/mastodon/issues/6074#issuecomment-378452136">Mastodon doesn&#39;t do that for privacy reasons</a> (understandably).</p>
<p>Fortunately, I&#39;m not the first one to run into this problem, and so there&#39;s a free service available that solves this problem. If you sign up for <a href="https://brid.gy/">brid.gy</a>, you can link your social media accounts, including Mastodon, and brid.gy will automatically send webmentions to your site whenever one of your posts contains a link to your site, and people reply to, boost or favourite your post. Essentially, your Mastodon posts become an anchor for all the interactions on your blog posts.</p>
<p>With these two services in hand, here&#39;s how you can integrate Mastodon into your website the way I did:</p>
<h2 id="1-sign-in-to-webmentionio">1. Sign in to <a href="https://webmention.io/">webmention.io</a>.</h2>
<p>You need to sign in with your website URL, and your GitHub account. Also, your blog needs to link to that GitHub profile with either <code>&lt;a href=&quot;https://github.com/jesseskinner&quot; rel=&quot;me&quot;&gt;</code> or <code>&lt;link href=&quot;https://github.com/jesseskinner&quot; rel=&quot;me&quot;&gt;</code>, to prove that you own the site.</p>
<h2 id="2-add-webmention-tags-to-your-blog">2. Add webmention tags to your blog</h2>
<p>When you sign in, go to <a href="https://webmention.io/settings">https://webmention.io/settings</a>. Under Setup, you&#39;ll see these two link tags:</p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>webmention<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://webmention.io/username/webmention<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pingback<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://webmention.io/username/xmlrpc<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>Copy these and paste them into the <code>&lt;head&gt;</code> on your blog. These will tell other services (like brid.gy) where they need to send webmentions for your posts.</p>
<h2 id="3-link-your-mastodon-account-on-bridgy">3. Link your Mastodon account on <a href="https://brid.gy/">brid.gy</a></h2>
<p>Go to <a href="https://fed.brid.gy/">fed.brid.gy</a> and, if you&#39;re like me, you&#39;ll want to click on &quot;Cross-post to a Mastodon account&quot;, so that it&#39;ll integrate with your existing Mastodon account.</p>
<h2 id="4-post-a-link-to-a-blog-post-on-mastodon">4. Post a link to a blog post on Mastodon</h2>
<p>Try linking to your most recent blog post on Mastodon. If you already did this some time ago, brid.gy will scan your posts looking for links. You can also feed it a URL to a specific Mastodon post so that it will discover it.</p>
<p>Brid.gy will periodically poll your account looking for new interactions on these posts, and will send any new favourites, boosts or replies to webmention.io.</p>
<p>Note that your post doesn&#39;t count as a webmention - only the interactions on that post do. But you can reply to your own post as a way to trigger a webmention.</p>
<p>When I was setting this up, I was logged into both brid.gy and webmention.io, clicking the &quot;Poll now&quot; button on brid.gy and eagerly looking for interactions to show up. You have to have some patience here as well, as both services have a bit of a delay.</p>
<p>Once you see some mentions show up on webmention.io, you&#39;re ready to render them onto your blog.</p>
<h2 id="5-add-the-webmentions-onto-your-website">5. Add the webmentions onto your website</h2>
<p>Here&#39;s the trickier part. You&#39;ll need to hit the <a href="https://github.com/aaronpk/webmention.io#api">webmention.io API</a> and fetch the mentions for your blog post. You can do this server-side, if you want. My blog is static, so I needed to do this client side.</p>
<p>Since the results are paginated, you can only get back 100 at a time. I wrote this function to help me retrieve all the pages at once, and sort the results into chronological order:</p>
<pre><code class="language-javascript"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getMentions</span><span class="token punctuation">(</span><span class="token parameter">url</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> mentions <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token keyword">let</span> page <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
    <span class="token keyword">let</span> perPage <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">;</span>

    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> results <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span>
            <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://webmention.io/api/mentions.jf2?target=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>url<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&amp;per-page=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>perPage<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&amp;page=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>page<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
        <span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">r</span><span class="token punctuation">)</span> <span class="token operator">=></span> r<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        mentions <span class="token operator">=</span> mentions<span class="token punctuation">.</span><span class="token function">concat</span><span class="token punctuation">(</span>results<span class="token punctuation">.</span>children<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>results<span class="token punctuation">.</span>children<span class="token punctuation">.</span>length <span class="token operator">&lt;</span> perPage<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">break</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        page<span class="token operator">++</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">return</span> mentions<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span>published <span class="token operator">||</span> a<span class="token punctuation">[</span><span class="token string">'wm-received'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token punctuation">(</span>b<span class="token punctuation">.</span>published <span class="token operator">||</span> b<span class="token punctuation">[</span><span class="token string">'wm-received'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token operator">-</span><span class="token number">1</span> <span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Then, I used the results of this to pull out four things: the favourites, boosts, replies, and also a link to the original post where I can send other visitors to my blog if they want to &quot;Discuss this article on Mastodon&quot;. Here&#39;s how that looks:</p>
<pre><code class="language-javascript"><span class="token keyword">let</span> link<span class="token punctuation">;</span>
<span class="token keyword">let</span> favourites<span class="token punctuation">;</span>
<span class="token keyword">let</span> boosts<span class="token punctuation">;</span>
<span class="token keyword">let</span> replies<span class="token punctuation">;</span>

<span class="token keyword">const</span> mentions <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">getMentions</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span>mentions<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    link <span class="token operator">=</span> mentions
        <span class="token comment">// find mentions that contain my Mastodon URL</span>
        <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">m</span><span class="token punctuation">)</span> <span class="token operator">=></span> m<span class="token punctuation">.</span>url<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span><span class="token string">'https://toot.cafe/@JesseSkinner/'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token comment">// take the part before the hash</span>
        <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> url <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> url<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'#'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
        <span class="token comment">// take the first one</span>
        <span class="token punctuation">.</span><span class="token function">shift</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// use the wm-property to make lists of favourites, boosts &amp; replies</span>
    favourites <span class="token operator">=</span> mentions<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">m</span><span class="token punctuation">)</span> <span class="token operator">=></span> m<span class="token punctuation">[</span><span class="token string">'wm-property'</span><span class="token punctuation">]</span> <span class="token operator">===</span> <span class="token string">'like-of'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    boosts <span class="token operator">=</span> mentions<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">m</span><span class="token punctuation">)</span> <span class="token operator">=></span> m<span class="token punctuation">[</span><span class="token string">'wm-property'</span><span class="token punctuation">]</span> <span class="token operator">===</span> <span class="token string">'repost-of'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    replies <span class="token operator">=</span> mentions<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">m</span><span class="token punctuation">)</span> <span class="token operator">=></span> m<span class="token punctuation">[</span><span class="token string">'wm-property'</span><span class="token punctuation">]</span> <span class="token operator">===</span> <span class="token string">'in-reply-to'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Of course, you should replace the link to my profile with the link to your own. I&#39;m taking the first mention (after sorting chronologically) that is interacting with one of my posts, and linking to that post URL.</p>
<p>With those in hand, you&#39;ll have everything you need to render the replies, boosts and favourites to your blog. My approach was to render just the avatars of everyone who boosted or favourited my post, and all the replies.</p>
<p>One thing to watch out for is that the content of each reply is HTML. To be safe (paranoid), I&#39;m running the HTML through <a href="https://github.com/apostrophecms/sanitize-html">sanitize-html</a> to make sure nobody can inject sketchy HTML into my site.</p>
<h2 id="6-allow-people-to-share-posts-without-mentions">6. Allow people to share posts without mentions</h2>
<p>For any posts that don&#39;t have any mentions, I added a different button, &quot;Share this on Mastodon&quot;. When you click it, it runs this code, which prompts you for your Mastodon server (inspired by <a href="https://adventofcode.com">Advent of Code</a>&#39;s share functionality):</p>
<pre><code class="language-javascript"><span class="token keyword">const</span> server <span class="token operator">=</span> <span class="token function">prompt</span><span class="token punctuation">(</span><span class="token string">'Mastodon Instance / Server Name?'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span>server<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// test if server looks like a domain</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>server<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">^[^\s]+\.[^\s]+$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">'Invalid server name'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">const</span> text <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>post<span class="token punctuation">.</span>title<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">" by @JesseSkinner@toot.cafe\n\n</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>url<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>

    window<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>server<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/share?text=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">encodeURIComponent</span><span class="token punctuation">(</span>text<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2 id="yay-for-mastodon-comments">Yay for Mastodon comments!</h2>
<p>I&#39;m really happy with how this turned out. To add some placeholders on my old blog posts, I posted some links to some of the more recent posts, so they interactions would have a place to live. For the older posts, I&#39;m just relying on the share functionality.</p>
<p>I&#39;m considering implementing some server-side functionality to replace either webmention.io or brid.gy in the future, so that the mentions live in my database instead of relying on a third-party service that may disappear one day. I think I could also skip the webmentions process by associating a Mastodon post URL with each blog post, and then using the Mastodon API of my server to periodically check for interactions and replies. Or maybe it could log in to my server and listen for notifications. But for now, this works really well.</p>
<p>So from now on, whenever I write a new blog post, like this one, I&#39;m sure to share in on Mastodon and give a place for readers to ask questions or discuss the article. Check the end of <a href="https://www.codingwithjesse.com/blog/add-mastodon-replies-to-your-blog/#comments">this blog post</a> to see how it all looks, and be sure to favourite, boost or reply to <a href="https://toot.cafe/@JesseSkinner/109586473582034626">my Mastodon post</a> so that you show up on the page as well!</p>
<hr/><p>Interested in web development? <a href="https://www.codingwithjesse.com/newsletter">Subscribe to the Coding with Jesse newsletter!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Advent of Code 2022]]></title>
            <link>https://www.codingwithjesse.com/blog/advent-of-code-2022/</link>
            <guid>https://www.codingwithjesse.com/blog/advent-of-code-2022/</guid>
            <pubDate>Sun, 04 Dec 2022 14:43:48 GMT</pubDate>
            <content:encoded><![CDATA[<p>I&#39;ve been really enjoying working on this year&#39;s <a href="https://adventofcode.com/">Advent of Code</a>. If you haven&#39;t heard of it, it&#39;s a series of coding puzzles, two a day for 25 days, from December 1st to December 25th every year. It only started a few days ago, so it&#39;s not too late to catch up. Or, if you&#39;re reading this later, you can always go back and try it out at your leisure. But, it is a lot of fun to wait until midnight each day to see what the next puzzle is.</p>
<p>What&#39;s cool about it, is that you can use any programming language you want. You just need to take an input file, run a calculation based on the puzzle instructions, and come up with an &quot;answer&quot;, which is usually a number of some kind.</p>
<p>You can use your favourite language to try to come up with an answer as fast as possible, or you can use it as an opportunity to strengthen your skills in another language, even a language you&#39;ve never used before and want to try out!</p>
<p>You need to log in with GitHub, Google, Reddit or Twitter, and then you can download an input file for each day. You&#39;ll need to read the input file in to your language of choice, and parse and process each line of the file.</p>
<p>If you&#39;re really fast, you can even get on the leaderboard. But doing that requires completing the puzzles in just a few minutes at midnight EST so that you&#39;re one of the first 100 people to do so. I&#39;m definitely not fast enough to even bother trying!</p>
<p>So far, I&#39;ve been using JavaScript with Node.js this year. My approach is to pipe in the input into my puzzle solution like this:</p>
<pre><code class="language-bash">node solution.js &lt; input.txt</code></pre>
<p>To do this, I&#39;m using an npm library called <a href="https://www.npmjs.com/package/split">split</a> that simply splits a stream into lines, to make it easier to work with. Here&#39;s a simple example that just counts the number of lines in a stream:</p>
<pre><code class="language-javascript"><span class="token keyword">import</span> split <span class="token keyword">from</span> <span class="token string">'split'</span><span class="token punctuation">;</span>

<span class="token comment">// keep some variables in the module scope to keep track of things</span>
<span class="token keyword">let</span> count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

<span class="token comment">// read the input file as a stream from stdin (Standard Input)</span>
process<span class="token punctuation">.</span>stdin

    <span class="token comment">// pipe it to the split library, to split it into lines</span>
    <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

    <span class="token comment">// receive a callback for each line in the stream</span>
    <span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'data'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">line</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
        <span class="token comment">// do something with each line, in this case just counting</span>
        count<span class="token operator">++</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>

    <span class="token comment">// receive a single callback when the file is done</span>
    <span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'end'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
        <span class="token comment">// do something at the end, eg. console.log the output</span>
        console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>count<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>If you&#39;re interested, there is an <a href="https://www.reddit.com/r/adventofcode/">online community on Reddit</a> where you can share your solution and join in the discussion to see what others have done each day.</p>
<p>I&#39;ll be sharing my progress on Mastodon at <a href="https://toot.cafe/@JesseSkinner">@JesseSkinner@toot.cafe</a>, so you can follow me on there for updates and commentary.</p>
<p>I&#39;ve also been pushing my solutions to Advent of Code up to GitHub, so feel free to <a href="https://github.com/jesseskinner/adventofcode">see how I&#39;ve approached it</a>, if you&#39;re interested. But no cheating! 😉</p>
<hr/><p>Interested in web development? <a href="https://www.codingwithjesse.com/newsletter">Subscribe to the Coding with Jesse newsletter!</a></p>]]></content:encoded>
        </item>
    </channel>
</rss>