<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Posts on Read Only Memory</title>
    <link>https://www.jeffyang.io/posts/</link>
    <description>Thoughts from a life in progress.</description>
    <language>en-us</language>
    <managingEditor>Jeff Yang</managingEditor>
    <copyright>Jeff Yang</copyright>
    <lastBuildDate>Mon, 06 Oct 2025 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.jeffyang.io/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>My Story - Growth from IC3 to IC5</title>
      <link>https://www.jeffyang.io/posts/career-growth-as-an-engineer/</link>
      <pubDate>Mon, 06 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/career-growth-as-an-engineer/</guid>
      <category>Personal Reflection</category>
      <description>&lt;p&gt;&lt;em&gt;My first 3 years at Meta &amp; career growth&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/career-growth-as-an-engineer/meta-career-growth_hu_44b0cbe35130c473.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/career-growth-as-an-engineer/meta-career-growth_hu_b2d5d25d27f4e026.webp 640w, https://www.jeffyang.io/posts/career-growth-as-an-engineer/meta-career-growth_hu_44b0cbe35130c473.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;720&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;It’s been a while since I last wrote here — work and life have been full, and writing fell off my routine. A lot has changed since then. Over the past three years, I’ve gone from an &lt;strong&gt;IC3 engineer to IC5&lt;/strong&gt; at Meta.&lt;/p&gt;&#xA;&lt;p&gt;It’s been fast, chaotic at times, and full of learning curves.&lt;/p&gt;&#xA;&lt;p&gt;This post isn’t advice — just a few honest notes on what that journey looked like for me.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ic3--starting-out&#34;&gt;IC3 — Starting Out&lt;/h2&gt;&#xA;&lt;p&gt;When I joined full-time, I had some context already from a previous internship. That helped a lot — I could ship code from day one and didn’t have to learn Meta’s development stack from scratch.&lt;/p&gt;&#xA;&lt;p&gt;I joined the &lt;strong&gt;Checkout&lt;/strong&gt; team as a backend engineer. About ten of us worked on backend, with another ten on the sister frontend team and a few PMs across the space. It was a big, fast-moving surface with a lot of cross-team dependencies.&lt;/p&gt;&#xA;&lt;p&gt;The first few months were intense. I had to learn not just the product logic, but how the whole production system fit together — everything from loggers to metrics pipelines to how code made it into prod.&lt;/p&gt;&#xA;&lt;p&gt;The “war room” atmosphere built a strange kind of camaraderie; everyone was in it together.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Projects&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Meta Checkout “War Room”&lt;/strong&gt; – supporting stabilization and reliability work during heavy launch cycles.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Meta Checkout Shopify SDK&lt;/strong&gt; – owned the Meta-side API design and communication flow between Shopify and Meta (and got my first taste of cross-company collaboration).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;IC3 was about &lt;strong&gt;execution&lt;/strong&gt;. Shipping quickly, asking the right questions, and being reliable. Nothing fancy — just doing the work well.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ic3--ic4--building-independence&#34;&gt;IC3 → IC4 — Building Independence&lt;/h2&gt;&#xA;&lt;p&gt;It took about a year to go from IC3 to IC4.&lt;/p&gt;&#xA;&lt;p&gt;The biggest shift wasn’t technical — it was how I approached work. At IC4, no one tells you what to do. You’re expected to find things that matter and push them forward yourself.&lt;/p&gt;&#xA;&lt;p&gt;The moment that made a difference was when I took initiative to &lt;strong&gt;refactor a large part of our codebase&lt;/strong&gt;. It wasn’t assigned — it was just something that had to be done if the team wanted to move faster. That project forced me to understand the system end to end and coordinate across engineers to avoid breaking things.&lt;/p&gt;&#xA;&lt;p&gt;I was lucky to have strong mentors (IC5+) who taught me how to balance technical idealism with pragmatism. One funny twist: the project I spent the most time on that year never shipped — a strategic decision above my pay grade. But the promotion still happened, because what mattered wasn’t output, it was &lt;strong&gt;independence, initiative, and reliability&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ic4--broadening-scope&#34;&gt;IC4 — Broadening Scope&lt;/h2&gt;&#xA;&lt;p&gt;Soon after the promotion, I got &lt;strong&gt;reorged&lt;/strong&gt; into the Orders team. I didn’t choose it — it just happened — but it turned out to be good for growth. I had to re-onboard from scratch and learn a completely new system.&lt;/p&gt;&#xA;&lt;p&gt;I worked on the &lt;strong&gt;Order Management System&lt;/strong&gt;, the backend that handles everything from payment capture to refunds.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Projects&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Tax-Inclusive Pricing&lt;/strong&gt; – a horizontal change that touched every order flow (refunds, fulfillment, capture). A lot of careful coordination.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;BYOG (“Bring Your Own Gateway”)&lt;/strong&gt; – building a new order flow to integrate external gateways like PayPal.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;It was challenging at first. The new team had different norms, and I missed my old context. But adapting to change ended up being a skill in itself.&lt;/p&gt;&#xA;&lt;p&gt;Over time, I realized I wanted to work on something more technical and foundational — closer to systems and infrastructure. Product cycles can be exciting, but also repetitive. I wanted to go deeper. That’s what led me to make the switch to &lt;strong&gt;AI Data Infrastructure&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;switching-to-ai-data-infra&#34;&gt;Switching to AI Data Infra&lt;/h2&gt;&#xA;&lt;p&gt;Joining AI Data Infra was like starting over again. Different domain, different tools, different way of thinking.&lt;/p&gt;&#xA;&lt;p&gt;But I was one of the &lt;strong&gt;first engineers&lt;/strong&gt; on the team, which meant more ownership and less guardrail. I had to learn about distributed data systems, LLM training workflows, and what “data quality” means at scale.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Projects&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;GitHub PR Dataset&lt;/strong&gt; – a research-oriented project to ingest and analyze open-source pull request data.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Code Data Sourcing&lt;/strong&gt; – infrastructure for extracting and structuring code data for training models.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The ambiguity was high, but so was the sense of impact. I was no longer just building product features — I was helping build the data backbone for AI research.&lt;/p&gt;&#xA;&lt;p&gt;It was easily the steepest learning curve of my career.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ic4--ic5--from-execution-to-ownership&#34;&gt;IC4 → IC5 — From Execution to Ownership&lt;/h2&gt;&#xA;&lt;p&gt;After about two years at IC4, I was promoted to IC5.&lt;/p&gt;&#xA;&lt;p&gt;The expectations at this level are different. You’re not just delivering tasks — you’re creating direction.&lt;/p&gt;&#xA;&lt;p&gt;I started &lt;strong&gt;mentoring other engineers&lt;/strong&gt;, leading designs, and architecting new pipelines from scratch.&lt;/p&gt;&#xA;&lt;p&gt;The GitHub PR dataset was especially tricky: open-ended, research-driven, and full of uncertainty. Progress depended on aligning people who didn’t technically report to me, finding clarity in fuzzy goals, and building something useful out of it.&lt;/p&gt;&#xA;&lt;p&gt;At this stage, what mattered most was:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Consistency&lt;/strong&gt; – sustained output over time.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Initiative&lt;/strong&gt; – finding problems before someone assigns them.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Leadership in ambiguity&lt;/strong&gt; – bringing clarity to undefined areas.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Time and trust&lt;/strong&gt; – promotions at senior levels are built over multiple review cycles.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Impact (and some luck)&lt;/strong&gt; – timing and organizational needs always play a role.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;It wasn’t about one big launch. It was about building credibility, judgment, and calmness over a long stretch of work.&lt;/p&gt;&#xA;&lt;h2 id=&#34;looking-back&#34;&gt;Looking Back&lt;/h2&gt;&#xA;&lt;p&gt;When I joined as an IC3, I thought growth meant learning new technologies.&lt;/p&gt;&#xA;&lt;p&gt;Now I think it’s more about learning how to operate in uncertainty — how to keep moving when direction isn’t clear.&lt;/p&gt;&#xA;&lt;p&gt;If I had to summarize:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;IC3&lt;/strong&gt; taught me how to execute.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;IC4&lt;/strong&gt; taught me how to take ownership.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;IC5&lt;/strong&gt; taught me how to create direction.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Each step forced me to relearn what “being good at my job” means.&lt;/p&gt;&#xA;&lt;p&gt;I’m grateful for the people and teams I’ve worked with — and for the luck of being in places where I could keep learning.&lt;/p&gt;&#xA;&lt;p&gt;More reflections (and hopefully more regular writing) to come.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>First 3 Months at Meta</title>
      <link>https://www.jeffyang.io/posts/first-three-months-at-meta/</link>
      <pubDate>Sun, 23 Oct 2022 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/first-three-months-at-meta/</guid>
      <category>Personal Reflection</category>
      <description>&lt;p&gt;&lt;em&gt;Reflecting back at my first 3 months at Meta&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/first-three-months-at-meta/meta_hu_b5b8078745e1c49d.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/first-three-months-at-meta/meta_hu_f66dca33c3ac2e3e.webp 640w, https://www.jeffyang.io/posts/first-three-months-at-meta/meta_hu_b5b8078745e1c49d.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;720&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;It&amp;rsquo;s been over 3 months since I started my first job out of college - and I wanted to leave a blog&#xA;post about it. A few things I&amp;rsquo;ll cover in this blog post: my bootcamp experience at Meta, and the&#xA;onboarding process after team selection.&lt;/p&gt;&#xA;&lt;h3 id=&#34;bootcamp-experience&#34;&gt;Bootcamp Experience&lt;/h3&gt;&#xA;&lt;p&gt;Most of you are probably wondering what I mean by &lt;em&gt;bootcamp&lt;/em&gt;. Isn&amp;rsquo;t that a military thing where new&#xA;recruits go through weeks of basic military training? Well yes, but that&amp;rsquo;s not what I&amp;rsquo;m going to&#xA;write about here (thankfully).&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/first-three-months-at-meta/bootcamp_hu_648b8ed912a49210.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/first-three-months-at-meta/bootcamp_hu_648b8ed912a49210.webp 800w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;800&#34; height=&#34;450&#34; alt=&#34;&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;Meta has a long-standing tradition of having all the new hires go through weeks of training&#xA;(different training depending on roles), regardless of seniority. My understanding is that new hires&#xA;that are recruited through the standard pipeline (meaning that you are not recruited for a specific&#xA;team), need to go through bootcamp. Bootcamp serves largely three purposes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Get accustomed to Meta&amp;rsquo;s work culture &amp;amp; company values.&lt;/li&gt;&#xA;&lt;li&gt;Get hands-on experience with Meta&amp;rsquo;s internal tools.&lt;/li&gt;&#xA;&lt;li&gt;Choose a team!&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I found my time in bootcamp to be a little bit redundant, since I&amp;rsquo;m a returning intern. I was&#xA;already relatively familiar with a lot of the internal tools, and had first-hand experience working&#xA;on a real project last summer - so I already had a decent understanding of Meta&amp;rsquo;s fast-paced work&#xA;culture. But it was a great few weeks to get myself mentally prepared to start working at full-pace,&#xA;without having the pressure of having to deliver a project. I also thought that the bootcamp program&#xA;is incredibly well structured. I was assigned a dedicated bootcamp mentor, and there were specific&#xA;engineering trainings available (e.g. iOS, android, backend, etc).&lt;/p&gt;&#xA;&lt;p&gt;The most exciting part of bootcamp is undoubtedly the team selection process. After the first few&#xA;weeks, I started interacting with hiring managers that were looking to hire engineers for their&#xA;team. I worked with ~8 teams and strategically narrowed down my team of choice. My guidelines for&#xA;evaluating teams were:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Instinctive feeling from interacting with team members&lt;/li&gt;&#xA;&lt;li&gt;Instinctive feeling from interacting with team manager&lt;/li&gt;&#xA;&lt;li&gt;Team&amp;rsquo;s area of work (Do I feel &lt;em&gt;excited&lt;/em&gt; about what the team is working on?)&lt;/li&gt;&#xA;&lt;li&gt;Potential projects I would work on if I joined&lt;/li&gt;&#xA;&lt;li&gt;Team&amp;rsquo;s culture around in-person work (I &lt;em&gt;preferred&lt;/em&gt; joining a team with a decent amount of&#xA;in-person work)&lt;/li&gt;&#xA;&lt;li&gt;Team&amp;rsquo;s culture around mentorship&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;In retrospect, I think these guidelines formed a solid framework for my team selection process. It&#xA;wasn&amp;rsquo;t too hard to narrow down to 3 teams - but making the final decision was a bit of a struggle.&#xA;At the end, I opted to listen to my gut feelings. The best part about Meta&amp;rsquo;s bootcamp is that you&#xA;can choose a team &lt;em&gt;after&lt;/em&gt; working on small engineering tasks with the team (and also attend team&#xA;meetings) - so you at least have &lt;em&gt;some&lt;/em&gt; idea of what your day-to-day life would look like if you&#xA;joined the team. &lt;strong&gt;At the end, I chose to join a team that owns the checkout backend &amp;amp;&#xA;infrastructure across Meta&amp;rsquo;s Family of Apps&lt;/strong&gt;. So far, I couldn&amp;rsquo;t be happier with the choice I made.&lt;/p&gt;&#xA;&lt;h3 id=&#34;team-onboarding--learnings&#34;&gt;Team Onboarding &amp;amp; Learnings&lt;/h3&gt;&#xA;&lt;p&gt;Technically I&amp;rsquo;m still onboarding, but I feel like the most basic onboarding is behind me now. I&#xA;still have to ask questions, of course, but I&amp;rsquo;m largely able to navigate through tasks assigned to&#xA;me without too much help. Here&amp;rsquo;s what I learned from the last few months of onboarding as a newbie&#xA;to the team.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Do your research before asking questions.&lt;/li&gt;&#xA;&lt;li&gt;When you are truly stuck, don&amp;rsquo;t be afraid to ask questions to unblock yourself.&lt;/li&gt;&#xA;&lt;li&gt;Read up on the documentations you can find about your team&amp;rsquo;s codebase.&lt;/li&gt;&#xA;&lt;li&gt;Don&amp;rsquo;t overwhelm yourself by trying to understand the entire codebase in a few weeks. Start with&#xA;understanding small parts of it, and gradually connect the dots.&lt;/li&gt;&#xA;&lt;li&gt;Block time for reviewing code everyday.&lt;/li&gt;&#xA;&lt;li&gt;Don&amp;rsquo;t back down from tasks that seem challenging. You can do it.&lt;/li&gt;&#xA;&lt;li&gt;Get in the habit of creating documents when communicating with other engineers. It&amp;rsquo;s easier for&#xA;them to follow if you have a document ready, rather than trying to explain everything on the spot.&lt;/li&gt;&#xA;&lt;li&gt;Schedule regular 1:1 meetings with manager &amp;amp; mentors. I feel like this is by far the best way to&#xA;ensure you are getting regular feedback (this is especially important for newbies).&lt;/li&gt;&#xA;&lt;li&gt;Don&amp;rsquo;t be shy to ask for feedback. It&amp;rsquo;s always nice to hear that you are doing well, but the best&#xA;of them all are the &lt;em&gt;constructive&lt;/em&gt; ones - ask what you can do better to improve. How can you start&#xA;operating at the next level?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;So far it&amp;rsquo;s been a really exciting first few months at Meta. My team has been nothing short of&#xA;amazing, and I&amp;rsquo;m working on an exciting project that I&amp;rsquo;m passionate about delivering. I knew I&amp;rsquo;d&#xA;enjoy working at Meta because I had a great time last summer as an intern - but to be a full-time&#xA;engineer is definitely a different feeling. I&amp;rsquo;m fortunate to wake up everyday, &lt;em&gt;excited&lt;/em&gt; to work :)&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Configuring Portable Dotfiles</title>
      <link>https://www.jeffyang.io/posts/configuring-portable-dotfiles/</link>
      <pubDate>Sat, 30 Jul 2022 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/configuring-portable-dotfiles/</guid>
      <category>Developer Tools</category>
      <description>&lt;p&gt;&lt;em&gt;A blog post on how to manage dotfiles with GNU Stow&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/configuring-portable-dotfiles/thumbnail_hu_2232b31e823ecda4.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/configuring-portable-dotfiles/thumbnail_hu_18567dc688d926d1.webp 640w, https://www.jeffyang.io/posts/configuring-portable-dotfiles/thumbnail_hu_2232b31e823ecda4.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;720&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;I recently stumbled across a problem that I never really had to solve before. As I started my first full-time job, I received a new laptop for work-related use. I also set up my development server, which is a remote computer sitting somewhere in a data center that I can SSH into for development purposes. As my preferred choice of text editor is Neovim, I have a lot of configuration files that go along with it. I also have some custom tmux configurations. What about ZSH configurations? The list goes on. One way of doing it would be to manually copy-and-paste all the configuration files. But there had to be a better way - one that doesn&amp;rsquo;t require so much error-prone manual reptition. If I start a new job at a different company, or if I get a new personal computer for myself, I don&amp;rsquo;t want to have to continue to copy-and-paste all these &amp;ldquo;dotfiles&amp;rdquo;. So I searched for a way to make my dotfiles &amp;ldquo;portable&amp;rdquo;. The solution that I decided to go with was &lt;a href=&#34;https://www.gnu.org/software/stow/&#34;&gt;GNU Stow&lt;/a&gt; - in this post I&amp;rsquo;ll explain a little bit about how Stow works.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-is-gnu-stow-&#34;&gt;What is GNU Stow ?&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;Following is taken from the GNU Stow official website.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;GNU Stow is a symlink farm manager which takes distinct packages of software and/or data located in separate directories on the filesystem, and makes them appear to be installed in the same place. For example, &lt;code&gt;/usr/local/bin&lt;/code&gt; could contain symlinks to files within &lt;code&gt;/usr/local/stow/emacs/bin&lt;/code&gt;, &lt;code&gt;/usr/local/stow/perl/bin&lt;/code&gt; etc.&lt;/p&gt;&#xA;&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;&#xA;&lt;p&gt;What makes GNU Stow so great is that it makes symlinking the config files a breeze. This makes it possible to simply have all of your dotfiles in a single VCS (Version Control Software) repository, whereever that may be in your machine. Let&amp;rsquo;s go over it step-by-step and see how it works.&lt;/p&gt;&#xA;&lt;h4 id=&#34;1-create-a-dotfiles-directory&#34;&gt;1. Create a &lt;code&gt;.dotfiles&lt;/code&gt; directory&lt;/h4&gt;&#xA;&lt;p&gt;In your home directory (not a requirement to be in your home directory, but this is how most people choose to do it), create a &lt;code&gt;.dotfiles&lt;/code&gt; directory.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;pwd&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/Users/jeffyang&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mkdir .dotfiles &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; .dotfiles&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;2-create-subdirectories-for-all-the-programs-you-want-to-configure-through-dotfiles&#34;&gt;2. Create subdirectories for all the programs you want to configure through dotfiles&lt;/h4&gt;&#xA;&lt;p&gt;Now that we have a &lt;code&gt;.dotfiles&lt;/code&gt; directory, we need to put create subdirectories for all the programs that we want to configure and manage with Stow. In this example, I&amp;rsquo;ll go ahead and create three subdirectories for &lt;code&gt;nvim&lt;/code&gt;, and &lt;code&gt;zsh&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;pwd&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/Users/jeffyang/.dotfiles&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mkdir nvim zsh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ tree&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── nvim&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── zsh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For each of the subdirectories we created, Stow will see these as individual worlds. Inside each of the subdirectories that we just created, we need to make sure that we maintain the exact same structure as home directory. For Stow, the directory structures we create for each of the subdirectories &lt;code&gt;nvim&lt;/code&gt;, and &lt;code&gt;zsh&lt;/code&gt; will be the symlink paths that it will create. Let me show you exactly what I mean through real examples.&lt;/p&gt;&#xA;&lt;p&gt;For &lt;code&gt;nvim&lt;/code&gt;, the configuration files would usually live in my home directory under &lt;code&gt;{$HOME}/.config/nvim&lt;/code&gt;. For Stow to know exactly where to symlink the dotfiles to, we need to emulate this exact path.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;pwd&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/Users/jeffyang/.dotfiles/nvim&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ tree&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── .config&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    └── nvim&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ├── autoload&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │   └── plug.vim&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ├── init.vim&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ├── lua&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │   └── jeffyang&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │       ├── colors.lua&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │       ├── git-signs.lua&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │       ├── keymap.lua&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │       ├── lsp-config.lua&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │       ├── lua-line.lua&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │       ├── meta-lsp-config.lua&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │       ├── null-ls.lua&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │       ├── nvim-cmp.lua&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │       ├── telescope.lua&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        │       └── utils.lua&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        └── plugins&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            ├── base.vim&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            ├── neoformat.vim&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            ├── nerdtree.vim&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            ├── tabline.vim&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            └── telescope.vim&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The specific organization of my nvim config files are unimportant - what&amp;rsquo;s important to note here is that inside the &lt;code&gt;nvim&lt;/code&gt; subdirectory we just created, I have &lt;code&gt;.config/nvim/&lt;/code&gt; which contains &lt;code&gt;init.vim&lt;/code&gt;. This way, Stow can symlink the files under the &lt;code&gt;nvim&lt;/code&gt; subdirectory correctly.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s try another example with &lt;code&gt;zsh&lt;/code&gt;. This will be a bit simpler, because ZSH configuration files typically live in &lt;code&gt;{$HOME}/.zshrc&lt;/code&gt;. We&amp;rsquo;ll create this exact structure for Stow.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;pwd&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/Users/jeffyang/.dotfiles/zsh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ tree&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── .zshrc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hopefully now you can see the pattern - the key is to keep the directory structure exactly the same as you would normally under your home directory. Now, let&amp;rsquo;s see how to &lt;em&gt;actually&lt;/em&gt; use Stow to create the symlinks so that the configuration files all work as expected.&lt;/p&gt;&#xA;&lt;h4 id=&#34;3-stowem-all&#34;&gt;3. Stow&amp;rsquo;em all!&lt;/h4&gt;&#xA;&lt;p&gt;This is arguably the easiest part.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; ~/.dotfiles&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;pwd&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/Users/jeffyang/.dotfiles&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ stow nvim &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ stow zsh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it! Stow will create all the symlinks needed to ensure that your configuration files for all the programs that you want to mangae &lt;em&gt;just work&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&#xA;&lt;p&gt;What&amp;rsquo;s so great about Stow is that you can keep &lt;em&gt;all&lt;/em&gt; of your dotfiles in a single VCS repository. You can then push it up to Github or any other VCS cloud provider of your choice. Any time you want to set up your configuration files for the programs you care about, you simply need to download them from the cloud, and Stow them. As a software developer, I can&amp;rsquo;t appreciate Stow enough for finally bringing all my dotfiles under control :)&lt;/p&gt;&#xA;&lt;p&gt;You can check out my dotfiles repo &lt;a href=&#34;https://github.com/jeffjaehoyang/.dotfiles&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Navigating a Computer Science Degree</title>
      <link>https://www.jeffyang.io/posts/navigating-a-computer-science-degree/</link>
      <pubDate>Sat, 19 Mar 2022 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/navigating-a-computer-science-degree/</guid>
      <category>Personal Reflection</category>
      <description>&lt;p&gt;&lt;em&gt;Relection on my life as an undergraduate CS student and takeaways&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/navigating-a-computer-science-degree/northwestern_hu_8d0af36063e16045.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/navigating-a-computer-science-degree/northwestern_hu_cebb63d2910957fd.webp 640w, https://www.jeffyang.io/posts/navigating-a-computer-science-degree/northwestern_hu_8d0af36063e16045.webp 1024w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1024&#34; height=&#34;768&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;I just completed my degree in computer science at&#xA;&lt;a href=&#34;https://www.northwestern.edu&#34;&gt;Northwestern University&lt;/a&gt;. Coming into college, I hadn&amp;rsquo;t written a&#xA;single line of code. Not even a line of &amp;ldquo;hello world&amp;rdquo;. I never saw myself majoring in a&#xA;science/engineering field. Throughout my middle school and high school life, I always considered&#xA;myself more gifted in things like public speech and debate - so naturally, I considered going into&#xA;law, although it was more just a mere stream of thought than a decisive opinion. I came to&#xA;Northwestern thinking that I&amp;rsquo;d study economics (because it seemed like the &lt;em&gt;safe&lt;/em&gt; major to go with,&#xA;for some reason) and international relations. 6 years and a half later (I took 2 years off due to&#xA;mandatory military service in Korea and another half a year off due to COVID-19), here I am, with a&#xA;degree in computer science and a full-time job at Meta (Facebook) secured. It wasn&amp;rsquo;t easy, but I&#xA;truly had an amazing experience at Northwestern. In this blog post, I want to jot down a few notes&#xA;on how I navigated my transformative adventure into the world of computer science and technology.&lt;/p&gt;&#xA;&lt;h3 id=&#34;disclaimer&#34;&gt;Disclaimer&lt;/h3&gt;&#xA;&lt;p&gt;Before we further dive into this post, I&amp;rsquo;d like to carefully just put this out there - I am an&#xA;&lt;em&gt;ambitious&lt;/em&gt; person. I see myself as an over-achiever, and I strive to be a better version of myself&#xA;every single day. That&amp;rsquo;s just who I am. At the same time, I also acknowledge that not everyone is&#xA;the same. Not everyone has the same values in life, and different people live with different&#xA;principles in life. I respect that. This blog post is not meant to persuade readers that they should&#xA;agree with my takeaways from college life. Even more so, this blog post is not meant to intimidate&#xA;prospective computer science students. I am simply reflecting on my college life as a computer&#xA;science (international) student who always dreamed of starting a career as a software engineer in&#xA;the U.S. after graduation. For those who are still interested in reading on, I hope this post will&#xA;help navigate your journey.&lt;/p&gt;&#xA;&lt;h3 id=&#34;computer-science-is-not-for-everyone&#34;&gt;Computer Science is Not For Everyone&lt;/h3&gt;&#xA;&lt;p&gt;The number of computer science students grew exponentially in the past few years. As someone who has&#xA;been in college since 2015, I&amp;rsquo;ve experienced this boom first-hand. When I first came to&#xA;Northwestern, economics was by far the most popular major among undergraduates. Fast forward to&#xA;2022, computer science is one of the most popular majors, if not the most popular major. There are&#xA;good reasons for this. Students seek to get a good return on their investment in college tuition,&#xA;and a computer science degree can pay a handsome salary straight out of college. Let&amp;rsquo;s address the&#xA;elephant in the room. In 2022, fresh grads with offers from top tech companies score a 150k+ salary.&#xA;Many computer science students also pursue summer internships which can pay 20k+ over a single&#xA;summer. Even though most of these top-paying jobs are in high cost of living areas (e.g. SF, NYC,&#xA;Seattle), there is no denying that making 150k+ a year with &lt;em&gt;zero&lt;/em&gt; years of experience is fantastic&#xA;by all standards and measures. Add to that the glamorous picture the media has successfully painted&#xA;around the tech industry - employees playing ping pong at work with free coffee - it&amp;rsquo;s no surprise&#xA;we see a record number of computer science grads every year. However, as great as all this may&#xA;sound, I cannot stress enough how important it is to seriously ask yourself if computer science is&#xA;the right path for you before diving in headfirst. I can confidently say that &lt;strong&gt;computer science is&#xA;not for everyone.&lt;/strong&gt; Just a few off the top of my head: are you okay with spending 10 hours straight&#xA;debugging a program? What if after 10 hours of debugging and losing sleep, you find out there was a&#xA;missing semi-colon on line 37? Are you willing to spend time outside of class building projects that&#xA;may never see the light of the world? Are you willing to constantly learn new technologies? These&#xA;are some of the experiences you will go through if you decide to pursue a computer science degree&#xA;and work in a related field. I can assure you that if you are simply in it for the money, you will&#xA;get burnt out very quickly. It&amp;rsquo;s &lt;em&gt;okay&lt;/em&gt; not to jump on the computer science hype-train just because&#xA;everyone else seems to be jumping on it. I advise you to take a few introductory computer science&#xA;classes to see if it&amp;rsquo;s something you&amp;rsquo;d like to continue with, before fully committing.&lt;/p&gt;&#xA;&lt;h3 id=&#34;school-tips&#34;&gt;School Tips&lt;/h3&gt;&#xA;&lt;h4 id=&#34;tip-1-apply-yourself-while-at-college&#34;&gt;Tip 1: Apply Yourself While at College&lt;/h4&gt;&#xA;&lt;p&gt;Apply yourself while at college. Either you are paying for it (and you have student loans), or your&#xA;parents are paying for you. Either way, going to college is a massive investment - and you should&#xA;try everything you can to make the most out of it. Apply yourself in all the classes and take&#xA;advantage of the resources that make colleges different from bootcamps. Try to sit in the front and&#xA;pay attention to the lecture. Be curious, and be express your desire to learn. You have extremely&#xA;capable professors - go talk to them. Go to office hours and form a close relationship with one or&#xA;two professors. These small things can make a huge difference - it will open doors to research&#xA;opportunities, professors will be more willing to write letters of recommendation, and some of these&#xA;professors may even have personal connections to startups that you may be able to intern at.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-2-utilize-free-online-resources&#34;&gt;Tip 2: Utilize Free Online Resources&lt;/h4&gt;&#xA;&lt;p&gt;The internet has certainly changed the landscape of almost every industry, and education is no&#xA;exception. There are &lt;em&gt;so many&lt;/em&gt; free online resources that you can take advantage of. Although it&amp;rsquo;s&#xA;crucial to go to your classes, don&amp;rsquo;t be afraid to learn on your own using resources that can easily&#xA;be found online. Google is your friend. Youtube is also your friend. Take advantage of them! Also,&#xA;top CS schools (e.g. Stanford, MIT, Carnegie Mellon) often upload their most popular courses on&#xA;Youtube for the world to learn from. If you feel like this is something that you would benefit from,&#xA;go for it! I personally found some of these resources extremely helpful in cementing my&#xA;understanding of certain core CS topics.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-3-dont-cheat&#34;&gt;Tip 3: Don&amp;rsquo;t Cheat&lt;/h4&gt;&#xA;&lt;p&gt;Cheating is quite a serious problem in higher education. Many students face disciplinary measures of&#xA;varying degrees due to violation of academic integrity policies. As is the case with anything else,&#xA;cheating in computer science classes won&amp;rsquo;t get you anywhere. You will be unable to learn the core&#xA;concepts and materials that will be crucial to learning the next thing. Once you start &amp;ldquo;borrowing&amp;rdquo;&#xA;code from your peers without even understanding it, you&amp;rsquo;ll find yourself falling behind quickly. It&#xA;will lead to a vicious cycle of continuous cheating. In the best case, you will have graduated&#xA;without getting caught - but you wouldn&amp;rsquo;t have learned anything while paying tens of thousands of&#xA;dollars for your education. In the worst case - you could get kicked out of school. So don&amp;rsquo;t cheat.&#xA;Try to actually learn the materials and go to office hours to ask questions. Consult the numerous&#xA;free online resources that can help you learn outside of class. If you find yourself having to cheat&#xA;over and over again in your computer science courses - this may be a good sign that you may not be&#xA;the best fit for a CS degree.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-4-dont-be-intimidated&#34;&gt;Tip 4: Don&amp;rsquo;t Be Intimidated&lt;/h4&gt;&#xA;&lt;p&gt;I was very fortunate to have someone who cared about the learning environment as my very first&#xA;computer science professor. It was a mandatory introductory computer science class at Northwestern -&#xA;and there were students of varying coding abilities. Some students started coding at a much younger&#xA;age - and there were students like me, who just started coding in college. The professor didn&amp;rsquo;t let&#xA;the more advanced students show off and didn&amp;rsquo;t let them intimidate other students by asking&#xA;questions about code compilers in front of a class just learning how to write a &amp;ldquo;hello world&amp;rdquo;&#xA;program. But I do acknowledge that not everyone will be lucky enough to have such a caring&#xA;professor! In that case, &lt;strong&gt;do not be intimidated by students that may seem to be ahead of you.&lt;/strong&gt;&#xA;Don&amp;rsquo;t let anyone make you feel small, or inadequate. As long as you are ready to passionately learn&#xA;and put in the effort required, that gap between you and the student asking about code compilers&#xA;will converge to zero over the next few years in college. Also, don&amp;rsquo;t feel too rushed to learn&#xA;everything at once. If you are surrounded by students who seem much more advanced than you are, you&#xA;may feel like you have to learn a million things at once. That will put a massive mental overhead on&#xA;you. Just focus on perfectly mastering the class materials. You&amp;rsquo;ll see that learning becomes a lot&#xA;easier once you are better equipped to learn the more advanced concepts. So don&amp;rsquo;t rush it!&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-5-find-a-mentor&#34;&gt;Tip 5: Find a Mentor&lt;/h4&gt;&#xA;&lt;p&gt;Find a mentor if you can. It&amp;rsquo;s best to find a mentor that can interact with you in person - but if&#xA;you can&amp;rsquo;t, at least find someone (e.g. Youtuber, Twitch tech streamer, etc) that you admire and&#xA;respect. If you are lucky enough to find someone that you can personally interact with, you are in a&#xA;great spot. What&amp;rsquo;s so great about having a mentor is that they are people who have already gone&#xA;through what you are currently going through in the past. They have &lt;em&gt;experience&lt;/em&gt; walking down the&#xA;path that you are walking through right now. So mentors can offer you invaluable advice - but&#xA;remember that you have to be proactive about it. No mentor will baby-sit you. You have to make sure&#xA;that you do all the hard work, and seek guidance from the mentor as much as you can. I was fortunate&#xA;to have a mentor, and I ended up getting an opportunity to intern at a startup that he founded.&#xA;Mentors can also be a source of inspiration, as I looked up to him and aspired to become like him&#xA;one day.&lt;/p&gt;&#xA;&lt;p&gt;So then the question must be, &lt;em&gt;how&lt;/em&gt; do you find a mentor? There is no easy answer for this. But I&amp;rsquo;d&#xA;recommend attending tech-related club events and networking events to expand your network. Do you&#xA;have school alums working at tech companies attending an info session? That&amp;rsquo;d be a great chance for&#xA;you to get to expand your network. Go for it. Also, there can be upperclassmen in your computer&#xA;science classes that may be willing to help you. Always keep an open mind, and be proactive about&#xA;everything.&lt;/p&gt;&#xA;&lt;p&gt;If you are still unable to find anyone or are simply too shy, you could also look for tech Youtubers&#xA;or Twitch tech streamers. This would be a slightly different type of mentorship, as you probably&#xA;won&amp;rsquo;t be interacting directly at a personal level with them. For example, I admire the passion that&#xA;&lt;a href=&#34;https://www.youtube.com/watch?v=qKl1_dtJ2hk&#34;&gt;this Youtuber&lt;/a&gt; shows. He shares some personal stories&#xA;as well as engaging videos about his journey in mastering Vim (the text editor). I found it&#xA;extremely helpful to have a source of inspiration that motivated me to push myself and get better&#xA;everyday.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-6-find-a-hobby&#34;&gt;Tip 6: Find a Hobby&lt;/h4&gt;&#xA;&lt;p&gt;It&amp;rsquo;s crucial to develop a sustainable study schedule. It can be &lt;em&gt;extremely&lt;/em&gt; draining to constantly&#xA;have to code and be under pressure. So instead of trying to force yourself to stick to a grueling&#xA;study schedule that can quickly burn you out, create a sustainable pattern that can help you finish&#xA;the years-long marathon. What that hobby may be really depends on what you like to do. While video&#xA;gaming and binge-watching Netflix are certainly valid ones, I&amp;rsquo;d recommend that you do something more&#xA;active, if possible! It&amp;rsquo;s important to take care of your physical health as well. For me, I try to&#xA;hit the gym almost every day, and it helps me clear my mind.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-7-its-okay-to-take-time-off-school&#34;&gt;Tip 7: It&amp;rsquo;s Okay to Take Time Off School&lt;/h4&gt;&#xA;&lt;p&gt;For those of you that are fortunate enough to be able to take time off college, it&amp;rsquo;s okay to do so!&#xA;It&amp;rsquo;s not a race to graduate as early as possible. But this doesn&amp;rsquo;t mean that you should take time&#xA;off just to rest. Try to make the most out of the time off school. I took 6 months off school to&#xA;pursue an internship opportunity at a startup. While working, I could see how knowledge learned in&#xA;school can be applied in the actual industry - this got me even more motivated to take certain&#xA;classes once I got back to school. Don&amp;rsquo;t think of college as a 4-year race. If you can, it&amp;rsquo;s okay to&#xA;take your time. Do it your way.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-8-start-learning-about-money--personal-finance-earlier&#34;&gt;Tip 8: Start Learning About Money &amp;amp; Personal Finance Earlier&lt;/h4&gt;&#xA;&lt;p&gt;Start learning about money earlier. Get a credit card and start building your credit! Especially if&#xA;you have an internship secured, you should start looking at investing early. Your time invested in&#xA;the stock market is the single most important factor of being a successful investor in the future&#xA;(if you are a &lt;em&gt;long-term investor&lt;/em&gt;, like me). Since software engineering internships pay quite a&#xA;decent salary (ranging from $20 ~ $60+ an hour, depending on the company), you can get a head start&#xA;on your investing journey. I highly recommend&#xA;&lt;a href=&#34;https://thecollegeinvestor.com/34999/the-psychology-of-money-review/&#34;&gt;The Psychology of Money&lt;/a&gt; -&#xA;it&amp;rsquo;s a fantastic and easy read for people looking to gain control of their personal finance.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-9-explore-different-areas-of-computer-science&#34;&gt;Tip 9: Explore Different Areas of Computer Science&lt;/h4&gt;&#xA;&lt;p&gt;At first, I was extremely confused by the generic term &amp;ldquo;software engineer&amp;rdquo;. It seemed to me that a&#xA;person writing C code for a device driver would have a very different job than someone writing&#xA;frontend javascript code to build UI. But they are all referred to as &lt;em&gt;software engineers&lt;/em&gt;. Do I&#xA;need to learn how to do &lt;em&gt;everything&lt;/em&gt; from embedded systems to CSS to be hired as a software&#xA;engineer? The answer is, &lt;em&gt;no&lt;/em&gt;. There are many different fields of software engineering (e.g. Web&#xA;Frontend, Mobile Frontend, Backend API Dev, Systems, Machine Learning, QA Engineer, Networking&#xA;Engineer, etc) and you are most likely going to have to choose one or two fields that you are&#xA;interested in exploring further. As an undergraduate student, you certainly do not have to feel&#xA;pressured to specialize in any of these fields - simply understanding that there are many different&#xA;areas of software engineering is good enough. As you go through your courses in college, try to see&#xA;which classes and technologies interest you the most. Another great way of finding out about which&#xA;part of the &amp;ldquo;tech stack&amp;rdquo; you would be a great fit for is to work on side projects. For example, if&#xA;you are building a relatively standard web application with a separate frontend and backend, you&#xA;should be able to get a feeling of what it would be like to work as a frontend engineer / backend&#xA;engineer. If you decide to get more complicated with your project and add fancy bits of&#xA;infrastructure to it by using AWS, Docker, and many other technologies, you will also be able to get&#xA;some exposure to deeper backend infrastructure work. Little by little, explore different areas of&#xA;software engineering and see what you find interesting.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-10-get-extremely-proficient-with-at-least-one-programming-language&#34;&gt;Tip 10: Get Extremely Proficient with at Least One Programming Language&lt;/h4&gt;&#xA;&lt;p&gt;Become extremely proficient with at least one programming language! You&amp;rsquo;ll be surprised to see how&#xA;many students graduate with a computer science degree without having a single programming language&#xA;they are extremely comfortable with. The reason why you should have at least one programming&#xA;language that you are extremely proficient in is very simple: it makes learning other languages&#xA;&lt;em&gt;that much easier&lt;/em&gt;. Different languages have different syntax, but a lot of the languages share&#xA;similar concepts. As a software engineer, you will have to learn different languages fairly quickly&#xA;on the job, and that becomes a lot easier if you have already mastered a language before. Another&#xA;reason is that you&amp;rsquo;ll have to interview to get jobs, and it helps to be very comfortable with a&#xA;specific language of your choice. It&amp;rsquo;s much easier to focus on the logic of the code (i.e. the&#xA;problem that you are trying to solve) if you are confident about your ability to actually write the&#xA;code out in the language of your choice.&lt;/p&gt;&#xA;&lt;h4 id=&#34;highly-opinionated-tip-11-learn-how-to-use-vim&#34;&gt;(Highly Opinionated) Tip 11: Learn How to Use Vim&lt;/h4&gt;&#xA;&lt;p&gt;Learn how to use VIM! I use Neovim as my primary text editor, and I personally think it has boosted&#xA;my productivity. As a software developer, you&amp;rsquo;ll spend a lot of time in the terminal, so I think&#xA;it&amp;rsquo;s great to get used to tools that live in the terminal, rather than depending on GUIs. There&amp;rsquo;s a&#xA;steep learning curve to learning how to use Vim, but I&amp;rsquo;d say it&amp;rsquo;s totally worth it :)&lt;/p&gt;&#xA;&lt;h3 id=&#34;recruiting-tips&#34;&gt;Recruiting Tips&lt;/h3&gt;&#xA;&lt;h4 id=&#34;tip-0-know-your-data-structures--algorithms-feat-leetcode&#34;&gt;Tip 0: Know Your Data Structures &amp;amp; Algorithms (feat. Leetcode)&lt;/h4&gt;&#xA;&lt;p&gt;Yes, this is tip #0. I cannot emphasize enough how important this is if you want to get a job in&#xA;software engineering. Leetcode-style problems involving knowledge of data structures &amp;amp; algorithms is&#xA;the industry standard way of conducting technical interviews. Like it or not, you are going to have&#xA;to get good at it. It may seem daunting at first, but the key is to create an interview prep plan&#xA;and stick to it. It&amp;rsquo;s extremely important not to rush it. Solving 2 problems every day consistently&#xA;is far better than trying to solve 10 questions a day for two weeks. Try to learn the key concepts&#xA;and patterns instead of memorizing the questions. There are many different sites out there that let&#xA;you practice these kinds of problems, but my recommendation is to stick with&#xA;&lt;a href=&#34;https://leetcode.com/&#34;&gt;Leetcode&lt;/a&gt;. They have by far the largest number of questions and a very&#xA;active community. I&amp;rsquo;d say that purchasing their premium plan is definitely worth it if you are in&#xA;the Leetcode grind. Not sure how to get the most out of Leetcode? Look no further than [this&#xA;post](&lt;a href=&#34;https://leetcode.com/discuss/career/450215/How-to-use-LeetCode-to-help-yourself-efficiently-and-effectively-(for-beginners)&#34;&gt;https://leetcode.com/discuss/career/450215/How-to-use-LeetCode-to-help-yourself-efficiently-and-effectively-(for-beginners)&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;When you are closer to the actual interviews, make sure to do multiple rounds of mock interviews&#xA;before the real one. &lt;a href=&#34;https://www.pramp.com/&#34;&gt;Pramp.com&lt;/a&gt; is a great site to brush up on your&#xA;interview skills.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-1-think-about-internships-early-in-college&#34;&gt;Tip 1: Think About Internships Early in College&lt;/h4&gt;&#xA;&lt;p&gt;If you are considering hopping right into the job market after graduation, the fastest &amp;amp; the most&#xA;straightforward way of achieving that goal is through scoring internships while a student - the most&#xA;important one being your junior year internship. Typically, students are able to get the &amp;ldquo;big&amp;rdquo;&#xA;internships in their junior year - but there&amp;rsquo;s no harm in starting early. Throw yourself out there&#xA;in your freshman/sophomore year. There are internship programs from big companies like&#xA;&lt;a href=&#34;https://buildyourfuture.withgoogle.com/programs/step/&#34;&gt;Google STEP&lt;/a&gt; and&#xA;&lt;a href=&#34;https://diversity.fb.com/initiative/facebook-university/&#34;&gt;Facebook University&lt;/a&gt; that are&#xA;specifically designed for 1st and 2nd-year students. You can also apply to startups on sites like&#xA;&lt;a href=&#34;https://angel.co/&#34;&gt;angel.co&lt;/a&gt;. Don&amp;rsquo;t be afraid to reach out to people on LinkedIn to ask for advice&#xA;and/or potential internship opportunities. Just remember to be respectful of them. If you start&#xA;thinking about internships early in your college life, you&amp;rsquo;ll be well equipped come junior year when&#xA;the stakes are high. You&amp;rsquo;ll know how recruiting works in general, you&amp;rsquo;ll likely have a few&#xA;recruiters that you&amp;rsquo;ve already established connections with, and the whole process won&amp;rsquo;t feel so&#xA;daunting and foreign.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-2-do-side-projects&#34;&gt;Tip 2: Do Side Projects&lt;/h4&gt;&#xA;&lt;p&gt;I&amp;rsquo;m sure you&amp;rsquo;ve already heard someone say this to you, but I&amp;rsquo;m just gonna say it again. Side&#xA;projects are &lt;em&gt;extremely&lt;/em&gt; important as a college student trying to break into the industry without&#xA;much work experience. If you already have multiple internships under your belt, then side projects&#xA;may be less important. However, for those of you that are looking for that first internship and have&#xA;no proper job experience to show for, side projects are &lt;em&gt;critical&lt;/em&gt;. Why? Employers want to see that&#xA;they are hiring someone who can contribute without them having to spoon-feed everything. In software&#xA;engineering, learning computer science theory and reading thick textbooks isn&amp;rsquo;t good enough as it&#xA;doesn&amp;rsquo;t necessarily translate into practical skills. Side projects outside of class is a great way&#xA;to bridge that gap between knowledge learned in class and practical skills required to work in the&#xA;industry. For example, side projects can teach you a lot about how the Git version control software&#xA;works, which is pretty much what the entire industry uses. You can also gain exposure to popular web&#xA;frameworks such as React and Vue.js for frontend and Django and Express.js for backend. If you&#xA;decide to get fancier, you could also play around with various cloud computing platforms such as AWS&#xA;(Amazon Web Services) and GCP (Google Cloud Platform). Knowing how to use these tools makes you a&#xA;&lt;em&gt;much more attractive&lt;/em&gt; candidate in the job market.&lt;/p&gt;&#xA;&lt;p&gt;As a college student, you are a part of your local community (of your peers and neighbors). As a&#xA;student developer, you are in a unique position to try and solve some of the problems that are&#xA;solvable through technology. For example, I built a &lt;a href=&#34;https://sublets-nu-new.herokuapp.com/&#34;&gt;website&lt;/a&gt;&#xA;that attempts to make off-campus room search easier - an exclusive Northwestern marketplace for&#xA;sublets. Although the website isn&amp;rsquo;t currently actively used, I learned &lt;em&gt;a lot&lt;/em&gt; while building this&#xA;website with my team. I also talked about this project in every single interview. The reason why I&#xA;recommend trying to solve a problem that your community uniquely has is that you can keep yourself&#xA;motivated to actually finish the project. Aim to launch the project - it&amp;rsquo;ll be a cool experience!&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-3-write-a-proper-resume&#34;&gt;Tip 3: Write a Proper Resume&lt;/h4&gt;&#xA;&lt;p&gt;Having a resume that is succinct and clear is incredibly important. Obviously, the more quality&#xA;content you have to put on your resume, the better it is. However, writing a good resume is more&#xA;than just having good content. Make sure that you have your resume formatted properly. Make sure&#xA;there aren&amp;rsquo;t any silly grammar mistakes. Make sure you are putting things in the form of&#xA;&lt;strong&gt;accomplished [x], measured by [y], by doing [z]&lt;/strong&gt;. I found&#xA;&lt;a href=&#34;https://www.freecodecamp.org/news/writing-a-killer-software-engineering-resume-b11c91ef699d/&#34;&gt;this guide&lt;/a&gt;&#xA;to be particularly helpful. Remember that recruiters don&amp;rsquo;t look at your resume for more than a few&#xA;seconds - it&amp;rsquo;s up to you to create a resume that&amp;rsquo;s impressive and clear enough to ensure that your&#xA;resume doesn&amp;rsquo;t end up in that &amp;ldquo;no pass&amp;rdquo; pile.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-4-try-to-contribute-to-open-source-projects&#34;&gt;Tip 4: Try to Contribute to Open Source Projects&lt;/h4&gt;&#xA;&lt;p&gt;Try to contribute to open-source projects. Not only can you learn about how software development is&#xA;done in larger companies (e.g. code reviews), you can also learn the basics of Git - these are&#xA;extremely important skills if you want to be successful on the job. On top of that, contributing to&#xA;open source projects (especially the more famous ones) can make your resume really stand out. If you&#xA;are interested but don&amp;rsquo;t know how to get started,&#xA;&lt;a href=&#34;https://github.com/freeCodeCamp/how-to-contribute-to-open-source&#34;&gt;this&lt;/a&gt; is a great place to start.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-5-participate-in-hackathons&#34;&gt;Tip 5: Participate in Hackathons&lt;/h4&gt;&#xA;&lt;p&gt;To be truthful, I&amp;rsquo;ve only participated in one Hackathon in my college life. I wish I had gone to&#xA;more. I had such a great experience at Stanford&amp;rsquo;s TreeHacks - I had the chance to meet numerous&#xA;like-minded people who were passionate about using technology to make the world a better place.&#xA;Participating in a Hackathon can be a great way to pad your resume with an amazing project. Go and&#xA;have fun hacking over a weekend!&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/navigating-a-computer-science-degree/treehacks_hu_7978822c46e8f2b9.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/navigating-a-computer-science-degree/treehacks_hu_7978822c46e8f2b9.webp 768w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;768&#34; height=&#34;1024&#34; alt=&#34;&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;h4 id=&#34;tip-6-take-advantage-of-networking-events-on-campus&#34;&gt;Tip 6: Take Advantage of Networking Events on Campus&lt;/h4&gt;&#xA;&lt;p&gt;Take advantage of on-campus networking events as much as possible. While networking may not be as&#xA;important for computer science students compared to some of the other industries, it still can make&#xA;the world&amp;rsquo;s difference. The most difficult part of the recruiting process is often landing the&#xA;interview itself. By attending on-campus networking events, you can certainly maximize the chances&#xA;that you&amp;rsquo;ll at least get an interview. Unlike when you apply to a company online, where your resume&#xA;will be sitting with tens of thousands of other resumes, you can pass you resume directly to a&#xA;campus recruiter during these events. Some companies even conduct on-campus interviews as well.&#xA;Don&amp;rsquo;t be shy and get yourself out there!&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-7-dont-compare-yourself-to-your-peers&#34;&gt;Tip 7: Don&amp;rsquo;t Compare Yourself to Your Peers&lt;/h4&gt;&#xA;&lt;p&gt;Recruiting is, for most, a draining experience. There are many different reasons as to why it can be&#xA;mentally draining, but I think the single most unhealthy thing to do is to constantly compare&#xA;yourself to your peers. If you have friends around you landing offers from top companies while you&#xA;are struggling, it can be tough. You&amp;rsquo;ll also see that LinkedIn explodes with posts from people who&#xA;are posting their success stories - about their dream FAANG offers. It&amp;rsquo;s important not to compare&#xA;yourself against them. Remember to go at your own pace, and that &lt;em&gt;you only need one offer&lt;/em&gt; no matter&#xA;how many rejections you get. If you feel down, just take a look at&#xA;&lt;a href=&#34;https://rejected.us/&#34;&gt;this website&lt;/a&gt;, and see for yourself how much luck is involved in the process.&#xA;You just need to keep going and never give up :)&lt;/p&gt;&#xA;&lt;h4 id=&#34;tip-8-getting-ghosted-is-okay&#34;&gt;Tip 8: Getting Ghosted is Okay&lt;/h4&gt;&#xA;&lt;p&gt;During my first recruiting season, I applied to about 200 different companies and was ghosted by&#xA;more than 100 of them. &lt;em&gt;This is normal&lt;/em&gt; - especially if your resume doesn&amp;rsquo;t have big-name companies&#xA;on it yet. Don&amp;rsquo;t take it personally, and focus on the companies that do get back to you. Recruiting&#xA;is a numbers game. During the recruiting season where I landed offers from Microsoft and Facebook&#xA;(this was my second recruiting season), I applied to about 200 companies, heard back from 15, had&#xA;final round interviews with 5, and received 2 offers.&lt;/p&gt;&#xA;&lt;p&gt;If you read along this far, thank you! I wanted to share my experience studying computer science in&#xA;the hopes of helping someone out there. Best of luck in your journey!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>A Practical Application of Redis Cache</title>
      <link>https://www.jeffyang.io/posts/practical-application-of-redis-cache/</link>
      <pubDate>Sat, 18 Dec 2021 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/practical-application-of-redis-cache/</guid>
      <category>Web Development</category>
      <description>&lt;p&gt;&lt;em&gt;How a Redis cache can help solve problems when fetching data&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/practical-application-of-redis-cache/thumbnail_hu_3c7f4f2f38dead1c.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/practical-application-of-redis-cache/thumbnail_hu_c271d940961e1bae.webp 640w, https://www.jeffyang.io/posts/practical-application-of-redis-cache/thumbnail_hu_3c7f4f2f38dead1c.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;720&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;Recently, I&amp;rsquo;ve been working on a side project - a web application (&lt;a href=&#34;https://www.inretrospect.finance&#34;&gt;inretrospect.finance&lt;/a&gt;) that lets users record their investments with notes for future reference. The whole project is built on top of a fantastic API from &lt;a href=&#34;https://www.alphavantage.co/documentation/&#34;&gt;Alphavantage&lt;/a&gt; that provides detailed financial data on public companies. While it would have been great if it were possible to make unlimited API requests - their free tier plan only allows for 5 requests per minute and 500 requests per day. This obviously is a significant bottleneck, so I needed to find a solution. After putting some thought into this, I decided to &lt;em&gt;cache&lt;/em&gt; API requests so that whenver there&amp;rsquo;s a &lt;em&gt;cache hit&lt;/em&gt;, a request to the actual API endpoint is not made, therefore cutting down the number of requests to the API service. With that context in mind, let&amp;rsquo;s dive in.&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-what-is-redis&#34;&gt;1. What is Redis?&lt;/h2&gt;&#xA;&lt;p&gt;Redis (&lt;strong&gt;RE&lt;/strong&gt;mote &lt;strong&gt;DI&lt;/strong&gt;ctionary &lt;strong&gt;S&lt;/strong&gt;erver) is an advanced NoSQL key-value data store that is open source (BSD license) - often used as a database, cache, and message broker. Redis is increasingly a popular choice by developers that are looking for an extremely fast and versatile data storage. While it can serve a variety of different purposes, we will focus soley on using Redis as a cache - explicitly to speed up future requests for the same data. I am by no means an expert at Redis, and in this blog post my aim is to document my process of solving a relatively simple problem I faced while building a side project - not to give a detailed technical introduction to Redis. For that, I&amp;rsquo;d recommend visiting their &lt;a href=&#34;https://redis.io/documentation&#34;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;2-installing-redis&#34;&gt;2. Installing Redis&lt;/h2&gt;&#xA;&lt;p&gt;To get started, we first need to install Redis. I have a machine that runs MacOS, so please take that into consideration.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ brew update&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ brew install redis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ brew services start redis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ redis-cli ping&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; PONG&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you want to stop Redis from running in the background, simply run the following:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ brew services stop redis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s about it - you should be able to run Redis on your machine now. For installing Redis on different operating systems such as Linux and Windows, I found &lt;a href=&#34;https://www.kindacode.com/article/how-to-install-redis-on-macos-windows-and-ubuntu/&#34;&gt;this&lt;/a&gt; article to be helpful.&lt;/p&gt;&#xA;&lt;h2 id=&#34;3-how-can-redis-help&#34;&gt;3. How can Redis Help?&lt;/h2&gt;&#xA;&lt;p&gt;Before we proceed, I&amp;rsquo;d like to explain a little more about the problem I faced, and why Redis turned out to be a great option to address the problem at hand. In the web application &lt;a href=&#34;https://www.inretrospect.finance&#34;&gt;inretrospect.finance&lt;/a&gt;, I needed historical stock market data for stocks of user&amp;rsquo;s choice (US stocks). For the purposes of this application, I did not need real-time market data, but I did need the data to be updated once a day after market close. For example, if a user registered Facebook and Google as stocks of interest, I needed historical market data on both of these stocks in order to render a line chart. To help your understanding, below is a screenshot of the UI that needs such historical market data to render properly.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/practical-application-of-redis-cache/stock_chart_hu_c023dcde1f3dd02b.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/practical-application-of-redis-cache/stock_chart_hu_b44db9d7f672ad72.webp 640w, https://www.jeffyang.io/posts/practical-application-of-redis-cache/stock_chart_hu_c023dcde1f3dd02b.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;519&#34; alt=&#34;&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;I obviously cannot magically pull stock market data out of nowhere - I needed to use an API that provides such market data. Now, let us think about how one might go about achieving this relatively simple task. The most naive and simple solution would be to send a request to the API endpoint whenever the UI is rendered. I used React to build the client side of the application - let us see what such an implementation would look like in code.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5&#xA;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt; 6&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt; 7&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt; 8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt; 9&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// StockCard component&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stockData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;setStockData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;useState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fetchData&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// API request is made here with the ticker symbol of interest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`/stockData?symbol=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tickerSymbol&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nx&#34;&gt;setStockData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;c1&#34;&gt;// handle error here&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;useEffect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// fetch data on every render of component&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fetchData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s a standard API call that would occur in the &lt;code&gt;useEffect&lt;/code&gt; hook of a React functional component - which updates the state upon a successful retrieval of data from the API request. This implementation &lt;em&gt;works&lt;/em&gt;, but what would happen if someone had 5 stocks registered and hit the refresh button 3 times? That would be 15 API calls in the span of 5 seconds - and this is problematic in two ways. Firstly, the free tier API service that I used only supports up to 5 API calls per minute. Secondly, this naive implementation would be suboptimal in terms of user experience, since every single render of the component would need to wait for the API to return data. This is where caching can help us significantly. If we can cache the data returned by the API somewhere, we would be able to fetch data that has been requested previously right from the cache instead of waiting for the API to respond. That&amp;rsquo;s &lt;em&gt;exactly&lt;/em&gt; where Redis comes to the rescue!&lt;/p&gt;&#xA;&lt;h2 id=&#34;3-setting-up-redis-within-an-express-application&#34;&gt;3. Setting up Redis Within an Express Application&lt;/h2&gt;&#xA;&lt;p&gt;In order to handle the caching logic, I created an API endpoint that acts as a wrapper to the actual API endpoint that provides the market data. I did this using a Node.js Express application - let us see how to set up Redis within a basic and simple Express application. Since this post is not meant to be an Express tutorial, I&amp;rsquo;ll assume that you already have an Express app up and running, or that you&amp;rsquo;d be able to set it up yourself (it should be rather simple).&lt;/p&gt;&#xA;&lt;p&gt;We first need to install Redis client that will help us interact with the Redis service that is being run in the background. Because our backend is being built in Node.js, we will need to do the following within the Express project directory:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ npm install redis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OR&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ yarn add redis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;redis&lt;/code&gt; package installed will help us interact with Redis instances easily in our Node.js codebase. Let us first create a Redis client in our Express application before moving on to the actual business logic (i.e. caching API responses).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16&#xA;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt;17&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt;18&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt;19&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt;20&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// load dotenv file for sensitive information&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;dotenv&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;express&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;express&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;axios&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;axios&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;redis&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;redis&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cors&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;cors&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;express&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;redisPort&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6379&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;REDIS_URL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// production&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;client&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;redis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;createClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;REDIS_URL&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// dev&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;client&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;redis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;createClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;port&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;redisPort&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;Note that I am initializing a Redis client in two different ways - one with a specified URL and one with a specified port number. I conscioulsy made this choice because I needed to configure Redis in Heroku. In practice, these two different ways of creating a Redis client bear no difference.&lt;/p&gt;&#xA;&lt;p&gt;Now, let us see the code that actually handles the core logic. The flow is as follows:&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/practical-application-of-redis-cache/cache_explain_hu_405739f716910bcb.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/practical-application-of-redis-cache/cache_explain_hu_5b325a84e0423e09.webp 640w, https://www.jeffyang.io/posts/practical-application-of-redis-cache/cache_explain_hu_405739f716910bcb.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;720&#34; alt=&#34;&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;With that in mind, let&amp;rsquo;s take a look at the code.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34; filename=&#34;server.js&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16&#xA;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt;17&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt;18&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt;19&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;hl&#34;&gt;&lt;span class=&#34;lnt&#34;&gt;20&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// implement API endpoint&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/stockData&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;tickerSymbol&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tickerSymbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stockData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stockData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// cache hit --&amp;gt; simply return data stored in cache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;JSON&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stockData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// cache miss --&amp;gt; attempt API call&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stockData&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;axios&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;sb&#34;&gt;`https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&amp;amp;symbol=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tickerSymbol&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;amp;outputsize=full&amp;amp;apikey=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ALPHAVANTAGE_API_KEY&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;c1&#34;&gt;// stringify data to store in redis cache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;storableStockData&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;JSON&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stringify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stockData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;c1&#34;&gt;// store data from API in redis cache - expiration timeout 24H&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line hl&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nx&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;setex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tickerSymbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;24&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;60&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;60&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;storableStockData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stockData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;500&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;message&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;API Response Err&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;500&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;message&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;Relatively simple and straightforward implementation, right? Redis has an extremely friendly API and makes for a delightful developer experience. With minimal amount of code, you can bring drastic changes to the logic of your code for the better. If you want to check the Redis cache yourself, you can do the following:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ redis-cli&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;// a cache miss&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1:6379&amp;gt; GET &lt;span class=&#34;s2&#34;&gt;&amp;#34;GOOG&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;nil&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;// a cache hit&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1:6379&amp;gt; GET &lt;span class=&#34;s2&#34;&gt;&amp;#34;GOOG&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;DATA SHOULD BE SHOWN&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, let us check if Redis actually helped us achieve our goals - how much faster is Redis compared to an API call over the network?&lt;/p&gt;&#xA;&lt;h2 id=&#34;4-measuring-performance-with-redis-implementation&#34;&gt;4. Measuring Performance with Redis Implementation&lt;/h2&gt;&#xA;&lt;p&gt;It&amp;rsquo;d be interesting to see how much of a difference Redis can make - how much faster is it with Redis? To test this, let&amp;rsquo;s first try requesting data over the API that is not stored in cache.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;// verify that there is no data stored&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1:6379&amp;gt; GET &lt;span class=&#34;s2&#34;&gt;&amp;#34;NFLX&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;nil&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using Chrome Developer Tools, we can inspect the intricate details of networking operations. The screenshot below shows how long it took for the API request to process. &amp;ldquo;Waiting (TTFB)&amp;rdquo;  indicates total time for the sent request to get to the destination (in this case the Express backend), then for the destination to process the request, and finally for the response to traverse the networks back to the client. It took &lt;strong&gt;7.5 seconds&lt;/strong&gt; for an API call asking for historical market data on Netflix stocks.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/practical-application-of-redis-cache/without_cache_hu_959d4a1342ee51d4.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/practical-application-of-redis-cache/without_cache_hu_b43b6915438c6691.webp 640w, https://www.jeffyang.io/posts/practical-application-of-redis-cache/without_cache_hu_959d4a1342ee51d4.webp 1172w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1172&#34; height=&#34;208&#34; alt=&#34;API call without cache.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;API call without cache.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;Now, let us verify that data on Netflix is actually stored in our Redis cache.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;// verify that there is data stored stored&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1:6379&amp;gt; GET &lt;span class=&#34;s2&#34;&gt;&amp;#34;NFLX&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;HUGE DATA LOAD STORED AS STRING&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, let&amp;rsquo;s refresh the page to initiate another call to our backend, and inspect with Chrome Developer Tools.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/practical-application-of-redis-cache/with_cache_hu_5d80e35a75adf82e.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/practical-application-of-redis-cache/with_cache_hu_c4108d5d99bcfb63.webp 640w, https://www.jeffyang.io/posts/practical-application-of-redis-cache/with_cache_hu_5d80e35a75adf82e.webp 1168w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1168&#34; height=&#34;204&#34; alt=&#34;API call with cache.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;API call with cache.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;&lt;em&gt;Wow&lt;/em&gt;. The cached response only took &lt;strong&gt;60 milliseconds&lt;/strong&gt;. We just shaved off &lt;strong&gt;99.3%&lt;/strong&gt; of the total response time with the Redis cache implementation. It&amp;rsquo;s not only that - by caching API responses in Redis cache, I was also able to work with the Alphavantage free tier API during development (maximum of 5 requests per minute and 500 requests per day). If &lt;a href=&#34;https://www.inretrospect.finance&#34;&gt;inretrospective.finance&lt;/a&gt; scales up in the future, there will definitely be a need to start using the paid plans, but for now, this will do.&lt;/p&gt;&#xA;&lt;h2 id=&#34;wrapping-up&#34;&gt;Wrapping Up&lt;/h2&gt;&#xA;&lt;p&gt;What we went through today is an example of how the Redis cache can be used in a real-world project. Redis cache is so simple to use, yet so powerful. As developers, it is our responsibility to ensure that users get the best experience from the software we create - tools like Redis make our lives so much easier! I plan on writing another blog post on things I learned while building &lt;a href=&#34;https://www.inretrospect.finance&#34;&gt;inretrospect.finance&lt;/a&gt;, so stay tuned :)&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>My Facebook Internship</title>
      <link>https://www.jeffyang.io/posts/my-facebook-internship/</link>
      <pubDate>Wed, 08 Sep 2021 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/my-facebook-internship/</guid>
      <category>Personal Reflection</category>
      <description>&lt;p&gt;&lt;em&gt;Working at Facebook as a remote software engineering intern&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/my-facebook-internship/facebook-logo_hu_4e08291e0c80b9e3.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/my-facebook-internship/facebook-logo_hu_3e1674d07cf671f4.webp 640w, https://www.jeffyang.io/posts/my-facebook-internship/facebook-logo_hu_4e08291e0c80b9e3.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;804&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;I started my journey in computer science as a freshman in college. Little did I know, this journey would take me to the heart of Silicon Valley.&lt;/p&gt;&#xA;&lt;p&gt;In this blog post, I share my experience as a software engineer intern at Facebook. For those who are trying to gauge if Facebook is the right fit - I hope this post will provide valuable insight. For those who are on the grind for an internship - I hope this will serve as a source of inspiration to keep going. Facebook seemed like a pipe dream, but through grit and little bit of luck, I&amp;rsquo;m really happy to have made the impossible possible.&lt;/p&gt;&#xA;&lt;h3 id=&#34;application-process&#34;&gt;Application Process&lt;/h3&gt;&#xA;&lt;p&gt;In tech, the greatest challenge is landing the interview itself. The competition for a software engineering gig is borderline ridiculous, with tens of thousands of students competing for only a handful of available spots. A LinkedIn resume drop or applying through a portal will only get you so far, and many times, without an interview.&lt;/p&gt;&#xA;&lt;p&gt;My recommendation is to at least submit your application alongside a referral from a current employee of that company. For anyone applying to Facebook, I would highly recommend asking for a referral from someone who can speak to your qualities as an engineer.&lt;/p&gt;&#xA;&lt;p&gt;However, my journey with Facebook started out a bit differently — I got lucky. A recruiter reached out to me on Piazza, a learning management site used by professors at my university, after viewing my resume. Frankly, it was the unlikeliest of all places for a Facebook recruiter to be reaching out. But it happened. I was invited to interview — although my first attempt at cracking the coding interview was unsuccessful.&lt;/p&gt;&#xA;&lt;p&gt;Exactly a year later, in Fall 2020, I gathered the courage to contact the same recruiter and was lucky to be granted a chance to interview again. This time, I cracked both rounds of technical interviews. A couple days after the second round, I received a call from the recruiter with a formal offer to join Facebook for Summer 2021 as a software engineer intern. It felt surreal.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/my-facebook-internship/facebook-piazza_hu_6286c74a49bfd484.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/my-facebook-internship/facebook-piazza_hu_baba4d3b6ecc78d7.webp 640w, https://www.jeffyang.io/posts/my-facebook-internship/facebook-piazza_hu_6286c74a49bfd484.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;543&#34; alt=&#34;Recruiter message on Piazza Careers.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;Recruiter message on Piazza Careers.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;h3 id=&#34;team-selection&#34;&gt;Team Selection&lt;/h3&gt;&#xA;&lt;p&gt;The team selection process for interns at Facebook is done in a manner so that both parties — the interns and company — benefit from the program. Few months prior to the internship, interns are given a survey through which they can express interest in certain pillars within Facebook.&lt;/p&gt;&#xA;&lt;p&gt;Specific teams are NOT provided as options to select from, but interns can express interest in certain fields (e.g. machine learning, frontend engineering, backend engineering, etc) and app pillars (e.g. Instagram, Facebook, Oculus, WhatsApp, Messenger, etc).&lt;/p&gt;&#xA;&lt;p&gt;Once the surveys are submitted, a mysterious script magically performs team matching. From what I experienced and heard from others, the team matching script does a pretty great job of satisfying both parties.&lt;/p&gt;&#xA;&lt;p&gt;I was matched with the Business Messaging Lead Gen Ads team, a business-facing ads product team. It is a product used by businesses worldwide, and at the time I was matched, I felt it was an assignment I could excel in.&lt;/p&gt;&#xA;&lt;h3 id=&#34;a-remote-internship&#34;&gt;A Remote Internship&lt;/h3&gt;&#xA;&lt;p&gt;Due to the pandemic, the 2021 Summer Internship Program was fully remote. Although my team was based in Menlo Park, I chose to work from Los Angeles. It made the most sense as LA shares the same time zone, has more affordable rent, and offers a lot as a city, including outdoor hikes, beautiful beaches, and great restaurants — especially Korean ones. My roommate was also working remotely as a software engineer intern at a different tech company, so we decided to rent an Airbnb together.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/my-facebook-internship/la-scenery_hu_bc94e911e1b8a97f.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/my-facebook-internship/la-scenery_hu_c45312ed9a25bf2c.webp 640w, https://www.jeffyang.io/posts/my-facebook-internship/la-scenery_hu_bc94e911e1b8a97f.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;960&#34; alt=&#34;Beautiful scenery of LA.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;Beautiful scenery of LA.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;Working remotely meant that Facebook shipped its tech equipment to all interns.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/my-facebook-internship/facebook-equipments_hu_1023351a3c017ccb.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/my-facebook-internship/facebook-equipments_hu_13bc24b7a12bfcd5.webp 640w, https://www.jeffyang.io/posts/my-facebook-internship/facebook-equipments_hu_1023351a3c017ccb.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;960&#34; alt=&#34;Facebook tech equipments.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;Facebook tech equipments.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;Facebook also sent out intern swag a few weeks before the internship!&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/my-facebook-internship/facebook-swag_hu_54e1a59fbed701ae.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/my-facebook-internship/facebook-swag_hu_b2a9f598fbedfec9.webp 640w, https://www.jeffyang.io/posts/my-facebook-internship/facebook-swag_hu_54e1a59fbed701ae.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;960&#34; alt=&#34;Facebook swag.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;Facebook swag.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;I set up a nice home office for myself — having that ultrawide monitor was extremely helpful. 😇&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/my-facebook-internship/desk-setup_hu_73ede4e95451ac14.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/my-facebook-internship/desk-setup_hu_de8dc8715285470f.webp 640w, https://www.jeffyang.io/posts/my-facebook-internship/desk-setup_hu_73ede4e95451ac14.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;960&#34; alt=&#34;Home office setup due to COVID-19.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;Home office setup due to COVID-19.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;h3 id=&#34;imposter-syndrome&#34;&gt;Imposter Syndrome&lt;/h3&gt;&#xA;&lt;p&gt;Imposter syndrome is defined as doubting your abilities and feeling like a fraud. I heard and read about it on countless online forums, but had never experienced it myself until my internship with Facebook. As excited as I was to work at Facebook, the thought of working at one of the world’s leading tech firms was incredibly daunting. I always had the idea that engineers working at companies like Facebook and Google were geniuses — I never thought I would belong to one of them.&#xA;As the start date loomed, I became slightly overwhelmed. Would I be able to meet expectations at Facebook? What if it takes too long for me to learn the internal tools that are required for work? What if other engineers on the team think I don&amp;rsquo;t deserve to be working with them? I spent hours reading about imposter syndrome on various tech forums online, and it offered a sense of comfort to know that many others were experiencing the same thing I was. I read a lot of advice along the lines of &amp;ldquo;There&amp;rsquo;s a reason why they hired you. Believe in yourself&amp;rdquo; — but still, I wasn’t convinced.&lt;/p&gt;&#xA;&lt;h3 id=&#34;ramping-up&#34;&gt;Ramping Up&lt;/h3&gt;&#xA;&lt;p&gt;In the blink of an eye, I started my internship. The first  days consisted of orientation and general training on source control (Facebook uses Mercurial instead of Git). Once I completed general training, I held my first official meeting with my intern manager. We spent that time getting to know each other before diving into the details of the internship. My manager prepared a very detailed document on my intern project, including general onboarding documents on our team&amp;rsquo;s codebase. We also went over my goals and KPIs, ensuring all expectations were clearly communicated. This included preferred ways of receiving feedback, working hours, and more. I felt very supported by my intern manager throughout the internship, and the project was most definitely doable within the given time frame (it was well scoped out by my manager beforehand). I outlined 2 goals for myself and communicated these very clearly to my manager:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;To receive a return offer at the end of the internship&lt;/li&gt;&#xA;&lt;li&gt;To get to the level where I could confidently review another team member&amp;rsquo;s code and approve it (at Facebook, only one person needs to review and approve code to ship it to production.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I was assigned starter tasks that provided just the right amount of exposure to Facebook’s vast codebase. After completing the starter tasks in a few days, I moved on to tackling the intern project milestones. By this point, I felt increasingly confident that I would be able to handle the work assigned. It also helped to have a supportive team and intern manager who genuinely wanted me to succeed.&lt;/p&gt;&#xA;&lt;h3 id=&#34;not-just-an-intern&#34;&gt;Not Just an Intern&lt;/h3&gt;&#xA;&lt;p&gt;At Facebook, I never felt like “just an intern.” I had full access to the gigantic monorepo (yes, Facebook keeps all its code in a single repository), attended meetings with full time engineers, performed code reviews, provided feedback on our team&amp;rsquo;s products, and had full control over my working schedule.&lt;/p&gt;&#xA;&lt;p&gt;Furthermore, I worked on a full-stack project using technologies such as React for frontend and Hack/PHP for backend logic (Note: a lot of Facebook&amp;rsquo;s backend code is written in Hack/PHP, especially for product teams). Although I had extensive experience with React, I never worked with Hack/PHP, but it was an easy learning curve  as it is quite similar to other programming languages I was already familiar with. As such, I finished my intern project in 7 weeks.&lt;/p&gt;&#xA;&lt;p&gt;For the remaining 5 weeks, I drafted a proposal for my own mini-venture to build on the project I had completed. Once I received my team’s blessings, I collaborated closely with our product designer, product manager, and content designer to bring it to life.&lt;/p&gt;&#xA;&lt;p&gt;That’s why what I loved about interning at Facebook was contributing to projects that impact the real world. The feature I built, for example, was highly requested by high-profile clients such as AT&amp;amp;T and Lexus.&lt;/p&gt;&#xA;&lt;h3 id=&#34;takeaways&#34;&gt;Takeaways&lt;/h3&gt;&#xA;&lt;p&gt;I learned so much during my time at Facebook. Not only that, I left Facebook a much more confident engineer. Here are some of the takeaways:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;You don&amp;rsquo;t need to be a programming prodigy to work at Facebook. You need to be self-driven and eager to learn!&lt;/li&gt;&#xA;&lt;li&gt;Soft skills are extremely important for engineers. Code is merely the end product that software engineers produce, but a lot more goes into making that happen.&lt;/li&gt;&#xA;&lt;li&gt;There are lots of internal tools to navigate at big tech companies like Facebook. Learning how to use them will help you become much more productive.&lt;/li&gt;&#xA;&lt;li&gt;Don&amp;rsquo;t take existing code for granted. Just because it passed code reviews and was merged to the main trunk does not mean that the code is failproof.&lt;/li&gt;&#xA;&lt;li&gt;Take advantage of the opportunity to work with senior engineers and learn from their code. Review their code and see how they write theirs differently. How would you have implemented the same thing yourself and how did they do it differently?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;tips-for-future-facebook-interns&#34;&gt;Tips for Future Facebook Interns&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Communication is key - always make sure that your intern manager is on the same page as you. Share your progress daily, and whenever you reach an important milestone, communicate your accomplishment to your team as well. Communication was the single most important factor in having completed my remote internship successfully. It makes it easier for your manager to vouch for you in the final calibration as they’ll have a solid idea of your hard work. Two peer engineers also write reviews for you in the final calibration, so make sure to also communicate with them as much as possible.&lt;/li&gt;&#xA;&lt;li&gt;Don&amp;rsquo;t be afraid to ask questions, especially when you are having trouble getting unblocked.&lt;/li&gt;&#xA;&lt;li&gt;If you want to maximize your chance of a return offer, do yourself a favor and start working on your project as early as you can.&lt;/li&gt;&#xA;&lt;li&gt;Once you receive comments on your code, act on it, and make sure to keep a checklist before submitting your code for review in the future. It&amp;rsquo;s best that other engineers don&amp;rsquo;t have to point out the same things over and over again.&lt;/li&gt;&#xA;&lt;li&gt;Remember that an internship is a sprint, not a marathon. You only have ~10 weeks to prove that you can provide value to the team. Don&amp;rsquo;t focus too much on maintaining a great work life balance during your internship - you can worry about that when you’re full-time. Make sure you stay focused on doing whatever you can to deliver your project in time!&lt;/li&gt;&#xA;&lt;li&gt;Own your project. Remember that Facebook treats you as a full-time employee during your internship. If you have ideas, share it with your team. Feel free to expand on the scope of your intern project. Showing initiative is a quality they look for - so don&amp;rsquo;t be shy!&lt;/li&gt;&#xA;&lt;li&gt;Before asking design questions about the code you are writing, always form your own opinion first. They will be curious on what your thoughts are before they present their take on it. It&amp;rsquo;s always a good idea to outline your thought process before asking for an answer.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Working at an Early Stage Startup (Pt. 2)</title>
      <link>https://www.jeffyang.io/posts/fumi-internship-2/</link>
      <pubDate>Sat, 13 Mar 2021 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/fumi-internship-2/</guid>
      <category>Personal Reflection</category>
      <description>&lt;p&gt;&lt;em&gt;What I learned working at an early stage startup - the engineering side&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/fumi-internship-2/fumi_startup_two_hu_d47952f1ad874953.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/fumi-internship-2/fumi_startup_two_hu_ace2d7fb00ed3f25.webp 640w, https://www.jeffyang.io/posts/fumi-internship-2/fumi_startup_two_hu_d47952f1ad874953.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;720&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;&lt;/em&gt;  &lt;em&gt;This post does not in any way represent the views of Fumi as a company, but is solely my individual opinion and reflection on my experience as a software engineer at an early-stage startup&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;I was a software engineering intern, after all - so let&amp;rsquo;s talk about things from an engineer&amp;rsquo;s perspective. To give you context, I was part of a small engineering team of 3, and did not have a direct report (i.e. no &amp;ldquo;manager&amp;rdquo;). Although my title was a &amp;ldquo;software engineering intern&amp;rdquo; I was more like a full-time contractor. I was (for the most part) expected to do things on my own, as the other 2 engineers did not have enough time to be taking care of an intern.&lt;/p&gt;&#xA;&lt;h4 id=&#34;what-did-i-work-on&#34;&gt;What did I work on?&lt;/h4&gt;&#xA;&lt;p&gt;I was a full-stack engineer - I worked both on the frontend and the backend of Fumi&amp;rsquo;s main product and Fumi&amp;rsquo;s back office application (inventory tracking, delivery tracking, etc). The Fumi mobile app used Ionic, Capacitor, Angular 9 on the frontend and used express, GraphQL and MySQL on the backend. The back office application used Angular 9 and KendoUI on the frontend, and used the identical set of backend technologies as the Fumi app.&lt;/p&gt;&#xA;&lt;h4 id=&#34;struggle-1--development-speed-vs-quality-of-code&#34;&gt;Struggle 1 : Development Speed vs Quality of Code&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The greatest struggle that I had while working at Fumi as an engineer was trying to figure out the right balance between the speed of development vs the quality of development. Every sprint, the COO pushed for a rather tight (?) schedule that was often unrealistic. Of course, it was up to us as developers to agree/disagree with the COO in setting the deadlines, but it was sometimes almost as if the company leadership really didn&amp;rsquo;t care about the quality of the &lt;em&gt;software&lt;/em&gt; we were building. They did care about the &lt;em&gt;product&lt;/em&gt; that the end-users will see, but not so much the internal &lt;em&gt;software.&lt;/em&gt; It was a real struggle because as an engineer, I wanted to write and produce code that I would be happy to revisit in 3 months, but with the timeframe we were working with (runway of about 1.5 yrs), asking for more time to refactor and write maintainable, clean code was often difficult. Even writing proper test cases to increase confidence in code (especially during code release) was a luxury we could not afford. At the same time, I also understood that quick iterations (depending on user feedback) were &lt;em&gt;absolutely necessary&lt;/em&gt; to survive as a startup. After all, what&amp;rsquo;s the point of writing 100% coverage test suites and meticulously refactoring code if the newly developed feature is going to be tossed out the window in a few weeks upon being labeled a &amp;ldquo;failure&amp;rdquo;?&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The conclusion I walked away with was that it&amp;rsquo;s important to carefully evaluate whether or not certain parts of the code base are worth investing time into refactoring and perfecting. Is this piece of code an integral part of the user experience, and is the code likely to be used elsewhere in the future? If so, go ahead and ask for more time, and perfect the code to your heart&amp;rsquo;s content. Is not, feel comfortable moving on without looking back. An early-stage startup need not hold their engineering standards on the same level as Google&amp;rsquo;s or NASA&amp;rsquo;s - different kinds of engineering is required for different occasions.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;struggle-2--lack-of-mentorship&#34;&gt;Struggle 2 : Lack of Mentorship&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;As I mentioned above, I was more of a full-time contractor than an intern while working at Fumi. There was no structured program to help a junior engineer succeed and get guidance. This is very unlike interning at a larger company with a well-structured internship, where there is a designated intern manager and a whole team of engineers that is always willing to help out. Being able to figure things out on yourself (of course, with the help of Stackoverflow) is considered an important quality to have as a software engineer, but mentorship and guidance from experienced engineers is always invaluable. This is NOT to say that I didn&amp;rsquo;t learn from working at Fumi - I learned a lot. However, most of the learning came from teaching myself new technologies and having to do things myself, not from dedicated mentors.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;struggle-3--getting-used-to-bad-engineering-practices&#34;&gt;Struggle 3 : Getting Used to Bad Engineering Practices&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;I am a firm believer that different kinds of companies require different kinds of engineering. For example, an engineering team at an early stage startup that is constantly fighting for survival cannot have the same level of attention to detail as an engineering team at Google can afford to have. &lt;em&gt;However,&lt;/em&gt; if your goal is to learn the &amp;ldquo;best engineering practices&amp;rdquo; such as having thorough code reviews and 100% test coverage, an early stage startup may not be what you are looking for. Always chasing deadlines after deadlines, engineers at early stage startups are often forced to produce duplicate code, spaghetti code (that would be an absolute nightmare to refactor in 3 months), and hacky code - &lt;em&gt;that just works.&lt;/em&gt; Most early stage startups don&amp;rsquo;t have the luxury to think months in advance and what to do when the company scales - shipping this feature on that date is what matters.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;struggle-4--communication&#34;&gt;Struggle 4 : Communication&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;I want to stress the importance of communication. Communication with a product manager, a product designer, or company leadership is extremely important to keep everyone on the same page. For example, I found it very helpful to create slack channels or project proposal documents. It was also very important to learn to clearly communicate what I thought was an appropriate timeline for a given project. What I struggled with especially towards the beginning of my internship was to accept all the deadlines given. If you think a deadline is unreasonable, speak out and let yourself be heard! I realized that it&amp;rsquo;s &lt;em&gt;very&lt;/em&gt; important to get everyone on the same page so that it&amp;rsquo;s easier to anticipate when certain features will &lt;em&gt;actually&lt;/em&gt; be shipped to production.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;positive-1--shaping-engineering-culture&#34;&gt;Positive 1 : Shaping Engineering Culture&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;What was great about working at an early stage startup was that I could directly contribute to the engineering culture. I was able to persuade my team to set up certain code style guides, start a Notion documentation page on critical frontend and backend services, and also write a detailed onboarding documentation for new hires. Yes, my team was in its &lt;em&gt;very early&lt;/em&gt; stages, so I was able to start these initiatives and actually have a say on &lt;em&gt;how&lt;/em&gt; our engineering team should function. I&amp;rsquo;m pretty sure that this would not have been possible at a more established company, so it was a pretty cool feeling to be able to have this kind of influence, to say the least.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;positive-2--experience-working-across-the-stack&#34;&gt;Positive 2 : Experience Working Across the Stack&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Angular, GraphQL API Endpoints, MySQL DB Modeling, Jenkins build - these are some of the things I worked on over the last 4 months. It almost sounds like an entire engineering team&amp;rsquo;s job, right? Frontend, backend, and even a sprinkle of devOps. The reality at a very early stage startup with 3 engineers is that they cannot afford to hire specialists for every stack - all the engineers are &lt;em&gt;generalists&lt;/em&gt; and are expected to fill in wherever they can. Hence, even as an intern, I had the opportunity to work across the entire stack, which in my opinion was a beneficial experience for me. Instead of being stuck with a small part of the frontend or the backend, I was able to draw out the frontend UI directly from Figma files that the designer gave me, implement the frontend logic, create necessary API endpoints, create a new MySQL database table if needed for a new feature, and then watch the Jenkins build succeed (or sometimes fail). I was able to see the bigger picture of how products are built and deployed, and I&amp;rsquo;m pretty sure it would not have been the case had I worked for a large company.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;positive-3--feel-like-a-core-part-of-the-team&#34;&gt;Positive 3 : Feel Like a Core Part of the Team&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Last but not the least, I would highly recommend people to try working for a startup because it&amp;rsquo;s such a great feeling to feel like a core part of the team. At a large company, people come and go, and &lt;em&gt;most&lt;/em&gt; people don&amp;rsquo;t feel like they are an absolutely integral part of the company. At an early stage startup with very few engineers, every single person on the team is an invaluable asset. Your opinion can have a direct impact on the product, and  the all the other teams count on the engineering team to make things happen - and you get to be a part of it. I found that to be an amazing feeling.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;whats-next-for-me&#34;&gt;What&amp;rsquo;s next for me?&lt;/h4&gt;&#xA;&lt;p&gt;Working at Fumi was definitely a great experience for me, and I certainly learned a &lt;em&gt;lot.&lt;/em&gt; Not only did I grow as an engineer, but I also learned a lot about how startups operate. What&amp;rsquo;s next for me? I am extremely lucky to be able to say that I will be working as an intern at Facebook HQ this coming summer. Here are some things I am looking forward to learning more about (very generic because I don&amp;rsquo;t know which team I&amp;rsquo;ll be working with yet):&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How are code reviews done at a large tech company?&lt;/li&gt;&#xA;&lt;li&gt;How do senior engineers at a company like Facebook write code differently?&lt;/li&gt;&#xA;&lt;li&gt;What kinds of internal tools do Facebook engineers use, and are they useful?&lt;/li&gt;&#xA;&lt;li&gt;Am I good enough to be held against the highest standards in the industry?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I&amp;rsquo;ll definitely write about my Facebook internship experience at the end of this summer, so stay tuned!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Working at an Early Stage Startup (Pt. 1)</title>
      <link>https://www.jeffyang.io/posts/fumi-internship-1/</link>
      <pubDate>Sun, 28 Feb 2021 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/fumi-internship-1/</guid>
      <category>Personal Reflection</category>
      <description>&lt;p&gt;&lt;em&gt;What I learned working at an early stage startup - the business side&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/fumi-internship-1/fumi_startup_hu_2d6b544df2229cfc.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/fumi-internship-1/fumi_startup_hu_cc9643ab1713758c.webp 640w, https://www.jeffyang.io/posts/fumi-internship-1/fumi_startup_hu_2d6b544df2229cfc.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;720&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;&lt;/em&gt;  &lt;em&gt;This post does not in any way represent the views of Fumi as a company, but is solely my individual opinion and reflection on my experience as a software engineer at an early-stage startup&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;COVID-19 brought an abrupt stop to my studies in the United States, leading me back to Seoul. As I was always interested in the startup scene, I could not turn down the opportunity to intern at an early stage startup when it presented itself. Following rounds of interviews, I landed an internship at &lt;a href=&#34;https://fumi.co.kr/main/home/home&#34;&gt;Fumi&lt;/a&gt;, a Korean e-commerce startup catered to the senior fashion market. Fumi had recently raised $3M in Series A funding and was rapidly growing its team. Over the past 4 months as a software engineering intern at Fumi, I was able to grow not only as a software engineer, but also as a future entrepreneur. In this blog post I would like to share my takeaways from working at an early stage startup as a software engineer.&lt;/p&gt;&#xA;&lt;h3 id=&#34;business-side-of-things&#34;&gt;Business Side of Things&lt;/h3&gt;&#xA;&lt;p&gt;One of the best things about working at Fumi was that I got to experience the business and engineering verticals of an early stage startup. More specifically, I worked with the product team, which was composed of 3 software engineers, 1 product designer, and the Chief Operations Officer (who also largely took on the role of a product manager). Most of my business-side engagements occurred during daily product team meetings where we shared our progress and shaped short &amp;amp; long term goals for the product we were building. It was particularly interesting to gain insight into the company&amp;rsquo;s finance / product metrics on a daily basis. Here are some of the insights I walked away with following my internship:&lt;/p&gt;&#xA;&lt;h4 id=&#34;competition-is-fierce-in-the-business-of-e-commerce&#34;&gt;Competition is Fierce in the Business of E-commerce&lt;/h4&gt;&#xA;&lt;p&gt;E-commerce is a highly competitive industry, especially in South Korea. There are extremely popular e-commerce platforms such as Coupang that already dominate the market (think Amazon). Coupang offers next-day delivery, one-click pay, and an expansive product lineup. In other words, as an e-commerce platform, it is very difficult to compete with such a massive and established entity. Even if we hone in on a more specific vertical like “fashion e-commerce,” the situation doesn&amp;rsquo;t get much better. This area is hyper-competitive in Korea because fashion is HUGE here (especially among the younger generations). Hence, there are apps like Musinsa, Ably, and StyleShare that are catered towards younger audiences and boast millions of active users. But what if we narrowed down the market even further - to senior fashion e-commerce, for example? All of a sudden there isn&amp;rsquo;t a clear market leader - and that is how Fumi came to be. The founder of Fumi saw significant market opportunity in the space of senior fashion e-commerce.&lt;/p&gt;&#xA;&lt;h4 id=&#34;market-for-seniors-is-big-but-difficult&#34;&gt;Market for Seniors is Big but Difficult&lt;/h4&gt;&#xA;&lt;p&gt;Fumi&amp;rsquo;s standout point in comparison to other e-commerce platforms was that we targeted senior customers. This is how Fumi was able to attract heavy-weight investors including Altos Ventures for their Series A funding. Senior consumers (aged 50 and over) tend to hold stronger purchasing power, which makes them a very attractive demographic to target. Needless to say, however, getting them to actively participate in online-shopping is a challenge, and it was the most important problem our team set out to solve. Our product needed design considerations such as larger fonts and UI components in order to accommodate senior customers who may suffer from deteriorating eye sight. We also found that many of our customers preferred to make phone calls to ask for assistance rather than using embedded online chatting solutions. Many of our customers also had a difficult time with making payments online, to which our team made significant efforts to build the payment experience as seamless as possible. As we can see, the market for seniors is a constantly growing market, but it is a difficult market to crack due to the need to properly understand the different characteristics of senior customers.&lt;/p&gt;&#xA;&lt;h4 id=&#34;finding-the-product-market-fit-is-hard&#34;&gt;Finding the Product Market Fit is Hard&lt;/h4&gt;&#xA;&lt;p&gt;The single most important factor for any startup to succeed - finding the product market fit. Can I confidently say that Fumi found a product market fit (PMF)? Not yet. In fact, I don&amp;rsquo;t think Fumi even came close to finding PMF during my 4 months there. Objectively speaking, Fumi is currently an e-commerce platform with an emphasis on senior fashion, with no next-day delivery and no brand-name recognition. However, we were able to see some explosive increase in active users when launching events such as the &amp;ldquo;Senior Model Contest.” The platform definitely grew significantly due to efforts on all fronts over the last 4 months, but it is still unable to demonstrate signs of sustainable &amp;amp; organic growth. There might have been some negativity in what I just said - but most startups that exist out there have also not found PMF, remember that :)&lt;/p&gt;&#xA;&lt;h4 id=&#34;back-office-application-is-important-for-e-commerce&#34;&gt;Back Office Application is Important for E-Commerce&lt;/h4&gt;&#xA;&lt;p&gt;The importance of a solid back office application for an e-commerce company is often overlooked. Obviously, as back office applications typically are not client facing, it often receives less attention from engineers (especially if the engineering team behind the product is also responsible for building the back office application). However, from my experience at Fumi, having a well thought-out back office application is extremely important for the business to function properly. A back office application is used to track inventory, respond to customer service needs, track delivery, and configure the customer-facing app to meet certain business needs. All of the above are a critical part of any e-commerce platform business. If the back office application is not properly set up, then there can be a lot of overhead for the developers, as all other teams (merchandising, marketing, customer relations, etc) constantly look to the developers to solve issues. For example, a customer relations manager may have the need to change user data on the database. If a back office application is not well set-up to perform such a task, they have no option but to ask the developers to do it for them. Over time, this can decrease the working efficiency of the software developers, as they constantly have to deal with external matters instead of being able to focus on the product itself. These kinds of issues only become more apparent as the business scales.&lt;/p&gt;&#xA;&lt;p&gt;What I learned from an engineering standpoint will be covered in part two.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Github Authentication with SSH</title>
      <link>https://www.jeffyang.io/posts/github-ssh-authentication/</link>
      <pubDate>Thu, 31 Dec 2020 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/github-ssh-authentication/</guid>
      <category>Developer Tools</category>
      <description>&lt;p&gt;&lt;em&gt;How to setup local machine to properly authenticate Github with SSH&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/github-ssh-authentication/github_ssh_auth_hu_ae1a045be433547a.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/github-ssh-authentication/github_ssh_auth_hu_23238794aa8f3359.webp 640w, https://www.jeffyang.io/posts/github-ssh-authentication/github_ssh_auth_hu_ae1a045be433547a.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;720&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;This blog post is meant to serve as a kind note for my future self, inevitably struggling to properly setup Github SSH authentication.&#xA;If there&amp;rsquo;s anyone else out there who happens to stumble upon this post while struggling with the same issue, hopefully this post will help you out.&#xA;I will go over &lt;strong&gt;what&lt;/strong&gt; SSH authentication is, &lt;strong&gt;why&lt;/strong&gt; you might be interested in setting one up yourself (in the context of Github), and finally, &lt;strong&gt;how&lt;/strong&gt; to set it up properly.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-is-ssh-&#34;&gt;What is SSH ?&lt;/h2&gt;&#xA;&lt;p&gt;SSH stands for &lt;strong&gt;S&lt;/strong&gt;ecure &lt;strong&gt;Sh&lt;/strong&gt;ell, and is a cryptographic network protocol for establishing trustworthy and secure network operations over an unsecured public network such as the internet.&#xA;Explaining the details of how exactly SSH works behind the scenes is not within the scope of this blog post, so I&amp;rsquo;ll keep this explanation at a high-level overview.&#xA;SSH Authentication is generally implemented via a classic client-server model, where the connection between the client and server is initiated by an SSH client making a connection request.&#xA;The server, upon receiving a connection request from a client, sends a public key over to the SSH client, starting a process well known as &lt;strong&gt;public key cryptography&lt;/strong&gt;&#xA;(if you are not familiar with it, and would like to learn more about it in depth, feel free to check &lt;a href=&#34;https://ssd.eff.org/en/module/deep-dive-end-end-encryption-how-do-public-key-encryption-systems-work&#34;&gt;this&lt;/a&gt; link out).&#xA;Once the SSH client and SSH server agree that each other can be trusted, the two establish a communication channel in which data is &lt;strong&gt;encrypted&lt;/strong&gt;, making it &lt;strong&gt;secure&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/github-ssh-authentication/client_server_model_hu_17e3d8be5061e576.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/github-ssh-authentication/client_server_model_hu_de80eb18e201be10.webp 640w, https://www.jeffyang.io/posts/github-ssh-authentication/client_server_model_hu_17e3d8be5061e576.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;571&#34; alt=&#34;Client-Server Model.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;Client-Server Model.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;h2 id=&#34;why-use-ssh-for-github-&#34;&gt;Why Use SSH for Github ?&lt;/h2&gt;&#xA;&lt;p&gt;There are two different ways to interact with remote github repositories (clone, pull, push, etc): &lt;strong&gt;HTTPS and SSH&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/github-ssh-authentication/github_ssh_https_screenshot_hu_1ccc90d01d4128e3.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/github-ssh-authentication/github_ssh_https_screenshot_hu_1ccc90d01d4128e3.webp 718w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;718&#34; height=&#34;400&#34; alt=&#34;Github SSH Screenshot.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;Github SSH Screenshot.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;HTTPS is a very popular option for a lot of Github users, because it is extremely simple. In fact, &lt;em&gt;Github also recommends&lt;/em&gt; HTTPS over SSH due to its port (443) being globally accessible,&#xA;even behind firewalls. If you opt for HTTPS, all you need is your Github account information (id / password). However, when interacting with remote repositories, &lt;strong&gt;you will be prompted to enter your Github credentials every time&lt;/strong&gt;&#xA;(yes, even if you cache your credentials somewhere, it is still being sent over the network for authentication).&#xA;This is where I see the comparative advantage of setting up SSH for authentication. Using HTTPS exposes your Github account credentials in some way, shape, or form.&#xA;If your account information is compromised, you may be locked out of all your repositories and your account itself. That can be a real disaster.&lt;/p&gt;&#xA;&lt;p&gt;On the other hand, if you opt for SSH, &lt;strong&gt;you do not have to enter your Github credentials at all&lt;/strong&gt;. It is more secure in the sense that you do not expose your Github account.&#xA;However, in order to set up SSH on your local machine, you do have to create a public/private key, add the private key to your ssh-agent, and copy the public key to your Github account.&#xA;This way, Github can verify that it is indeed &lt;em&gt;you&lt;/em&gt; that is trying to interact with Github. Let&amp;rsquo;s take a look at how to set all this up!&lt;/p&gt;&#xA;&lt;h2 id=&#34;how-to-set-up-ssh-for-github&#34;&gt;How to Set up SSH for Github&lt;/h2&gt;&#xA;&lt;h3 id=&#34;generate-a-key-pair&#34;&gt;Generate a Key Pair&lt;/h3&gt;&#xA;&lt;p&gt;The first step in the process is to generate a public-private key pair. Use the &lt;code&gt;ssh-keygen&lt;/code&gt; command to generate your keys.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ssh-keygen -t ed25519 -C &lt;span class=&#34;s2&#34;&gt;&amp;#34;youremailaddress@example.com&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will have been prompted for a location to store your SSH keys, and I&amp;rsquo;ll leave that up to you. As a note, the default location is &lt;code&gt;~/.ssh&lt;/code&gt;.&#xA;The &lt;code&gt;ssh-keygen&lt;/code&gt; command should also have prompted you to enter a passphrase. It&amp;rsquo;s up to you to add a passphrase or not, but for security purposes, I would highly recommend using one.&#xA;Now, check if your keys have been properly generated:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; ~/.ssh &lt;span class=&#34;c1&#34;&gt;# or wherever you specified your keys to be saved&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ls&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;id_ed25519      id_ed25519.pub&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now start the ssh-agent in the background:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;eval&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;ssh-agent -s&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now it&amp;rsquo;s time to add the config file for SSH:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ vim ~/.ssh/config&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# add the following in the config file and save&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Host *&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  AddKeysToAgent yes &lt;span class=&#34;c1&#34;&gt;# automatically load keys to agent&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  UseKeychain yes &lt;span class=&#34;c1&#34;&gt;# store passphrases in keychain&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  IdentityFile ~/.ssh/id_ed25519 &lt;span class=&#34;c1&#34;&gt;# or wherever you specified your keys to be saved&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, add your SSH private key to the ssh-agent and store your passphrase in the keychain.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ssh-add -K ~/.ssh/id_ed25519&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it! You now have successfully set up your local machine. But we still need to copy the public key that we&amp;rsquo;ve generated to Github.&#xA;Run the following command to copy the content of your public key file on your clipboard:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pbcopy &amp;lt; ~/.ssh/id_rsa.pub&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, head over to your &lt;code&gt;Github account &amp;gt; Settings &amp;gt; SSH and GPG keys&lt;/code&gt;. Click on &amp;ldquo;New SSH Key&amp;rdquo;.&#xA;Enter the title of your SSH key (try to be descriptive, just in case you add more SSH keys later on), and copy the public key content from your clipboard.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/github-ssh-authentication/github_add_ssh_key_hu_267546aab7f0f7ab.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/github-ssh-authentication/github_add_ssh_key_hu_5f65279881ddd958.webp 640w, https://www.jeffyang.io/posts/github-ssh-authentication/github_add_ssh_key_hu_267546aab7f0f7ab.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;717&#34; alt=&#34;Screenshot of Github Add SSH.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;Screenshot of Github Add SSH.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;You should see that your public key is now added to your Github account. You are all set now!&lt;/p&gt;&#xA;&lt;h3 id=&#34;ssh-port-connection&#34;&gt;SSH Port Connection&lt;/h3&gt;&#xA;&lt;p&gt;After following all the steps from above, everything should be working fine. But as with everything else, there&amp;rsquo;s a &lt;em&gt;caveat&lt;/em&gt;.&#xA;Your SSH connection will &lt;strong&gt;default to port 22&lt;/strong&gt;, which is the standard port number for SSH. However, unlike HTTPS, certain firewalls or networks&#xA;may be blocking that port, in which case your SSH connection will hang. Fortunately, there is a workaround. We can simply configure SSH to establish connection&#xA;on &lt;strong&gt;port 443&lt;/strong&gt;, which is the default port for HTTPS.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ vim ~/.ssh/config&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# we already added this part before&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Host *&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  AddKeysToAgent yes &lt;span class=&#34;c1&#34;&gt;# automatically load keys to agent&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  UseKeychain yes &lt;span class=&#34;c1&#34;&gt;# store passphrases in keychain&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  IdentityFile ~/.ssh/id_ed25519 &lt;span class=&#34;c1&#34;&gt;# or wherever you specified your keys to be saved&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# TODO : set github ssh connection to port 443&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Host github.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Hostname ssh.github.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Port &lt;span class=&#34;m&#34;&gt;443&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now your SSH setup with Github should really be working!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Setting up ESLint and Prettier with VS Code</title>
      <link>https://www.jeffyang.io/posts/eslint-prettier-vscode/</link>
      <pubDate>Sat, 08 Aug 2020 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/eslint-prettier-vscode/</guid>
      <category>Developer Tools</category>
      <description>&lt;p&gt;&lt;em&gt;Setting up ESLint and Prettier with VS Code&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/eslint-prettier-vscode/eslint_prettier_vscode_hu_50c45998e21469ca.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/eslint-prettier-vscode/eslint_prettier_vscode_hu_50c45998e21469ca.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;An important part of software development/engineering (or whatever else you may want to call it) is&#xA;writing code that works as intended, and is as bug-free as can be made. However, there&amp;rsquo;s actually a&#xA;lot more to writing code than just to &amp;ldquo;make it work.&amp;rdquo; This is primarily because almost any&#xA;meaningful project/software that exists in the world is created through collaboration. In other&#xA;words, it is important that you are able to easily read others&amp;rsquo; code, and that others are also able&#xA;to easily read and understand your code. There are lots of ways to achieve this, including manual&#xA;methods. A team of engineers can manually check for code style issues during code reviews, and&#xA;possibly even create a detailed documentation on the coding styles that every engineer on the team&#xA;should adhere to. However, with what we are about to cover in this blog post, investing engineers&#39;&#xA;time should no longer be put into reviewing code style. Without any further ado, let&amp;rsquo;s find out how&#xA;to set up ESLint and Prettier with Visual Studio Code (VS Code).&lt;/p&gt;&#xA;&lt;h2 id=&#34;linter-vs-code-formatter&#34;&gt;Linter vs. Code Formatter&lt;/h2&gt;&#xA;&lt;p&gt;Before we dive into the actual set up, let us briefly discuss what a linter and a code formatter is.&lt;/p&gt;&#xA;&lt;h3 id=&#34;what-is-a-linter&#34;&gt;What is a linter?&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Lint, or a linter, is a tool that analyzes source code to flag programming errors, bugs, stylistic&#xA;errors, and suspicious constructs. The term originates from a Unix utility that examined C&#xA;language source code.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Linter is a developer tool that flags potential errors and bugs, which in turn lets the programmer&#xA;easily spot potential bugs he/she may have accidentally introduced. For example, a linter can help&#xA;catch some of the most commmon bugs such as import errors, undeclared variable errors, while also&#xA;producing warnings where adequate; we all know about the tale of &lt;code&gt;const&lt;/code&gt; vs &lt;code&gt;let&lt;/code&gt; in JavaScript.&#xA;While there are many linters to choose from, the most popular JavaScript/TypeScript linter in the&#xA;market today is &lt;a href=&#34;https://eslint.org&#34;&gt;ESlint&lt;/a&gt;, so that&amp;rsquo;s what we will focus on in this blog post.&lt;/p&gt;&#xA;&lt;h3 id=&#34;what-is-a-code-formatter&#34;&gt;What is a code formatter?&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;A code formatter, unlike a linter, is only concerned with code style.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;A code formatter will not be able to catch errors or bug in your code; that is not what it was built&#xA;for. It is soley interested in beautifying your code according to configurable standards. There are&#xA;many different code formatters available, but we will focus on a code formatter called&#xA;&lt;a href=&#34;https://prettier.io/&#34;&gt;Prettier&lt;/a&gt;, which is a highly popular and opinionated formatter.&lt;/p&gt;&#xA;&lt;h2 id=&#34;setting-up-eslint-and-prettier-with-vs-code&#34;&gt;Setting up ESLint and Prettier with VS Code&lt;/h2&gt;&#xA;&lt;p&gt;A linter and a code formatter are made to be used with a code editor. There are many code editors&#xA;out there, the most popular ones being VS Code, Sublime Text, and Atom. VS Code, the most recent&#xA;addition to the list, was made by Microsoft and has gained popularity over the last couple of years&#xA;due to its rich collection of plugins that can all be configured directly within the application. In&#xA;this blog post, we will specifically see how ESLint and Prettier can be configured for use with VS&#xA;Code.&lt;/p&gt;&#xA;&lt;h3 id=&#34;understanding-the-difficulty&#34;&gt;Understanding the Difficulty&lt;/h3&gt;&#xA;&lt;p&gt;The main reason why people find it tricky to properly set up ESLint and Prettier together is because&#xA;ESLint does not strictly restrict itself within the boundaries of a linter; it also supports&#xA;formatting code. There are style-related rules that can be configured within ESLint that may&#xA;conflict with Prettier, which also wants to format your code. Hence, we need to make sure that we&#xA;get avoid any possible conflicts between the two. With this in mind, let&amp;rsquo;s continue and embark on&#xA;our journey of successfully configuring ESLint and Prettier to harmoniously work together.&lt;/p&gt;&#xA;&lt;h3 id=&#34;install-eslint-and-prettier&#34;&gt;Install ESLint and Prettier&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: For official documentation and detailed guides, see official website for&#xA;&lt;a href=&#34;https://eslint.org/docs/user-guide/getting-started&#34;&gt;ESLint&lt;/a&gt; and&#xA;&lt;a href=&#34;https://prettier.io/docs/en/integrating-with-linters.html&#34;&gt;Prettier&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;We first need to install ESLint using &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt;, whichever one you prefer. You can either&#xA;install ESLint globally, or locally. I personally use ESLint for all of my projects now, so I&#xA;installed ESLint globally. Even if installed globally, we will still have to set up specific&#xA;configuration files for each specific projects.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# install eslint globally&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install -g eslint&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we&amp;rsquo;ve installed ESLint globally, we need to install Prettier. As with ESLint, Prettier can&#xA;either be installed locally as a dependency, or can be installed globally. As I like to use Prettier&#xA;with all my projects, I will install it globally.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# install prettier globally&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install -g prettier&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it for basic installation! Now we have to configure ESLint and Prettier so that they can work&#xA;together harmoniously within our editor.&lt;/p&gt;&#xA;&lt;h3 id=&#34;install-eslint--prettier-plugin-for-vs-code&#34;&gt;Install ESLint &amp;amp; Prettier Plugin for VS Code&lt;/h3&gt;&#xA;&lt;p&gt;You will need to install the appropriate extension/plugin for your code editor. For VS Code, install&#xA;&lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint&#34;&gt;ESLint&lt;/a&gt; and&#xA;&lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode&#34;&gt;Prettier&lt;/a&gt;. These&#xA;plugins have millions of downloads each, so it&amp;rsquo;ll be difficult to miss them in the VS Code extension&#xA;marketplace. Once you&amp;rsquo;ve installed these, we are ready to move on to configuring each of them for&#xA;proper usage.&lt;/p&gt;&#xA;&lt;h3 id=&#34;configuring-eslint-and-prettier&#34;&gt;Configuring ESLint and Prettier&lt;/h3&gt;&#xA;&lt;p&gt;As I mentioned earlier, ESLint and Prettier will conflict with each other if not set up with care.&#xA;Fortunately, because this is such a widely recognized problem, there are packages that are made to&#xA;solve exactly this problem. We are going to install two different packages; &lt;code&gt;eslint-config-prettier&lt;/code&gt;&#xA;to exclude all ESLint rules that could conflict with Prettier, and &lt;code&gt;eslint-plugin-prettier&lt;/code&gt; to&#xA;integrate the Prettier rules into ESLint rules. This way, you won&amp;rsquo;t have two different rules that&#xA;conflict with each other.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install --save-dev eslint-config-prettier eslint-plugin-prettier&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Curious what the &lt;code&gt;--save-dev&lt;/code&gt; flag does? By intalling with &lt;code&gt;--save-dev&lt;/code&gt; flag, npm installs&#xA;the packages &lt;code&gt;eslint-config-prettier&lt;/code&gt; and &lt;code&gt;eslint-plugin-prettier&lt;/code&gt; as &lt;code&gt;devDependencies&lt;/code&gt;. This&#xA;differs from the default behavior of &lt;code&gt;npm install&lt;/code&gt; which installs it as a dependency to run the&#xA;application, and places the downloaded files in &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Now that we have that successfully installed those packages, we need to run some commands to&#xA;initialize ESLint within our project.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;eslint --init&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;✔ How would you like to use ESLint? · style&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;✔ What &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt; of modules does your project use? · esm&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;✔ Which framework does your project use? · none&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;✔ Does your project use TypeScript? · No / Yes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;✔ Where does your code run? · browser&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;✔ How would you like to define a style &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; your project? · guide&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;✔ Which style guide &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt; you want to follow? · airbnb&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;✔ What format &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt; you want your config file to be in? · JSON&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Checking peerDependencies of eslint-config-airbnb-base@latest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Local ESLint installation not found.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The config that you&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;ve selected requires the following dependencies:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;eslint-config-airbnb-base@latest eslint@^5.16.0 &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; ^6.8.0 &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; ^7.2.0 eslint-plugin-import@^2.21.2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;✔ Would you like to install them now with npm? · No / Yes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Installing eslint-config-airbnb-base@latest, eslint@^5.16.0 &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; ^6.8.0 &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; ^7.2.0, eslint-plugin-import@^2.21.2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm WARN eslint-plugin-prettier@3.1.4 requires a peer of prettier@&amp;gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.13.0 but none is installed. You must install peer dependencies yourself.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm WARN eslint-prettier-blogpost@1.0.0 No repository field.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+ eslint-plugin-import@2.22.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+ eslint-config-airbnb-base@14.2.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+ eslint@7.6.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;added &lt;span class=&#34;m&#34;&gt;171&lt;/span&gt; packages from &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt; contributors and audited &lt;span class=&#34;m&#34;&gt;176&lt;/span&gt; packages in 10.789s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;24&lt;/span&gt; packages are looking &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; funding&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  run &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;npm fund&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; details&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;found &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; vulnerabilities&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As illustrated above, when you run the command &lt;code&gt;eslint --init&lt;/code&gt;, you will be asked questions to&#xA;determine the configurations of the linter. You just need to answer those questions based on the&#xA;project you are working on. One thing to note here is the question &amp;ldquo;Which style guide do you want to&#xA;follow?&amp;rdquo; There are many different style guides out there, including the JavaScript standard style&#xA;guide, Airbnb style guide, and Google style guide. These are some very popular guides that a lot of&#xA;developers choose to use (I chose the Airbnb style guide, which seems to be the most popular style&#xA;guide out there). If you or your team have your own set of style guides, you don&amp;rsquo;t necessarily have&#xA;to select an existing one.&lt;/p&gt;&#xA;&lt;p&gt;Once the setup is successful, you should see a file &lt;code&gt;eslintrc.{js/json/yaml}&lt;/code&gt; based on the format&#xA;you chose your configuration file to be in. For Prettier to run with ESLint, your configuration file&#xA;should look something like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;env&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;browser&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;es2020&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;extends&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;airbnb-base&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;prettier&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;plugins&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;prettier&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;parserOptions&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;ecmaVersion&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;sourceType&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;module&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;rules&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;prettier/prettier&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can include files/directories that you want your linter to ignore in a file called&#xA;&lt;code&gt;.eslintignore&lt;/code&gt; (very similar concept to a &lt;code&gt;.gitignore&lt;/code&gt; file).&lt;/p&gt;&#xA;&lt;p&gt;Now, we just have to one final step. We need to set up Prettier locally, and tell Prettier how we&#xA;want it to format our code.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# install prettier as local devDependency&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install --save-dev prettier&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That should have installed Prettier as a local &lt;code&gt;devDependency&lt;/code&gt;. Now, we need to create a&#xA;configuration file for Prettier, and tell it how to format our code.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# in your project root directory&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;touch .prettierrc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Open &lt;code&gt;.prettierrc&lt;/code&gt; in your code editor, and set up a configuration for Prettier.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;semi&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;trailingComma&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;all&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;singleQuote&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;printWidth&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;70&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is simply a possible configuration, but feel free to take a look at other options&#xA;&lt;a href=&#34;https://prettier.io/docs/en/configuration.html&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;vs-code-configurations&#34;&gt;VS Code Configurations&lt;/h3&gt;&#xA;&lt;p&gt;So, you may still be asking yourself how we would actually use ESLint and Prettier. Will it work&#xA;automagically? One thing we need to understand is that the way we set up Prettier to run as an&#xA;ESLint rule. We have excluded all conflicting rules through &lt;code&gt;eslint-config-prettier&lt;/code&gt;, and integrated&#xA;Prettier as an ESLint rule through &lt;code&gt;eslint-plugin-prettier&lt;/code&gt;. As a result, we only need to run ESLint&#xA;to achieve the results we want for both linting and code formatting. ESLint can be run in mainly two&#xA;different ways. We can use the&#xA;&lt;a href=&#34;https://eslint.org/docs/user-guide/command-line-interface&#34;&gt;ESLint CLI&lt;/a&gt; to manually run the linter&#xA;(in the form of &lt;code&gt;eslint index.js&lt;/code&gt;), or we could set up our VS Code configurations in such a way that&#xA;will automatically format the code on save. To clarify, ESLint will check your code for possible&#xA;bugs and errors even if you don&amp;rsquo;t manually run it via the CLI, but will not format your code&#xA;automatically if VS Code configurations aren&amp;rsquo;t set up to do so. I like to have my code formatted&#xA;automatically on save, so I will tweak my VS Code configurations to do exactly that.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// VS Code Settings JSON&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Set the default&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;editor.formatOnSave&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Enable per-language&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;[javascript]&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;editor.formatOnSave&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this set up, you should now be able to see that your code is formatted automatically when you&#xA;save the file.&lt;/p&gt;&#xA;&lt;h2 id=&#34;bonus&#34;&gt;Bonus&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens&#34;&gt;Error Lens&lt;/a&gt; is a very&#xA;useful VS Code plugin that I love. By default, ESLint will underline error-prone code in red or&#xA;orange, depending on how you&amp;rsquo;ve set up certain rules for ESLint. However, you will need to hover&#xA;your mouse over the underlined code to see the error message.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/eslint-prettier-vscode/eslint_prettier_without_errorlens.gif&#34; width=&#34;748&#34; height=&#34;490&#34; alt=&#34;A GIF of ESLint and Prettier working in VS Code without Error Lens.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;A GIF of ESLint and Prettier working in VS Code without Error Lens.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;You should be able to see that ESLint and Prettier are properly working! The red underline shows&#xA;that a &lt;code&gt;const&lt;/code&gt; variable should not be reassigned, and the code is automatically formatted when I&#xA;save the file. However, notice that I had to hover over the red underline to actually see the error&#xA;messages that ESLint has produced for us. With Error Lens, the error messages are printed on the&#xA;screen, which in my opinion is very convenient. Let&amp;rsquo;s see that in action!&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/eslint-prettier-vscode/eslint_prettier_with_errorlens.gif&#34; width=&#34;748&#34; height=&#34;490&#34; alt=&#34;A GIF of ESLint and Prettier working in VS Code with Error Lens.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;A GIF of ESLint and Prettier working in VS Code with Error Lens.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;Voila! There we have it! ESLint and Prettier working beautifully with VS Code, and long gone are&#xA;your worries of having to scratch your head over how to create a codebase with uniform code style.&#xA;With Error Lens added to that combination, your day to day coding task should certainly be an&#xA;experience! I hope this blog post helped you get started with using ESLint and Prettier for your&#xA;JavaScript project, but please feel free to explore further by visiting their respective&#xA;documentation.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Resize Images Stored in AWS S3 with AWS Lambda (feat. Docker)</title>
      <link>https://www.jeffyang.io/posts/aws-s3-and-lambda/</link>
      <pubDate>Mon, 27 Apr 2020 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/aws-s3-and-lambda/</guid>
      <category>Web Development</category>
      <description>&lt;p&gt;&lt;em&gt;A blog post on how to resize images stored in AWS S3 with AWS Lambda&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/aws-s3-and-lambda/aws_lambda_s3_hu_f71a09a8a149af2c.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/aws-s3-and-lambda/aws_lambda_s3_hu_f71a09a8a149af2c.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Note: An AWS Account and a basic knowledge of their services are assumed throughout this blog post&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;I have been working on a &lt;a href=&#34;https://www.sublets.nu&#34;&gt;team project&lt;/a&gt; for the past couple of months. The idea is to build a platform for off-campus room search, specifically geared towards students of Northwestern University. Django doing the heavy-lifting as the main engine to power the application, we were able to develop such a platform quite rapidly on our local development environment. We soon migrated our work to Heroku for production testing, and started serving media files from AWS S3 buckets. While user testing in preparation for a full launch of the application, a few non-trivial issues were found. As a room searching platform, our site needs to handle image requests adequately. However, our site suffered from a significant performance issue due to slow loading speed. Photos taken with the iPhones or the Samsung Galaxies these days typically are well over 2 MB in file size. Considering the fact that multiple photos are associated with a single room post and that multiple room posts need to be loaded on a single page, such an enormous file size quickly becomes prohibitive. We needed a solution to scale down large image files, without compromising image quality too much. Luckily, AWS already had advanced cloud computing services to help us achieve our goals: &lt;em&gt;AWS Lambda&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-what-is-aws-s3--aws-lambda&#34;&gt;1. What is AWS S3 &amp;amp; AWS Lambda?&lt;/h2&gt;&#xA;&lt;p&gt;The Amazon Simple Storage Service (Amazon S3) is a classic among a myriad of cloud services Amazon Web Services provide. S3 buckets simply provide space in remote Amazon servers for customers to store assets (or &lt;em&gt;objects&lt;/em&gt;). It&amp;rsquo;s an attractive service because customers don&amp;rsquo;t have to worry about scalability, data availability, security, and performance themselves.&lt;/p&gt;&#xA;&lt;p&gt;AWS Lambda is the other state-of-the-art technology we opted to use to solve our problem. Lambda lets you run code without provisioning or managing servers (hence, &amp;ldquo;serverless&amp;rdquo;), and you only need to pay for the total amount of compute time your application actually uses. All you need to do is upload your code and Lambda takes care of everything required to run and scale your code with high availability. Lambda is often configured to listen to certain event triggers (such as an object &lt;em&gt;put&lt;/em&gt; event to an S3 bucket), which is exactly what we want to do. We want to have our Lambda function resize an image file, and store it back into an S3 bucket of our choice. Our goal is to significantly reduce loading time, by scaling images down to a more sensible size.&lt;/p&gt;&#xA;&lt;h2 id=&#34;2-create-an-execution-role&#34;&gt;2. Create an Execution Role&lt;/h2&gt;&#xA;&lt;p&gt;We are not going to go through creating the buckets themselves. Creating the buckets are a rather trivial task - consult the &lt;a href=&#34;https://docs.aws.amazon.com/AmazonS3/latest/user-guide/create-bucket.html&#34;&gt;documentation&lt;/a&gt; if necessary. Once the S3 buckets are set up, we will want to create a lambda function to attach to the buckets. Before we can create a lambda function, we need to create an execution role (AWSLambdaExecute) to grant explicit permission for our lambda function to manage objects in S3 buckets. Go ahead and create an execution role &lt;a href=&#34;https://console.aws.amazon.com/iam/home#/roles&#34;&gt;here&lt;/a&gt;, with the following properties:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Trusted entity – AWS Lambda&lt;/li&gt;&#xA;&lt;li&gt;Permissions – AWSLambdaExecute&lt;/li&gt;&#xA;&lt;li&gt;Role name - NameOfYourChoice&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Once you have an execution role set up following the guidelines above, we can head over to the AWS Lambda management console to create the function.&lt;/p&gt;&#xA;&lt;h2 id=&#34;3-create-the-lambda-function&#34;&gt;3. Create the Lambda Function&lt;/h2&gt;&#xA;&lt;p&gt;Creating a lambda function is relatively straightforward. All we need to do is to navigate to the AWS Lambda management console, click &amp;ldquo;create function&amp;rdquo; and give it the execution role that we already created above.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/aws-s3-and-lambda/create_lambda_hu_55d4b8f59780c43a.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/aws-s3-and-lambda/create_lambda_hu_55d4b8f59780c43a.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;350&#34; alt=&#34;A screenshot of AWS lambda create function page.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;A screenshot of AWS lambda create function page.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;Once the function is created, you will be able to see the control panel for the lambda function that you have just created. Your lambda function doesn&amp;rsquo;t do anything yet, because we didn&amp;rsquo;t write any code. There&amp;rsquo;s an option to write code directly in the inline editor that AWS provides for us, but that&amp;rsquo;s only possible if your lambda function does not have any external dependencies. Unfortunately, for image resizing, an external library must be installed for pretty much any language you might be working in (in Python, our code needs &lt;code&gt;Pillow&lt;/code&gt; to work properly). This is where it gets tricky. The lambda function executes in an amazonlinux environment, which most likely is not identical to your local environment. AWS provides a &lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/python-package.html&#34;&gt;detailed guideline&lt;/a&gt; on how to solve this problem - you have to upload your function as a deployment package. In other words, you need to package your function as a module (with all dependencies included) and ship it onto AWS Lambda so that it knows exactly what it&amp;rsquo;s working with. The following is the lambda function that grabs an image from the source bucket, resizes it, then puts the resized image in the destination bucket.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python:lambda_function.py&#34; data-lang=&#34;python:lambda_function.py&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# lambda_function.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;boto3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;io&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BytesIO&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;os&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;PIL&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Image&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;s3&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;boto3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resource&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;s3&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;INCOMING_BUCKET&lt;/span&gt;     &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;incoming-bucket-name&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;DESTINATION_BUCKET&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;destination-bucket-name&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;lambda_handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Records&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;object_key&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;s3&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;object&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;extension&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;splitext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lower&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Grabs the source file&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;bucket_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;INCOMING_BUCKET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;obj_body&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Body&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Checking the extension and&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Defining the buffer format&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;extension&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.jpeg&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;.jpg&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;.png&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nb&#34;&gt;format&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;JPEG&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Image resize&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;image&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Image&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BytesIO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj_body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;output_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;thumbnail&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;# image.thumbnail preserves aspect ratio, does does not exceed specified size, &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;buffer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BytesIO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;buffer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;seek&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Upload resized image to destination bucket&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;bucket_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DESTINATION_BUCKET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object_key&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;put&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Print to CloudWatch&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;File saved at &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;DESTINATION_BUCKET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;object_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Image resize lambda handler completed.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the function written above, we can try to follow &lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/python-package.html&#34;&gt;guidelines&lt;/a&gt; provided by Amazon to deploy our function as a package. Doing just that, I personally ran into issues with &lt;code&gt;Pillow&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ImportError: &lt;span class=&#34;s2&#34;&gt;&amp;#34;Unable to import module &amp;#39;lambda_function&amp;#39;: cannot import name &amp;#39;_imaging&amp;#39; from &amp;#39;PIL&amp;#39;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It is difficult to say whether or not you will run into the same issue. For me, it happened, and it didn&amp;rsquo;t go away. After &lt;em&gt;lots&lt;/em&gt; of googling, I came to a logical conclusion that it has to do with the amazonlinux environment that my lambda function is running in. The reason why I say a &lt;em&gt;logical conclusion&lt;/em&gt; is because I did not get any errors when trying to reproduce the same error in the Python virtual environment that I packaged my function in:&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/aws-s3-and-lambda/pillow_screenshot_hu_bb1e3fac80789e2b.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/aws-s3-and-lambda/pillow_screenshot_hu_bb1e3fac80789e2b.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;132&#34; alt=&#34;A screenshot of the terminal showing import errors resulting from Python&amp;#39;s Pillow module.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;A screenshot of the terminal showing import errors resulting from Python&amp;rsquo;s Pillow module.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;After gaining confidence in where the general problem lies in, I narrowed down the issue: the problem seemed to be that the module &lt;code&gt;PIL&lt;/code&gt; is already installed in the Python version that amazonlinux runs. &lt;code&gt;PIL&lt;/code&gt; is shipped with Python by default, which is also the case in my local environment, except I can do:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip uninstall PIL&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip install Pillow &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is in fact the recommended installation procedure outlined in the &lt;a href=&#34;https://pillow.readthedocs.io/en/latest/installation.html&#34;&gt;official documentation&lt;/a&gt; of &lt;code&gt;Pillow&lt;/code&gt;. However, this is not possible with AWS Lambda, because I have no direct ownership over the machine that the lambda function runs on. Of course, if you happen to be running the lambda function on an AWS EC2 instance that you have purchased, you are in luck. If not, don&amp;rsquo;t worry. &lt;em&gt;Docker to the rescue&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;4-docker-to-the-rescue&#34;&gt;4. Docker to the Rescue&lt;/h2&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/aws-s3-and-lambda/docker_hu_661320cb31b7283c.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/aws-s3-and-lambda/docker_hu_661320cb31b7283c.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;Docker&amp;#39;s logo.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;Docker&amp;rsquo;s logo.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Credits to &lt;a href=&#34;https://www.freecodecamp.org/news/escaping-lambda-function-hell-using-docker-40b187ec1e48/&#34;&gt;freecodecamp tutorial&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Docker technology is simply life-saving. I have no intensive background in working with Docker, but I was able to take a sneak peek at what Docker can offer to software developers. A simple introduction to Docker from their very own website: &lt;em&gt;&amp;ldquo;Containers are a standardized unit of software that allows developers to isolate their app from its environment, solving the “it works on my machine” headache. For millions of developers today, Docker is the de facto standard to build and share containerized apps - from desktop, to the cloud.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s get started by spinning up a Docker container. This Docker container will be able to see the lambda function code that you have written.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ brew cask install docker&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /your/path/to/aws_lambda_function&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ docker run -v &amp;lt;directory your code is in&amp;gt;:/aws_lambda -it --rm ubuntu&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The docker command above does the following:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The &lt;code&gt;-v flag&lt;/code&gt; makes your code directory available inside the container in a directory called “aws_lambda”.&lt;/li&gt;&#xA;&lt;li&gt;The &lt;code&gt;-it flag&lt;/code&gt; means you get to interact with this container.&lt;/li&gt;&#xA;&lt;li&gt;The &lt;code&gt;--rm flag&lt;/code&gt; means Docker will remove the container when you’re finished.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;ubuntu&lt;/code&gt; is the name of an official container, Ubuntu. If this container image isn’t already on your machine, Docker will download it for you.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;You should now see something like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@c1996f32a397:/#&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We now have to do a couple of &amp;ldquo;Ubuntu&amp;rdquo; things, before we can start baking the package to deploy on AWS Lambda.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ apt-get update&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ apt-get install python3-pip&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ apt-get install zip&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That just got us everything we need to start installing our dependencies within the Docker container. What we are about to do is fairly simple, and is documented in the AWS &lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/python-package.html&#34;&gt;documentation&lt;/a&gt;. The only difference is that we are doing it within a Docker container instead of your local machine.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; aws_lambda &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ls &lt;span class=&#34;c1&#34;&gt;# you should be able to see your function source code here &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lambda_function.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip3 install -t . Pillow &lt;span class=&#34;c1&#34;&gt;# install package specifically into current directory&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ zip lambda_function.zip lambda_function.py &lt;span class=&#34;c1&#34;&gt;# zip lambda_function.py &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ zip -r lambda_function.zip PIL &lt;span class=&#34;c1&#34;&gt;# zip downloaded package PIL &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ zip -r lambda_function.zip Pillow* &lt;span class=&#34;c1&#34;&gt;# zip downloaded packages Pillow-7.1.2.dist-info, Pillow.libs&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should now see &lt;code&gt;lambda_function.zip&lt;/code&gt; within your local directory that has your &lt;code&gt;lambda_function.py&lt;/code&gt; file. The container is automagically removed, because we set an &lt;code&gt;--rm&lt;/code&gt; flag when we ran the docker command. The &lt;code&gt;lambda_function.zip&lt;/code&gt; archive is ready to be uploaded to AWS!&lt;/p&gt;&#xA;&lt;h2 id=&#34;5-deploy-your-package-on-aws&#34;&gt;5. Deploy Your Package on AWS&lt;/h2&gt;&#xA;&lt;p&gt;The one final step remaining is to upload your zip file onto AWS. If your zip file is under 10 MB in size, you can upload it directly from the AWS Lambda console. If not, it is recommended that you upload it to an S3 bucket first, then pull in the S3 bucket URL from AWS Lambda console. Once your package is deployed, you should be able to see your lambda function at work whenever the event you set is triggered.&lt;/p&gt;&#xA;&lt;p&gt;There&amp;rsquo;s just one last thing that I want to point out. In this blog post, we have been working with two different S3 buckets: grab from the source and put in the destination. I want to note that it&amp;rsquo;s possible to work with just one bucket. There&amp;rsquo;s a &lt;em&gt;caveat&lt;/em&gt; there, however. Carefully think through what will happen if we configure our lambda function to trigger upon a &lt;em&gt;put&lt;/em&gt; event at the S3 bucket. Yes, an &lt;em&gt;infinte loop&lt;/em&gt;. Why? The initial &lt;em&gt;put&lt;/em&gt; event will trigger the lambda function, which will resize the image, and put it back in the same bucket. Then, the lambda function will be triggered &lt;em&gt;again&lt;/em&gt;. It will not stop. Amazon will happily serve your lambda function by scaling the resources needed for your lambda function to keep on running an infinite loop. But you certainly won&amp;rsquo;t be happy with that, or at least your wallet won&amp;rsquo;t be. To prevent such a disaster from happening, you must configure a &lt;em&gt;prefix&lt;/em&gt; for your lambda function trigger. Instead of listening to &lt;em&gt;all&lt;/em&gt; &lt;em&gt;put&lt;/em&gt; events, it will now only listen for a specific &lt;em&gt;put&lt;/em&gt; event that matches a certain prefix. Of course, you will also have to make sure that the lambda function uploads to a path that does not start with the prefix you have set.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Phewww&lt;/em&gt;. That was a lot! If you&amp;rsquo;ve followed along, you now should have a functional lambda function resizing images for you behind the scenes. To recap, here are the steps we took:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Create S3 bucket(s)&lt;/li&gt;&#xA;&lt;li&gt;Create an execution role for AWS Lambda&lt;/li&gt;&#xA;&lt;li&gt;Create an AWS Lambda function&lt;/li&gt;&#xA;&lt;li&gt;Write code for your function&lt;/li&gt;&#xA;&lt;li&gt;Package your function using Docker&lt;/li&gt;&#xA;&lt;li&gt;Upload the packaged function on AWS&lt;/li&gt;&#xA;&lt;li&gt;Enjoy!&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Again, Docker really saved my life.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Django Channels 2.0, Postgres, Daphne, and Heroku</title>
      <link>https://www.jeffyang.io/posts/deploy-django-on-heroku/</link>
      <pubDate>Mon, 20 Apr 2020 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/deploy-django-on-heroku/</guid>
      <category>Web Development</category>
      <description>&lt;p&gt;&lt;em&gt;A blog post on how to deploy a Django-ASGI application on heroku&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/deploy-django-on-heroku/heroku_django_hu_5a34c2099cb40f4a.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/deploy-django-on-heroku/heroku_django_hu_5a34c2099cb40f4a.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Disclaimer: This blog post is largely a quarantine coding production.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;I already have a blog post on how to deploy a very simple Flask application on Heroku. In this blog post, I will explore more details of Heroku deployment, but this time with Django. I have been working on a Django &amp;amp; Channels side project, a web platform to enhance off-campus room searching experience for Northwestern students. As the application development was almost over and user testing seemed like the logical next step to take, I turned my eyes to Heroku (largely for their known simplicity compared to other services such as AWS or Linode). It turned out that there were a few curveballs thrown at me, largely due to Channels using an ASGI server rather than a traditional Python WSGI server. I hope this blog post serves as a future reference for myself, and for a few other friends out there that may be facing similar problems that I did.&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-what-is-heroku&#34;&gt;1. What is Heroku?&lt;/h2&gt;&#xA;&lt;p&gt;Heroku is an extremely popular Platform as a Service (PaaS). Heroku is very attractive for developers who need to deploy something quickly, without having to worry about the complications of building server-side infrastructure. Heroku has a free-tier, but only provides very limited resources. For small personal projects, the free-tier should get the job done, but for bigger projects, you may want to consider upgrading your plan to handle more traffic.&lt;/p&gt;&#xA;&lt;h2 id=&#34;2-background&#34;&gt;2. Background&lt;/h2&gt;&#xA;&lt;p&gt;In this post, I will be writing specifically about how to deploy a Django Channels application on Heroku. For those of you who are unfamiliar with Django Channels, here&amp;rsquo;s a brief introduction: simply put, Channels gracefully extends Django&amp;rsquo;s boundaries beyond HTTP - to handle WebSockets, chat protocols, IoT protocols, and more. It’s built on a Python specification called ASGI (Asynchronous Server Gateway Interface), which makes the deployment process slightly different from a Python WSGI application. This post will not be a Channels tutorial. If you are interested in learning more about Channels, take a look at their official &lt;a href=&#34;https://channels.readthedocs.io/en/latest/index.html&#34;&gt;documentation&lt;/a&gt;. In moving forward, I will assume that you have a basic understanding of Python and Git version control, that you already have a working Django Channels application, and that you already have files such as &lt;code&gt;routing.py&lt;/code&gt; and &lt;code&gt;consumers.py&lt;/code&gt; already setup. This blog post will deal with deployment related content only.&lt;/p&gt;&#xA;&lt;h2 id=&#34;3-configuring-django-settings&#34;&gt;3. Configuring Django Settings&lt;/h2&gt;&#xA;&lt;p&gt;Environment variables play a big role in the separation of development and production environments. Rather than hard-coding environment variables (which is bad practice in general), setting separate environment variables in the local machine and Heroku deployment server makes life a lot easier for us developers. For macOS users, environment variables can be set in &lt;code&gt;.zshenv&lt;/code&gt; or &lt;code&gt;.bash_profile&lt;/code&gt; depending on your macOS version. The way I achieved a separation of settings for development and production environment was by conditionally setting production environment variables in a conditional where &lt;code&gt;DEBUG&lt;/code&gt; is &lt;code&gt;False&lt;/code&gt;. For this to work, the &lt;code&gt;DEBUG&lt;/code&gt; value must be set to &lt;code&gt;True&lt;/code&gt; in local settings, and to &lt;code&gt;False&lt;/code&gt; in production. I used an environment variable named &lt;code&gt;DEBUG_VALUE&lt;/code&gt; to dynamically assign the appropriate &lt;code&gt;DEBUG&lt;/code&gt; value. You will also have to add an environment variable &lt;code&gt;SECRET_KEY&lt;/code&gt;, which should not be hard-coded for production. We will deal with setting Heroku environment variables later. I also used &lt;code&gt;django_heroku&lt;/code&gt;, which is an awesome package that automatically configures your Django application to work on Heroku. The use of &lt;code&gt;django_heroku&lt;/code&gt; is in fact endorsed by Heroku, as it provides many niceties, including the reading of &lt;code&gt;DATABASE_URL&lt;/code&gt;, logging configuration, a Heroku CI–compatible TestRunner, and an automatic configuration of ‘staticfiles’ to “just work” (according to Heroku).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip install django_heroku&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because a Django Channels application requires an ASGI server, we are going to need something different than a standard WSGI server such as Gunicorn. The Channels project maintains an official ASGI HTTP/WebSocket server, Daphne, which I will be using in my project. However, there are alternatives such as &lt;a href=&#34;https://www.uvicorn.org/&#34;&gt;uvicorn&lt;/a&gt;, which is also known to be stable. Because of this extra layer of complication, there are a few different ways one can go about configuring appropriate web servers depending on needs. For example, you may choose to keep running standard HTTP requests through a WSGI server and use Daphne only for things WSGI cannot do, like HTTP long-polling and WebSockets. You may also opt to utilize Daphne for all HTTP and WebSocket traffic, in which case Daphne auto-negotiates between HTTP and Websocket. For my own project, I decided to use Daphne as the only server to handle all requests.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip install daphne&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;settingspy&#34;&gt;settings.py&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;SECRET_KEY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;SECRET_KEY&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;DEBUG&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;DEBUG_VALUE&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;True&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ALLOWED_HOSTS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;yourappname.herokuapp.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ASGI_APPLICATION&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;yourappname.routing.application&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# you need channels_layers configured as follows, and channels_redis installed &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;CHANNEL_LAYERS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s1&#34;&gt;&amp;#39;BACKEND&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;channels_redis.core.RedisChannelLayer&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s1&#34;&gt;&amp;#39;CONFIG&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;hosts&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;REDIS_URL&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;redis://localhost:6379&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)],&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Add the following at the end of settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Settings below are used for deployment&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Note that AWS settings are not required, but was needed for my application&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DEBUG&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;django_heroku&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;AWS_STORAGE_BUCKET_NAME&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;AWS_STORAGE_BUCKET_NAME&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;AWS_ACCESS_KEY_ID&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;AWS_ACCESS_KEY_ID&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;AWS_SECRET_ACCESS_KEY&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;AWS_S3_FILE_OVERWRITE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;DEFAULT_FILE_STORAGE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;storages.backends.s3boto3.S3Boto3Storage&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;S3_URL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;http://&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AWS_STORAGE_BUCKET_NAME&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.s3.amazonaws.com/&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;STATIC_ROOT&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BASE_DIR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;staticfiles&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;django_heroku&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;settings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;locals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;asgipy&#34;&gt;asgi.py&lt;/h3&gt;&#xA;&lt;p&gt;Your &lt;code&gt;asgi.py&lt;/code&gt; file should be located in the same directory as your &lt;code&gt;wsgi.py&lt;/code&gt; file.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ touch asgi.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br/&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# asgi.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;ASGI entrypoint. Configures Django and then runs the application&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;defined in the ASGI_APPLICATION setting.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;os&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;django&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;channels.routing&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_default_application&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setdefault&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;DJANGO_SETTINGS_MODULE&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;yourappname.settings&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;django&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;application&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_default_application&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We are ready to move on to configuring Heroku now!&lt;/p&gt;&#xA;&lt;h2 id=&#34;4-configuring-heroku&#34;&gt;4. Configuring Heroku&lt;/h2&gt;&#xA;&lt;p&gt;First, you will need to download the Heroku CLI. For more information, please take a look &lt;a href=&#34;https://devcenter.heroku.com/articles/heroku-cli#download-and-install&#34;&gt;here&lt;/a&gt;. During this process, you should have been prompted to create a Heroku account (please make sure you do, you&amp;rsquo;ll need that!) This first step should have been relatively simple and straightforward. Once you download the Heroku CLI, you will have to login.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ brew tap heroku/brew &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew install heroku &lt;span class=&#34;c1&#34;&gt;# install Heroku CLI for macOS&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...installation &lt;span class=&#34;nb&#34;&gt;complete&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku login&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;heroku: Press any key to open up the browser to login or q to exit: &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;press any key!&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Logging in... &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Logged in as yourname@someemail.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git init &lt;span class=&#34;c1&#34;&gt;# you will want to add a .gitignore file before you add files to commit!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git add -A&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git commit -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;heroku deployment preparation&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku create name_your_own_app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku open &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;... you should be seeing a Heroku default screen on your browser&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git push heroku &amp;lt;your_local_branch_name&amp;gt;:master &lt;span class=&#34;c1&#34;&gt;# if you exclude your local branch name, it&amp;#39;ll default to master&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enumerating objects: 9, &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Counting objects: 100% &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;9/9&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Delta compression using up to &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; threads&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Compressing objects: 100% &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;5/5&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Writing objects: 100% &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;5/5&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, &lt;span class=&#34;m&#34;&gt;434&lt;/span&gt; bytes &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 434.00 KiB/s, &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Total &lt;span class=&#34;m&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;delta 4&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, reused &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;delta 0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: Compressing &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; files... &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: Building source:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: -----&amp;gt; Python app detected&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: -----&amp;gt; Installing SQLite3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: Sqlite3 successfully installed.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: -----&amp;gt; Installing requirements with pip&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;... push failed&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It seems like our push to &lt;code&gt;heroku master&lt;/code&gt; just failed. We just need to configure a couple more files to get our application up and running on Heroku. Now let&amp;rsquo;s take a look at what other files we need to successfully deploy our Django application.&lt;/p&gt;&#xA;&lt;h3 id=&#34;procfile&#34;&gt;Procfile&lt;/h3&gt;&#xA;&lt;p&gt;To deploy any application on Heroku, you will need to add a &lt;code&gt;Procfile&lt;/code&gt; (without &amp;lsquo;.txt&amp;rsquo;, &amp;lsquo;.py&amp;rsquo; or any extensions) specifying a web server and your application name. Without the &lt;code&gt;Procfile&lt;/code&gt;, Heroku will not be able to run a web server, which is needed to serve your application.&lt;br&gt;&lt;/br&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;c1&#34;&gt;# this file should be in your project&amp;#39;s root directory &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ vim Procfile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; web: daphne &amp;lt;your_application_name&amp;gt;.asgi:application --port &lt;span class=&#34;nv&#34;&gt;$PORT&lt;/span&gt; --bind 0.0.0.0 -v2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;runtimetxt&#34;&gt;Runtime.txt&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;runtime.txt&lt;/code&gt; is not absolutely necessary, but is highly recommended if you are looking to specify the Python version you want your application to run on Heroku&amp;rsquo;s servers.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;c1&#34;&gt;# this file should be in your project&amp;#39;s root directory &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ vim runtime.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; python-3.8.2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;I would highly recommend that you configure your &lt;code&gt;runtime.txt&lt;/code&gt; with a Python version of 3.8.1 or higher. I initially deployed my application with Python version 3.7.4, which had a strange problem with opening a new database connection for each request, which crashed my server (my plan only allows for a maximum of 20 database connections). I spent hours searching through Google, which did not get me anywhere. Upgrading the Python version fixed the issue for me.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;requirementstxt&#34;&gt;Requirements.txt&lt;/h3&gt;&#xA;&lt;p&gt;Having a &lt;code&gt;requirements.txt&lt;/code&gt; is necessary for Heroku to automatically setup their dynos (their Linux machines) for you. Your &lt;code&gt;requirements.txt&lt;/code&gt; must have any and all dependencies that your application needs to run properly. It is important that you remember to update your &lt;code&gt;requirements.txt&lt;/code&gt; file everytime you add an external dependency to your project.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;c1&#34;&gt;# this file should be in your project&amp;#39;s root directory &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip freeze &amp;gt; requirements.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;addons&#34;&gt;Addons&lt;/h3&gt;&#xA;&lt;p&gt;You may have been using an SQLite database for development on your local machine. For production, however, using an SQLite database is not recommended. I decided to use Postgres for my application, which is a popular choice. Django Channels also requires a Redis instance to act as the channel layer, so we&amp;rsquo;ll also need to add &lt;code&gt;heroku-redis&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku addons:create heroku-postgresql&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku addons:create heroku-redis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;environment-variables&#34;&gt;Environment Variables&lt;/h3&gt;&#xA;&lt;p&gt;Setting environment variables for Heroku is very simple. They support configuration through both the Heroku CLI and their website dashboard. I configured mine through the CLI.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku config:set &lt;span class=&#34;nv&#34;&gt;DEBUG_VALUE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;False&amp;#34;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;# yes, &amp;#34;False&amp;#34; as a string!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku config:set &lt;span class=&#34;nv&#34;&gt;SECRET_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;your_secret_key_in_strings&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;useful-heroku-cli-commands&#34;&gt;Useful Heroku CLI Commands&lt;/h3&gt;&#xA;&lt;p&gt;Here are a couple very useful Heroku CLI commands.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku login &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku restart &lt;span class=&#34;c1&#34;&gt;# restarts the Heroku dyno&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku create &amp;lt;your_app_name&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku open &lt;span class=&#34;c1&#34;&gt;# opens up your heroku app&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku open -a &amp;lt;your_app_name&amp;gt; &lt;span class=&#34;c1&#34;&gt;# when you are working outside your git repo &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku logs &lt;span class=&#34;c1&#34;&gt;# opens up server logs, very useful for debugging&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku addons:create &amp;lt;package_name&amp;gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku pg:info &lt;span class=&#34;c1&#34;&gt;# postgres database info&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku run bash &lt;span class=&#34;c1&#34;&gt;# opens a bash shell to Heroku machine&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku run &amp;lt;your command&amp;gt; &lt;span class=&#34;c1&#34;&gt;# e.g. heroku run python manage.py migrate&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku certs:auto:enable &lt;span class=&#34;c1&#34;&gt;# allow heroku to automatically manage ssl cert&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku ps:scale &lt;span class=&#34;nv&#34;&gt;web&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1:&amp;lt;tier_name&amp;gt; &lt;span class=&#34;c1&#34;&gt;# scale web dyno&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;5-deploy-your-application&#34;&gt;5. Deploy Your Application&lt;/h2&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s try it again now!&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git add -A&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git commit -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;deploying to heroku&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git push heroku &amp;lt;your_branch_name&amp;gt;:master&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enumerating objects: 9, &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Counting objects: 100% &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;9/9&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Delta compression using up to &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; threads&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Compressing objects: 100% &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;5/5&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Writing objects: 100% &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;5/5&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, &lt;span class=&#34;m&#34;&gt;434&lt;/span&gt; bytes &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 434.00 KiB/s, &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Total &lt;span class=&#34;m&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;delta 4&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, reused &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;delta 0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: Compressing &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; files... &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: Building source:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: -----&amp;gt; Python app detected&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: -----&amp;gt; Installing SQLite3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: Sqlite3 successfully installed.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: -----&amp;gt; Installing requirements with pip&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: -----&amp;gt; $ python nu_housing_app/manage.py collectstatic --noinput&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote:        &lt;span class=&#34;m&#34;&gt;168&lt;/span&gt; static files copied to &lt;span class=&#34;s1&#34;&gt;&amp;#39;/tmp/build_d5d77a4a08320db00239af29729591fe/nu_housing_app/staticfiles&amp;#39;&lt;/span&gt;, &lt;span class=&#34;m&#34;&gt;520&lt;/span&gt; post-processed.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: -----&amp;gt; Discovering process types&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote:        Procfile declares types -&amp;gt; web&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: -----&amp;gt; Compressing...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote:        Done: 86.2M&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: -----&amp;gt; Launching...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote:        Released v10&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote:        https://yourappname.herokuapp.com/ deployed to Heroku&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote: Verifying deploy... &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Your logs may look different from mine. As long as it doesn&amp;rsquo;t error, there shouldn&amp;rsquo;t be a problem.&lt;/p&gt;&#xA;&lt;p&gt;Seems like our application was successfully deployed! We just have one final step left; we need to initialize our database.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku run bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Running bash on ⬢ your-app-name... ⣷ connecting, run.3028 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Hobby&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# inside the directory that has manage.py file, run the following&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~ $ python manage.py makemigrations&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~ $ python manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;... you should see your database initializing ... and we are &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Congratulations! We have successfully deployed our Django Channels application on Heroku. If you run into any problems, Heroku has done a great job in documenting how to deploy your application on Heroku, so check it out &lt;a href=&#34;https://devcenter.heroku.com/articles/getting-started-with-python&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>What is a Graph?</title>
      <link>https://www.jeffyang.io/posts/what-is-a-graph/</link>
      <pubDate>Sat, 28 Dec 2019 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/what-is-a-graph/</guid>
      <category>Algorithms</category>
      <description>&lt;p&gt;&lt;em&gt;A blog post on the graph data structure in computer science&lt;/em&gt;&lt;/p&gt;&lt;p&gt;In this blog post, I will be going over the basics of the graph data structure, including its representation, implementation, and basic graph traversal algorithms. Graph is an important data structure, especially because of its practical applications that can range from representing friendships to maps of locational intelligence, computer networks and much more. Does that sound interesting? Let&amp;rsquo;s dive into learning the basics of graphs.&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-introduction-to-graphs&#34;&gt;1. Introduction to Graphs&lt;/h2&gt;&#xA;&lt;p&gt;Even if you are unfamiliar with graphs, you most likely have already seen one. If you have seen or worked with &lt;em&gt;trees&lt;/em&gt; before, you have already encountered a &lt;em&gt;graph&lt;/em&gt; before. Let me try to explain graphs in relation to trees, a data structure that you are more likely to be familiar with, or at least more so than graphs. &lt;strong&gt;All trees are graphs, but not all graphs are trees&lt;/strong&gt;. Like trees, graphs also have &lt;strong&gt;nodes (or vertices)&lt;/strong&gt; and &lt;strong&gt;edges&lt;/strong&gt; that connect the nodes, but are not bound to the restrictions that trees have. A tree is an &lt;em&gt;acyclic&lt;/em&gt; and &lt;em&gt;connected&lt;/em&gt; graph. A graph, however, is not limited to such restrictions; a graph can have &lt;em&gt;cycles&lt;/em&gt; and can be &lt;em&gt;disconnected&lt;/em&gt; to form a forest consisting of multiple components that are disconnected with another. Take a look at the following to get a rough idea of what a &lt;em&gt;directed&lt;/em&gt;, &lt;em&gt;acyclic&lt;/em&gt; graph looks like.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/what-is-a-graph/sample_graph_hu_18d2123160e9a309.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/what-is-a-graph/sample_graph_hu_18d2123160e9a309.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;A more mathematically oriented definition of a graph would be the following: a &lt;strong&gt;graph&lt;/strong&gt; is a data structure that represents a &lt;strong&gt;finite set of nodes (or vertices) and set of edges which connect a pair of vertices&lt;/strong&gt;. Confused about the technical terms (&lt;em&gt;connected&lt;/em&gt;, &lt;em&gt;directed&lt;/em&gt;, &lt;em&gt;acyclic&lt;/em&gt;, &lt;em&gt;cyclic&lt;/em&gt;, and more&amp;hellip;) that were just thrown at you? Don&amp;rsquo;t worry, that&amp;rsquo;s what we&amp;rsquo;ll explore in the next section.&lt;/p&gt;&#xA;&lt;h2 id=&#34;2-categorization-of-graphs&#34;&gt;2. Categorization of Graphs&lt;/h2&gt;&#xA;&lt;p&gt;For some of you, terms such as &lt;em&gt;acyclic&lt;/em&gt;, &lt;em&gt;cyclic&lt;/em&gt;, &lt;em&gt;directed&lt;/em&gt;, &lt;em&gt;undirected&lt;/em&gt;, &lt;em&gt;connected&lt;/em&gt;, &lt;em&gt;disconnected&lt;/em&gt; may sound unfamiliar. Such categorization of graphs, however, are essential in deepening your understanding of graphs, and are not too difficult to get a grasp of either. To help your understanding, take a look at the following:&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/what-is-a-graph/graph_ds_hu_90188566ef73652e.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/what-is-a-graph/graph_ds_hu_90188566ef73652e.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;A diagram representing different kinds of graphs.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;A diagram representing different kinds of graphs.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;As clearly depicted (on purpose) above, there are many different ways to categorize a graph. Here are some of the most important categorizations of a graph to be aware of:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;A &lt;strong&gt;connected&lt;/strong&gt; graph has at least one vertex and there must be a path between every pair of vertices (i.e. every vertex must be reachable by following only the edges). Otherwise, we call it a &lt;strong&gt;disconnected&lt;/strong&gt; graph. (We won&amp;rsquo;t go into the details of &lt;em&gt;strongly&lt;/em&gt; connected and &lt;em&gt;weakly&lt;/em&gt; connected graphs)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;A &lt;strong&gt;directed&lt;/strong&gt; graph&amp;rsquo;s edges have directions (often represented by arrows) associated to them. Otherwise, we call it an &lt;strong&gt;undirected&lt;/strong&gt; graph.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;A &lt;strong&gt;weighted&lt;/strong&gt; graph&amp;rsquo;s edges have associated weights or values. Otherwise, we call it an &lt;strong&gt;unweighted&lt;/strong&gt; graph. (Weights are often used to denote a &lt;strong&gt;cost&lt;/strong&gt; or &lt;strong&gt;distance&lt;/strong&gt; associated with two vertices. Depending on what we are trying to represent with the graph, the weights can carry different meanings. For instance, if a graph represents a map, a weight between two vertices may denote the distance between two locations (which are represented as two distinct vertices) on the map)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;A &lt;strong&gt;cyclic&lt;/strong&gt; graph contains at least one graph cycle. Otherwise, we call it an &lt;strong&gt;acylic&lt;/strong&gt; graph.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;It is important to have a very clear idea on the basic categorization of graphs based on certain characteristics (as listed above). As much as I would like to provide more detailed analysis on graph and its related theory, it is impossible to cover the entirety of graph theory in this blog post (nor do I have the knowledge to do so). If interested for more information on graphs (or graph theory), I found &lt;a href=&#34;https://medium.com/basecs/a-gentle-introduction-to-graph-theory-77969829ead8&#34;&gt;this&lt;/a&gt; article quite helpful.&lt;/p&gt;&#xA;&lt;h2 id=&#34;3-representation-of-graphs&#34;&gt;3. Representation of Graphs&lt;/h2&gt;&#xA;&lt;p&gt;To dive deeper into the discussion of graphs, we need to understand how graphs are represented without having to visually draw a graph out. There are largely two different ways to represent any graph: as an &lt;em&gt;adjacency list&lt;/em&gt; or as an &lt;em&gt;adjacency matrix&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Let us first take a look at the &lt;strong&gt;adjacency list representation&lt;/strong&gt; of a graph. A common way of representing a graph as an adjacency list is to directly map vertices to indices of an array, and at each index of an array, represent the neighboring vertices and weights in the form of linked lists. Take a look at the following example to help your understanding:&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/what-is-a-graph/adj_list_hu_d07a5145ad4faebe.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/what-is-a-graph/adj_list_hu_d07a5145ad4faebe.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;A diagram that explains representing graphs through an adjacency list.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;A diagram that explains representing graphs through an adjacency list.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;The diagram above shows how an undirected, weighted graph can be represented as an adjacency list. Note that using a linked list is not the only possible way to implement an adjacency list (we could use hash tables instead, and use vertices as keys and associated weights as values).&lt;/p&gt;&#xA;&lt;p&gt;Now let us examine the &lt;strong&gt;adjacency matrix representation&lt;/strong&gt; of a graph. An adjacency matrix representation differs from an adjacency list in that an adjacency matrix requires a |V| × |V| matrix (where |V| denotes the number of vertices) to store information on the existence of an edge between two vertices (if the graph is unweighted) or the weight between two vertices (if the graph is weighted). A non-existant edge is usually represented by a 0 for an unweighted graph, and by &amp;ldquo;inf&amp;rdquo; (infinity) for a weighted graph. To visualize the explanation, take a look at the following adjacent matrix representation of an undirected, weighted graph:&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/what-is-a-graph/adj_mat_hu_fad833b7aa64bcc8.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/what-is-a-graph/adj_mat_hu_fad833b7aa64bcc8.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;A diagram that explains representing graphs through an adjacency matrix.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;A diagram that explains representing graphs through an adjacency matrix.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;The diagram above should give a clear idea of how to represent a graph with an adjacency matrix. If we were to represent an unweighted graph, the &amp;ldquo;inf&amp;quot;s would be replaced with 0s, and all the weights (represented in red numbers) would be replaced with 1s. Now, one might ask &amp;ldquo;Which representation is better?&amp;rdquo; Well, it &lt;em&gt;depends&lt;/em&gt;. If optimizing for speed, an adjacency matrix representation will guarantee an O(1) lookup time when searching for an edge between two vertices. On the other hand, an adjacency list implemented with a linked list would give O(n) time, since in the worst case, we would have to search through the entire adjacency list of vertex &lt;em&gt;a&lt;/em&gt; to find that there exists an edge between, say, &lt;em&gt;a&lt;/em&gt; and &lt;em&gt;f&lt;/em&gt;. If we were to optimize for space, however, an adjacency list would yield a significant advantage especially when representing a &lt;em&gt;sparse&lt;/em&gt; graph. An adjacency list will only store information in a linked list if there &lt;em&gt;exists&lt;/em&gt; an edge between two vertices. However, as explained above, an adjacency matrix always requires a |V| × |V| matrix. Hence, if a graph is sparse (i.e. there are very few edges between vertices), the adjacency matrix will be full of default values that indicate that there is no edge between two vertices, which is quite space-inefficient.&lt;/p&gt;&#xA;&lt;h2 id=&#34;4-traversing-a-graph&#34;&gt;4. Traversing a Graph&lt;/h2&gt;&#xA;&lt;p&gt;Now that we have a solid idea of the graph data structure, it&amp;rsquo;s time to discuss how to do a &lt;em&gt;graph walk&lt;/em&gt; (or a &lt;em&gt;graph traversal&lt;/em&gt; ). Knowing how to traverse a graph is extremely important, because most advanced graph algorithms (such as &lt;a href=&#34;https://www.jeffyang.io/blog/dijkstras_algorithm&#34;&gt;Dijkstra&amp;rsquo;s Algorithm&lt;/a&gt;) use graph traversal algorithms as a fundamental cornerstone. There are largely two different approaches for traversing a graph; &lt;strong&gt;depth first&lt;/strong&gt; and &lt;strong&gt;breadth first&lt;/strong&gt;. A depth first traversal explores a graph until it is impossible to move further, backtracks, and continues exploring the graph in a depth-first manner until all nodes are visited. A breadth first traversal explores all the immediate neighbors first before moving on deeper into the graph, and continues exploring in a breadth-first manner until all nodes are visited. Both algorithms look quite similar, but a depth first traversal is usually implemented with a &lt;strong&gt;stack&lt;/strong&gt;, whereas a breadth first traversal uses a &lt;strong&gt;queue&lt;/strong&gt;. Let&amp;rsquo;s take a look at the following code that implements both algorithms.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;queue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dfs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#this is an iterative implementation of a depth-first search&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#using an auxiliary stack data structure&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start_node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;visited&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;currnode&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;currnode&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;visited&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;visited&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;currnode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#mark currnode as visited&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;currnode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;#goes through the entire list of neighbors for currnode&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;bfs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#this is the breadth-first algorithm&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#uses a queue instead of a stack&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;queue&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Queue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;maxsize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#queue with maxsize inf&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;visited&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qsize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;currnode&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;currnode&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;visited&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;visited&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;currnode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#mark currnode as visited&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;currnode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;#goes through the entire list of neighbors for currnode&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;put&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice how both the depth first traversal algorithm and the breadth first traversal algorithm are very similarly structured. In fact, the only difference is that the &lt;code&gt;dfs&lt;/code&gt; function uses a stack (implemented with Python&amp;rsquo;s list) and the &lt;code&gt;bfs&lt;/code&gt; function uses a queue from the Python&amp;rsquo;s &lt;code&gt;queue&lt;/code&gt; module. It is also very important to &lt;strong&gt;keep track of the visited nodes&lt;/strong&gt;, because graphs can have cycles that can lead to an infinite loop.&lt;/p&gt;&#xA;&lt;h2 id=&#34;5-conclusion&#34;&gt;5. Conclusion&lt;/h2&gt;&#xA;&lt;p&gt;In this post, we went through the basics of the graph data structure. This post certainly doesn&amp;rsquo;t cover everything, but hopefully is enough to serve as the starting point in your journey to conquer the world of graphs. More advanced graph algorithms will likely be discussed in future posts.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Finding the Shortest Path from A to Anywhere</title>
      <link>https://www.jeffyang.io/posts/dijkstras-algorithm/</link>
      <pubDate>Sun, 15 Dec 2019 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/dijkstras-algorithm/</guid>
      <category>Algorithms</category>
      <description>&lt;p&gt;&lt;em&gt;A deep dive into Dijkstra&#39;s Algorithm&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/dijkstras-algorithm/dijkstra_cover_hu_cbe61de76f39ccd6.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/dijkstras-algorithm/dijkstra_cover_hu_65cb3c087a694a3e.webp 640w, https://www.jeffyang.io/posts/dijkstras-algorithm/dijkstra_cover_hu_cbe61de76f39ccd6.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;720&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;In this post, I would like to take a slightly different approach and write about an algorithm that I learned about in a classroom setting. While taking a computer science course in data structures and algorithms this quarter, I found the graph data structure and related algorithms to be particularly fascinating, as graphs can be applied in real-life to represent virtually anything from friendships to geographic maps. Although far from being an expert on this topic, I am going to make an attempt to dissect perhaps the most famous graph algorithm and its practical application. Take a bow, introducing Dijkstra&amp;rsquo;s algorithm and the single source shortest path problem.&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-single-source-shortest-path&#34;&gt;1. Single Source Shortest Path&lt;/h2&gt;&#xA;&lt;p&gt;Before we talk about the algorithm itself, let&amp;rsquo;s first get a sense of what Dijkstra&amp;rsquo;s algorithm is all about; what significant questions can Dijkstra&amp;rsquo;s famous algorithm answer? Often referred to as the &amp;ldquo;Single source shortest path&amp;rdquo; problem, Dijkstra&amp;rsquo;s algorithm is suitable for finding the shortest distance from a single vertex to all other vertices. Using the results Dijkstra&amp;rsquo;s algorithm produces, we can also find the shortest path from a single vertex to a specific destination, say, vertex &lt;em&gt;f&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/dijkstras-algorithm/sssp_hu_fa6dbf08cae0ae04.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/dijkstras-algorithm/sssp_hu_fa6dbf08cae0ae04.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;A diagram of a graph denoting the start node and target node for finding the shortest path between two nodes.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;A diagram of a graph denoting the start node and target node for finding the shortest path between two nodes.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;The graph illustrated above might have you think &amp;ldquo;It doesn&amp;rsquo;t seem that hard to manually find the shortest path from vertex &lt;em&gt;a&lt;/em&gt; to &lt;em&gt;f&lt;/em&gt; !&amp;rdquo; If that&amp;rsquo;s the case, imagine a graph with hundreds of vertices and edges; would you still be willing to manually find the shortest path from &lt;em&gt;a&lt;/em&gt; to &lt;em&gt;f&lt;/em&gt; ? It would be possible, but extremely time-consuming. This is where Dijkstra&amp;rsquo;s algorithm comes into play. With some help from Dijkstra, we can find the shortest path from a single vertex to every other &lt;em&gt;reachable&lt;/em&gt; vertex in the graph, with relative ease.&lt;/p&gt;&#xA;&lt;h2 id=&#34;2-dijkstras-algorithm&#34;&gt;2. Dijkstra&amp;rsquo;s Algorithm&lt;/h2&gt;&#xA;&lt;p&gt;Finally, we can start our dissection of Dijkstra&amp;rsquo;s algorithm. If we simply try to memorize the code for the algorithm line by line, it will be difficult (at least for people with non-photographic memory, like me). The better approach would be to try and understand what repeated steps the algorithm goes through until it finishes processing the graph and returns. The following are the steps that Dijkstra&amp;rsquo;s algorithm follows, until it exhausts the entire vertex set of a graph:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;When selecting the next node to visit, decision should be made based on the &lt;strong&gt;known distance&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;For each node we visit, we check the neighboring nodes (i.e. the nodes that are 1 edge away from the node we are currently visiting)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;When checking the neighboring nodes, calculate the distance traveled so far (distance from the starting node to the current node) + distance to the neighboring node from the current node&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;If the calculation done above is &lt;strong&gt;less than&lt;/strong&gt; the known distance to the neighboring node, update the distance information for that neighboring node&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;(Note: Steps 3 - 4 are known as the &lt;em&gt;relaxation&lt;/em&gt; of vertices)&lt;/p&gt;&#xA;&lt;p&gt;In the implementation of the algorithm, I will be using Python&amp;rsquo;s &lt;code&gt;heapq&lt;/code&gt; module, which conveniently lets us use a priority queue to ensure that the nodes are relaxed in the correct order (minimum distance nodes first!). Also, note that I will be using an adjacency list representation of graphs, implemented with Python&amp;rsquo;s dictionary. With the steps specified above in mind, let&amp;rsquo;s take a look at the following implementaion of Dijkstra&amp;rsquo;s algorithm:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#python implementation of Dijkstra&amp;#39;s algorithm using priority queue &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;heapq&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dijkstra&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vertex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;inf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vertex&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vertex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vertex&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;visited&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vertex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vertex&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;todo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#repeat the process until nodes are exhausted&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;todo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &#x9;&lt;span class=&#34;c1&#34;&gt;#select the node with minimum known distance&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;curr_distance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;curr_vertex&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;heapq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heappop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;todo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;#if vertex has already been visited, skip it&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;visited&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;curr_vertex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;#if vertex has not been visited do the follwing&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;visited&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;curr_vertex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;#check the neighbors of the current node&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weight&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;curr_vertex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &#x9;&lt;span class=&#34;c1&#34;&gt;#update if following condition is satisfied&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;curr_distance&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weight&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;curr_distance&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weight&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;c1&#34;&gt;#keep track of previous node : we need it to find shortest path&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;curr_vertex&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;heapq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heappush&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;todo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;curr_distance&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#result packages dist and pred in a tuple     &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dijkstra&amp;rsquo;s algorithm implemented as above does not directly give us the shortest path between two vertices. However, it does produce two valuable pieces of information, one of which can be carefully crafted to give us the &lt;em&gt;shortest path&lt;/em&gt; between two vertices. Notice how the &lt;code&gt;dijkstra&lt;/code&gt; function above returns a tuple containing &lt;code&gt;pred&lt;/code&gt; and &lt;code&gt;dist&lt;/code&gt;. The &lt;code&gt;pred&lt;/code&gt; dictionary contains information on what node was visited before visiting each of the vertices while traversing the graph. The &lt;code&gt;dist&lt;/code&gt; dictionary contains information on the distance from the starting point to each of the vertices in the graph. By working our way backwards in the &lt;code&gt;pred&lt;/code&gt; dictionary, starting from the destination until we reach the starting point, it is possible to identify the &lt;em&gt;shortest path&lt;/em&gt; from the source to destination. We will need the help of a stack to get the order correct.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;shortest_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;destination&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;#retrieve the pred dictionary from dijkstra&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dijkstra&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;#we need a stack to reverse the order &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;n&#34;&gt;stack&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;destination&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;#we will return the shortest path as a list&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;n&#34;&gt;shortest_path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;n&#34;&gt;curr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;n&#34;&gt;before&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;curr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;c1&#34;&gt;#source and destination are disconnected&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;before&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;there is no path!&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;c1&#34;&gt;#we have reached the source node by traversing backwards&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;before&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;n&#34;&gt;stack&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;before&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;n&#34;&gt;stack&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;before&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;#reverse the order: source --&amp;gt; destination &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;n&#34;&gt;shortest_path&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stack&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;shortest_path&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#example graph to test dijkstra&amp;#39;s algorithm&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;example_graph&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;e&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;e&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;f&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;g&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;e&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;f&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;f&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;g&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;g&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the &lt;code&gt;shortest_path&lt;/code&gt; function, we can now find the shortest path from a source node to a destination node. Note that Dijkstra&amp;rsquo;s algorithm is not capable of finding the distance/shortest path between two nodes that are not connected.&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/dijkstras-algorithm/sssp2_hu_928c908f358f771f.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/dijkstras-algorithm/sssp2_hu_928c908f358f771f.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;A diagram that illustrates how the Dijkstra&amp;#39;s algorithm would proceed to find the shortest path between two nodes.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;A diagram that illustrates how the Dijkstra&amp;rsquo;s algorithm would proceed to find the shortest path between two nodes.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;The following is the result of running &lt;code&gt;dijkstra&lt;/code&gt; and &lt;code&gt;shortest_path&lt;/code&gt; functions inside my Python shell with the example graph provided above. Note that the &lt;code&gt;example_graph&lt;/code&gt; provided in the code is an adjacency list representation of the graph shown above, but is implemented with a Python dictionary.&lt;/p&gt;&#xA;&lt;p&gt;{{ &amp;lt; figure src=&amp;ldquo;dijkstra_demo.png&amp;rdquo; caption=&amp;ldquo;A screenshot of my terminal showing the results of running Dijkstra&amp;rsquo;s algorithm.&amp;rdquo; &amp;gt;}}&lt;/p&gt;&#xA;&lt;h2 id=&#34;3-limitations-of-dijkstras-algorithm&#34;&gt;3. Limitations of Dijkstra&amp;rsquo;s Algorithm&lt;/h2&gt;&#xA;&lt;p&gt;The Dijkstra&amp;rsquo;s algorithm is not bullet proof, however. The most remarkable limitation of Dijkstra&amp;rsquo;s algorithm is that it will not work as expected if there are negative edges in a graph. In order to find the shortest distance from one starting vertex to all connected vertices, Dijkstra relies on the fact that if all edges are non-negative, it is always true that adding an edge will never make a path shorter. Only when such an assumption can safely be made can Dijkstra&amp;rsquo;s &amp;ldquo;greedy&amp;rdquo; approach to the problem remain intact with solving global optimality. If negative edges are introduced and such an assumption is no longer valid, Dijkstra&amp;rsquo;s method of selecting a node with minimum known distance at each iteration will no longer guarantee a correct outcome. To overcome such limitations of Dijkstra&amp;rsquo;s algorithm, we can use other algorithms such as the Bellman-Ford&amp;rsquo;s algorithm and the Floyd-Warshall&amp;rsquo;s algorithm, as needed. These may be subject to discussion in future posts.&lt;/p&gt;&#xA;&lt;h2 id=&#34;4-conclusion&#34;&gt;4. Conclusion&lt;/h2&gt;&#xA;&lt;p&gt;Hopefully this blog post helped you gain a solid understanding of Dijkstra&amp;rsquo;s algorithm and the single source shortest path problem. If there is anyone reading this post who is interested in learning the mathematical proof on the correctness of Dijkstra&amp;rsquo;s famous algorithm, I found &lt;a href=&#34;https://web.engr.oregonstate.edu/~glencora/wiki/uploads/dijkstra-proof.pdf&#34;&gt;this&lt;/a&gt; helpful.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Tweet Bot with Tweepy and Flask</title>
      <link>https://www.jeffyang.io/posts/tweet-bot/</link>
      <pubDate>Thu, 14 Nov 2019 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/tweet-bot/</guid>
      <category>Web Development</category>
      <description>&lt;p&gt;&lt;em&gt;How to build a Twitter bot with Python&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/tweet-bot/tweetbot_hu_ba204fe4e6f5958c.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/tweet-bot/tweetbot_hu_ba204fe4e6f5958c.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;According to recent research conducted by USC and Indiana University affiliates, the &lt;em&gt;conservative&lt;/em&gt;&#xA;estimate for the number of Twitter bots is somewhere around 15% of all active accounts, suggesting&#xA;that automated account activity is in fact prominent across the Twitter ecosystem. In fact, your&#xA;daily interactions on Twitter may very well include interactions with bots! Despite the negative&#xA;connotations surrounding the term &lt;em&gt;Twitter Bots&lt;/em&gt;, the qualitative judgement shouldn&amp;rsquo;t be so&#xA;simplistic. The usage of Twitter bots can vary from being as helpful as disseminating information on&#xA;natural disasters, to being as harmful as cultivating terrorist recruitment propaganda. Leaving that&#xA;value judgment up to you, in this blog post, I&amp;rsquo;ll be writing about how to make a fully functional&#xA;Twitter bot that Tweets simple sentences composed of three words - hence the &amp;ldquo;Three Word&#xA;Philosopher.&amp;rdquo; Hop on!&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-getting-ready---the-basics&#34;&gt;1. Getting Ready - The Basics&lt;/h2&gt;&#xA;&lt;p&gt;As I always emphasize, when starting Python projects, it&amp;rsquo;s a good idea to default to creating a&#xA;Python virtual environment for the specific project that you are working on. So let&amp;rsquo;s do just that:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mkdir my_twitter_bot&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; my_twitter_bot&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ python3 -m venv venv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; venv/bin/activate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once we have our virtual environment ready, we are going to need to install some dependencies that&#xA;we need for this project. For now, let us install the non-trivial dependencies that are must-haves&#xA;to create a fully functioning Twitter bot. Make sure that you have your &lt;code&gt;my_twitter_bot&lt;/code&gt; virtual&#xA;environment activated!&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;c1&#34;&gt;#we are going to need BS4 for web scraping purposes&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip install beautifulsoup4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;c1&#34;&gt;#we need tweepy to seamlessly interact with the Twitter API&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip install tweepy&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;c1&#34;&gt;#we need Flask to create a web app later on for user interface!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip install Flask&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;c1&#34;&gt;#create requirements.txt containing all the dependency versions you&amp;#39;ve just installed&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip freeze &amp;gt; requirements.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We now have all the must-haves ready. Let&amp;rsquo;s move on to the next step. Again, recall the order of the&#xA;steps we took to start out our project here: &lt;em&gt;first&lt;/em&gt; create a virtual environment, &lt;em&gt;then&lt;/em&gt; install&#xA;any dependencies you are going to need for the project.&lt;/p&gt;&#xA;&lt;p&gt;Note that in the snippet above, I&amp;rsquo;ve installed all major dependencies at once. However, in real&#xA;life, this is unlikely to happen because you will never know which dependencies you are going to end&#xA;up with at the end of the day. So in real life, it is more likely that you would update your&#xA;requirements.txt file as you make progress with your project.&lt;/p&gt;&#xA;&lt;h2 id=&#34;2-feeding-your-bot---a-wealthful-and-clean-text-corpus&#34;&gt;2. Feeding Your Bot - A Wealthful and Clean Text Corpus&lt;/h2&gt;&#xA;&lt;p&gt;To build a bot that doesn&amp;rsquo;t dully Tweet out same sets of hard-coded sentences over and over again,&#xA;we need to first feed our bot with words so that it can dynamically generate sentences. With a bit&#xA;of machine learning knowledge (such as the&#xA;&lt;a href=&#34;https://medium.com/@__amol__/markov-chains-with-python-1109663f3678&#34;&gt;Markov Chain&lt;/a&gt;), it&amp;rsquo;s possible&#xA;to generate a grammatically sound sentence given that the bot is fed with a wealthful repository of&#xA;words. However, for this bot, I did not use any machine learning concepts and instead opted to try&#xA;and mimic a short, &amp;ldquo;philosophical&amp;rdquo; sentence by performing a simple categorization of words. To&#xA;achieve any of this, we are first going to need a source for our texts. Because I wanted a long text&#xA;with &amp;ldquo;philosophical&amp;rdquo; words, I scraped an e-book of John Stuart Mill&amp;rsquo;s &lt;em&gt;Utilitarianism&lt;/em&gt; from&#xA;&lt;a href=&#34;https://www.gutenberg.org/&#34;&gt;Project Gutenberg&lt;/a&gt;. You are more than welcome to choose any other&#xA;source, but it&amp;rsquo;s going to be better if the text is decently long, to prevent repetitive sentences.&lt;/p&gt;&#xA;&lt;p&gt;For the purposes of scraping an e-book (or any other source as long as it&amp;rsquo;s online), we are going to&#xA;use BeautifulSoup, Python&amp;rsquo;s famous parsing library.&#xA;&lt;a href=&#34;https://www.crummy.com/software/BeautifulSoup/bs4/doc/&#34;&gt;BeautifulSoup&lt;/a&gt; is a fantastic package for&#xA;parsing HTML and XML documents. We are not looking to do anyting complex here, so the code is fairly&#xA;simple. Make a python file (I named mine &lt;code&gt;scrape_words.py&lt;/code&gt;), and do:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#scrape_words.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;bs4&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BeautifulSoup&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;requests&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;URL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;http://www.gutenberg.org/files/11224/11224-h/11224-h.htm&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#use BeautifulSoup to extract content we want from an e-book from Project Gutenberg&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;fetch_text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;requests&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;soup&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BeautifulSoup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;lxml&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;text_content&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;el&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;soup&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;p&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;text_content&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text_content&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;NOTE: Web scraping isn&amp;rsquo;t illegal, but keep in mind that programs can send out requests at speeds&#xA;that can have detrimental effects on the sites being scraped. It&amp;rsquo;s important to adhere to web&#xA;scraping&#xA;&lt;a href=&#34;https://www.scrapehero.com/how-to-prevent-getting-blacklisted-while-scraping/&#34;&gt;guidelines&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;The few lines of Python code above are enough to extract the text content from the selected e-book&amp;rsquo;s&#xA;HTML source. Since this is not a BeautifulSoup tutorial post, I will not go into the details of&#xA;using BeautifulSoup as a web scraping tool for Python (A great, free tutorial can be found&#xA;&lt;a href=&#34;https://www.youtube.com/watch?v=ng2o98k983k&amp;amp;t=2409s&#34;&gt;here&lt;/a&gt;, though). If you have been following&#xA;along, you might have realized that the text content extracted from the HTML document tree still has&#xA;some unwanted punctuations and sentences. We want to clean up that mess, or else our bot will be&#xA;making sentences with unwanted words. In &lt;code&gt;scrape_words.py&lt;/code&gt; add:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#scrape_words.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;re&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#text clean-up process&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;text_cleanup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;re&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sub&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;\[.*?\]&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;re&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sub&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;[^\w\d.\s]+&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;re&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sub&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;\w*\d\w*&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;output_file_write&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;gutenberg.txt&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;output_file_write&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;text_cleanup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fetch_text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;URL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;output_file_write&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Essentially the function &lt;code&gt;text_cleanup&lt;/code&gt; takes a string as input, gets rid of all the unwanted&#xA;punctuations, and returns the cleaned up string. Then, we write that cleaned text into an output&#xA;file, so our bot can later access it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;3-a-simple-categorization-of-the-corpus&#34;&gt;3. A Simple Categorization of the Corpus&lt;/h2&gt;&#xA;&lt;p&gt;Now that we have a clean text corpus ready for use, let&amp;rsquo;s write some code to help our bot generate&#xA;sentences. Create a new file called &lt;code&gt;sentence.py&lt;/code&gt; in your project directory, and do:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#sentence.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;random&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;categorize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;gutenberg.txt&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;contents&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;listify&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;contents&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;word&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;word&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isupper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;word&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isupper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;word&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;word&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endswith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;word&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isupper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;word&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;word&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isupper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;word&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;categorize()&lt;/code&gt; function reads the long text from the cleaned up &lt;code&gt;gutenberg.txt&lt;/code&gt; file, and&#xA;categorizes it into three different lists. The &lt;code&gt;start&lt;/code&gt; list is the list of words that our bot is&#xA;going to select from to put as the starting word of a three-word sentence. The &lt;code&gt;end&lt;/code&gt; list is the&#xA;list of words that our bot is going to finish the sentence with. The &lt;code&gt;body&lt;/code&gt; list contains the&#xA;remaining words that are neither the starting nor ending words. To choose the words to put in the&#xA;&lt;code&gt;start&lt;/code&gt; list, I leveraged the fact that proper nouns and words that mark the start of any sentence&#xA;are capitalized. To choose the words to put in the &lt;code&gt;end&lt;/code&gt; list, I leveraged the fact that words that&#xA;mark the end of any sentence are immediately followed by a period. Anything that is not in the&#xA;&lt;code&gt;start&lt;/code&gt; or &lt;code&gt;end&lt;/code&gt; list, I threw into the &lt;code&gt;body&lt;/code&gt; list.&lt;/p&gt;&#xA;&lt;p&gt;Although not necessarily the most sophisticated way of categorization, it will suffice for the&#xA;purpose of feeding our bot with words to generate a sentence comprised of three parts - &lt;em&gt;start,&#xA;body, end&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;4-make-that-bot-speak&#34;&gt;4. Make That Bot Speak!&lt;/h2&gt;&#xA;&lt;p&gt;We&amp;rsquo;ve finished categorizing our words, so now we need to generate a sentence for the bot to Tweet&#xA;out. We are going to accomplish that by using Python&amp;rsquo;s random module.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#sentence.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;generate_sentence&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;sentence&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;choice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt; \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;choice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lower&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt; \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;choice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sentence&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;generate_sentence&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;categorize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;categorize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;categorize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it! Now by calling the function &lt;code&gt;run()&lt;/code&gt;, we can generate three-word sentences. Granted,&#xA;because I didn&amp;rsquo;t use a more sophisticated algorithm (perhaps a machine learning algorithm) to try&#xA;and emulate a more human-like sentence, the sentences generated will not always make perfect sense.&#xA;However, for the purposes of this project, it does the minimum job of sounding like a &amp;ldquo;Three Word&#xA;Philosopher.&amp;rdquo; Here is a sample of three-word sentences that our bot is capable of producing:&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/tweet-bot/sentence_sample_hu_4b4b68018acd446f.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/tweet-bot/sentence_sample_hu_4b4b68018acd446f.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;274&#34; alt=&#34;A screenshot of the terminal showing the kinds of sentences our bot is capable of producing.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;A screenshot of the terminal showing the kinds of sentences our bot is capable of producing.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;h2 id=&#34;5-connecting-our-bot-to-twitter&#34;&gt;5. Connecting our Bot to Twitter&lt;/h2&gt;&#xA;&lt;p&gt;We now have a bot that can generate three-word sentences (well, something close it) by selecting&#xA;words from the three categorized lists we built. To build a bot that &lt;em&gt;Tweets&lt;/em&gt;, we now need to plug&#xA;our bot that resides in our local machine to the outer world - Twitter.&lt;/p&gt;&#xA;&lt;p&gt;To connect our bot to Twitter, we first need to somehow programmatically access Twitter, right?&#xA;That&amp;rsquo;s where Twitter&amp;rsquo;s API comes into play. API stands for &lt;em&gt;Application Programming Interface&lt;/em&gt;&#xA;(check this &lt;a href=&#34;https://medium.com/@perrysetgo/what-exactly-is-an-api-69f36968a41f&#34;&gt;article&lt;/a&gt; out), and&#xA;more specifically, Twitter&amp;rsquo;s API is a predefined set of Twitter-developed protocols that programmers&#xA;can use to interact with Twitter at a programmatic level, without any user interface. Although&#xA;Twitter&amp;rsquo;s API can be used by anyone who needs it, you do need to&#xA;&lt;a href=&#34;https://developer.twitter.com/en/apply-for-access.html&#34;&gt;apply online&lt;/a&gt; to get access to your Twitter&#xA;API keys (Twitter is very sensitive to malicious usage of their API). You will need to answer some&#xA;questions to confirm with Twitter that you have legitimate reasons for requesting to use their API.&#xA;Once Twitter approves, you will have access to your own API keys that you will need to connect your&#xA;bot to Twitter.&lt;/p&gt;&#xA;&lt;p&gt;Before we just copy and paste these API keys straight into our code, we need to first ask ourselves&#xA;if it&amp;rsquo;s a good idea to hard-code configurations. My code is also pushed up to Github, which means&#xA;that other people are going to be able to see it too. &lt;em&gt;Uh-oh&lt;/em&gt;. Then it would be a good idea to&#xA;encapsulate our API keys with a layer of protection, so other people don&amp;rsquo;t have access to sensitive&#xA;information, right? Fortunately, Python&amp;rsquo;s dotenv module provides a fantastic way to get around such&#xA;issues. After installing the dotenv package, you will need to create a &lt;code&gt;.env&lt;/code&gt; file inside your&#xA;project directory and store your sensitive information there.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip install python-dotenv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip freeze &amp;gt; requirements.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ touch .env&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then open your &lt;code&gt;.env&lt;/code&gt; file with an editor of your choice, and input the following:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#.env&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;CONSUMER_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;YOUR_CONSUMER_KEY&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;CONSUMER_SECRET_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;YOUR_CONSUMER_SECRET_KEY&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ACCESS_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;YOUR_ACCESS_KEY&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ACCESS_SECRET_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;YOUR_SECRET_ACCESS_KEY&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you are going to push your project up to a remote repository (i.e. Github), remember to include&#xA;your &lt;code&gt;.env&lt;/code&gt; file in your &lt;code&gt;.gitignore&lt;/code&gt; file!&lt;/p&gt;&#xA;&lt;p&gt;Good work, now we are ready to have our bot interact with Twitter API. There are numerous ways to&#xA;achieve this using Python. In this project, I chose to use &lt;a href=&#34;https://www.tweepy.org/&#34;&gt;Tweepy&lt;/a&gt;, a very&#xA;popular and easy-to-use Python library for accessing Twitter API. Create a file called&#xA;&lt;code&gt;tweet_generator.py&lt;/code&gt; and do:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#tweet_generator.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;dotenv&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;load_dotenv&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;tweepy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;os&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;load_dotenv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;CONSUMER_KEY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getenv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;CONSUMER_KEY&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;CONSUMER_SECRET_KEY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getenv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;CONSUMER_SECRET_KEY&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ACCESS_KEY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getenv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;ACCESS_KEY&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ACCESS_SECRET_KEY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getenv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;ACCESS_SECRET_KEY&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;tweet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;auth&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tweepy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OAuthHandler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CONSUMER_KEY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CONSUMER_SECRET_KEY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;auth&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;set_access_token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ACCESS_KEY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ACCESS_SECRET_KEY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;api&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tweepy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;API&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;auth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;update_status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code above accomplishes the simple job of connecting our bot to Twitter through our&#xA;authenticated API keys, and updating the status with a custom input - which we call &lt;em&gt;Tweeting&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;6-taking-it-a-bit-further---flask-and-heroku&#34;&gt;6. Taking it a Bit Further - Flask and Heroku&lt;/h2&gt;&#xA;&lt;p&gt;This is an optional part of the project that is not an integral part of the Twitter bot itself.&#xA;However, I thought that it would be nice to have a user friendly interface for the &amp;ldquo;Three Word&#xA;Philsopher&amp;rdquo; that I built so that people can easily use the bot that I built. I am not going to go&#xA;into the details of Flask here, but as an introduction , Flask is a great Python microframework for&#xA;building web applications. If you are interested, I have a more detailed post on Flask&#xA;&lt;a href=&#34;https://www.jeffyang.io/blog/build_a_flask_website&#34;&gt;here&lt;/a&gt;. Create a Python file with a name of your&#xA;choice, and do the following:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#flaskapp.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;flask&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Flask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render_template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;redirect&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#we need to import sentence and tweet_generator to access the functions we wrote!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sentence&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;tweet_generator&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Flask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@app.route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#we need a HTML file called index.html to render it&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#the sentence parameter provides the template with dynamically generated sentences&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render_template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;index.html&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sentence&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sentence&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@app.route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/tweet&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;methods&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;tweet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#this function will handle the tweeting&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;form&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;status&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;tweet_generator&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tweet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;redirect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;port&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;PORT&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;0.0.0.0&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;port&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;port&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;debug&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will need to create a HTML file &lt;code&gt;index.html&lt;/code&gt; and put it in a directory called &lt;code&gt;templates&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;c1&#34;&gt;#inside your project directory&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mkdir templates&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ touch index.html&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then open the &lt;code&gt;index.html&lt;/code&gt; file in an editor of your preference:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-HTML&#34; data-lang=&#34;HTML&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Tweet Bot &lt;span class=&#34;ni&#34;&gt;&amp;amp;#8211;&lt;/span&gt; Three Word Philosopher&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;h5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            Philosopher&amp;#39;s Tweet: &amp;#34;{{ sentence }}&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;h5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;form&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ url_for(&amp;#39;tweet&amp;#39;) }}&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;hidden&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ sentence }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;display:inline-block;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;button&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                Tweet&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;button&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;display: inline-block&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;role&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;button&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                Refresh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;text-center&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;display: inline-block;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://twitter.com/threethouthee&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;role&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;button&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                Visit the Philosopher&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;form&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The HTML code above is not going to generate a decent looking interface. However, it does the bare&#xA;minimum of displaying the tweets that the bot is generating through the &amp;ldquo;Refresh&amp;rdquo; button, and lets&#xA;the user Tweet the generated sentence through the &amp;ldquo;Tweet&amp;rdquo; button. Again, if you are lost, you should&#xA;take a look at this &lt;a href=&#34;https://www.jeffyang.io/blog/build_a_flask_website&#34;&gt;post&lt;/a&gt; to get a better&#xA;understanding of Flask and its templating engine Jinja2. Check out my&#xA;&lt;a href=&#34;https://github.com/jeffjaehoyang/twitter_bot&#34;&gt;repository&lt;/a&gt; to see what I did to make my interface&#xA;look a bit better.&lt;/p&gt;&#xA;&lt;p&gt;Now that we have a Flask web application, I wanted to deploy it on Heroku so that I could reveal my&#xA;bot to the world. Deploying a Flask Application on Heroku is not too difficult to do, and I have&#xA;that covered in detail &lt;a href=&#34;https://www.jeffyang.io/blog/deploy_flask_app_on_heroku&#34;&gt;here&lt;/a&gt;, if you are&#xA;interested.&lt;/p&gt;&#xA;&lt;h2 id=&#34;7-what-i-learned&#34;&gt;7. What I learned&lt;/h2&gt;&#xA;&lt;p&gt;The Twitter bot project turned out to be an enjoyable learning experience. Here are some areas that&#xA;I thought this project helped me hone my skills in:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Basic &lt;strong&gt;web scraping&lt;/strong&gt; with Python&lt;/li&gt;&#xA;&lt;li&gt;Python &lt;strong&gt;regular expressions&lt;/strong&gt; to clean up the text corpus&lt;/li&gt;&#xA;&lt;li&gt;Understanding the concept of &lt;strong&gt;APIs&lt;/strong&gt;, and how to interact with them&lt;/li&gt;&#xA;&lt;li&gt;Python &lt;strong&gt;configuration management&lt;/strong&gt; best practices&lt;/li&gt;&#xA;&lt;li&gt;How to &lt;strong&gt;deploy&lt;/strong&gt; a Flask Application on Heroku&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Possible improvements:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;This bot would have been more interesting with a machine learning oriented algorithm to generate&#xA;sentences that actually sound like real human, instead of three-word sentences.&lt;/li&gt;&#xA;&lt;li&gt;Twitter API allows for much more than just &lt;em&gt;Tweeting&lt;/em&gt;! It would be fun to implement other&#xA;functionalities to the bot, such as automated Retweeting or automated replying.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That&amp;rsquo;s all! &lt;a href=&#34;https://threewordthinker-api-heroku.herokuapp.com/&#34;&gt;This&lt;/a&gt; is the link to the project&#xA;site that I deployed on Heroku. Thank you for reading this post, and enjoy the philosophical Tweet&#xA;bot!&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;By the way&lt;/em&gt;, I wasn&amp;rsquo;t kidding when I said there are bots lurking around in Twitter&amp;hellip;&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/tweet-bot/jesus_retweet_hu_975c562664b7b214.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/tweet-bot/jesus_retweet_hu_975c562664b7b214.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;122&#34; alt=&#34;A real screenshot of another tweet bot retweeting a tweet that our bot just tweeted.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;figcaption&gt;&lt;p&gt;A real screenshot of another tweet bot retweeting a tweet that our bot just tweeted.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;Github repository &lt;a href=&#34;https://github.com/jeffjaehoyang/twitter_bot&#34;&gt;link&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Go Live With Your Flask Application on Heroku</title>
      <link>https://www.jeffyang.io/posts/deploy-flask-application-on-heroku/</link>
      <pubDate>Fri, 01 Nov 2019 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/deploy-flask-application-on-heroku/</guid>
      <category>Web Development</category>
      <description>&lt;p&gt;&lt;em&gt;How to deploy a Flask web application on Heroku&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/deploy-flask-application-on-heroku/heroku_flask_hu_df58f20d3e1ebb3e.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/deploy-flask-application-on-heroku/heroku_flask_hu_df58f20d3e1ebb3e.webp 650w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;650&#34; height=&#34;366&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;You worked for hours on your project, maybe days, or maybe even weeks or months. Regardless of the magnitude of your project, you probably want to share your work somewhere online so others can see it, right? At least for me, the urge to share my work online is something natural. An application is as good as dead if no one else can see your project, and is just sitting there on your local machine. Many inexperienced programmers go through that disheartening moment of thinking &amp;ldquo;How in the world do I host this application up on the internet?&amp;rdquo; If this sounds like you, this post is meant for you (because I know so very well how hopeless that feels), so follow along!&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-what-is-heroku&#34;&gt;1. What is Heroku?&lt;/h2&gt;&#xA;&lt;p&gt;Heroku is an extremely popular Platform as a Service (PaaS). In case you are unfamiliar with what PaaS is, it&amp;rsquo;s a type of cloud computing services that allows developers to develop, manage, and run applications without having to deal with the complications of building server-side infrastructure.&lt;/p&gt;&#xA;&lt;h2 id=&#34;2-prerequisites&#34;&gt;2. Prerequisites&lt;/h2&gt;&#xA;&lt;p&gt;To deploy on Heroku, you will need a basic application to deploy (obviously). In this post, I will be writing specifically about how to deploy a Flask application on Heroku. Hopefully, your application is running in a virtual environment (it&amp;rsquo;s always a good idea, when working with Python). Additionally, a very basic knowledge of the command line and git would be of great help. Before we get started, if you don&amp;rsquo;t have an application, you can still follow along by cloning my repository to your local machine (you will need to have git installed):&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git clone https://github.com/jeffjaehoyang/twitter_bot.git &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should see that you have a directory called &amp;ldquo;twitter_bot&amp;rdquo; now. Assuming you have pip and python3 installed:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; twitter_bot&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ python3 -m venv venv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; venv/bin/activate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip install -r requirements.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the last command above installs into the virtual environment all the dependencies that the project needs to run properly. Once you run the commands above, we are ready to get started.&lt;/p&gt;&#xA;&lt;h2 id=&#34;3-starter&#34;&gt;3. Starter&lt;/h2&gt;&#xA;&lt;p&gt;First, you will need to download the Heroku CLI. For more information, please take a look &lt;a href=&#34;https://devcenter.heroku.com/articles/heroku-cli#download-and-install&#34;&gt;here&lt;/a&gt;. During this process, you should have been prompted to create a Heroku account (please make sure you do, you&amp;rsquo;ll need that!) This first step should have been relatively simple and straightforward. Once you download the Heroku CLI, you will have to login.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku login&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  heroku: Press any key to open up the browser to login or q to exit: &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;press any key!&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That should have prompted you to a Heroku login page, where you need to type in your login credentials. Hopefully, you got that right. Now let&amp;rsquo;s move onto the specifics of deploying your Flask application on Heroku!&lt;/p&gt;&#xA;&lt;h2 id=&#34;4-a-step-before-deployment&#34;&gt;4. A Step Before Deployment&lt;/h2&gt;&#xA;&lt;h3 id=&#34;install-gunicorn&#34;&gt;Install Gunicorn&lt;/h3&gt;&#xA;&lt;p&gt;To deploy your application on Heroku, you need to specify which web server your application is going to be running. We will use Gunicorn. We need to install this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip install gunicorn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip freeze &amp;gt; requirements.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that we updated the &lt;code&gt;requirements.txt&lt;/code&gt; above, because we added gunicorn as a dependency.&lt;/p&gt;&#xA;&lt;h3 id=&#34;add-a-procfile&#34;&gt;Add a Procfile&lt;/h3&gt;&#xA;&lt;p&gt;You will need to add a Procfile (don&amp;rsquo;t add any extensions!) specifying a web server and your application name:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ vim Procfile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  web: gunicorn flaskrun: app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For those of you who are following along with your own application, please note that you should change &lt;code&gt;flaskrun&lt;/code&gt; to the name of your application. We are ready to deploy!&lt;/p&gt;&#xA;&lt;h2 id=&#34;5-deploy-your-application&#34;&gt;5. Deploy Your Application&lt;/h2&gt;&#xA;&lt;p&gt;Inside your project directory, run:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku create YOURAPPNAME-api-heroku&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you run the command above, you will be able to see two different URLs:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ https://YOURAPPNAME-api-heroku.herokuapp.com &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; https://git.heroku.com/YOURAPPNAME-api-heroku.git&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first URL above is where your application is deployed, so please save it somewhere safe. The second URL is the Heroku git repository, and for your application to go live, you need to push your work to the &lt;code&gt;master&lt;/code&gt; branch of the Heroku repository.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git push https://git.heroku.com/YOURAPPNAME-api-heroku.git master&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;6-configuration-on-heroku&#34;&gt;6. Configuration on Heroku&lt;/h2&gt;&#xA;&lt;p&gt;If you were using my tweet bot repository to follow along, you may have realized that there&amp;rsquo;s a problem. Don&amp;rsquo;t worry, we didn&amp;rsquo;t do anything wrong. We just need to do one more thing. Because I used Twitter API keys for my tweet bot application (and that&amp;rsquo;s considered sensitive information), I didn&amp;rsquo;t push that information to my git repository. Locally, that information sits in a dotenv file, and on Heroku, we have to provide that information for our appliation to work. I won&amp;rsquo;t be providing my own API keys, but you can go ahead and look &lt;a href=&#34;https://developer.twitter.com/en/docs/basics/authentication/guides/access-tokens&#34;&gt;here&lt;/a&gt; for official documentation on how to get your own Twitter API keys. Once you have your own keys ready, configure them on Heroku by doing the following inside your project directory:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku config:set &lt;span class=&#34;nv&#34;&gt;CONSUMER_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;YOUR-API-KEY&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku config:set &lt;span class=&#34;nv&#34;&gt;CONSUMER_SECRET_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;YOUR-SECRET-KEY&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku config:set &lt;span class=&#34;nv&#34;&gt;ACCESS_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;YOUR-ACCESS-KEY&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ heroku config:set &lt;span class=&#34;nv&#34;&gt;ACCESS_SECRET_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;YOUR-ACCESS-SECRET-KEY&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Congratulations! Your application should now be live on Heroku!&lt;/p&gt;&#xA;&lt;p&gt;Heroku has done a great job in documenting how to deploy your application on Heroku: &lt;a href=&#34;https://devcenter.heroku.com/articles/getting-started-with-python&#34;&gt;link&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Build Your Own Website with Flask</title>
      <link>https://www.jeffyang.io/posts/build-a-flask-website/</link>
      <pubDate>Sat, 26 Oct 2019 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/build-a-flask-website/</guid>
      <category>Web Development</category>
      <description>&lt;p&gt;&lt;em&gt;How to build your own website with Flask&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://www.jeffyang.io/static/images/build-a-flask-website/flask_md.png&#34; alt=&#34;Blog Post Thumbnail&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: &lt;em&gt;I have migrated my personal website since I wrote this post back in 2019, and moved away from the blog written with Flask. This website that you are on currently is built with Gatsby, and is hosted on Netlify. Regardless, for those of you interested in creating a simple personal website with Flask, read on!&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Hello World! This is a post that was on my mind for a while now, and I&amp;rsquo;m so glad to finally write this post on this personal portfolio site I created! I previously had a Wordpress site that got me a good-looking fancy website up and running in no time. With all the beautiful Wordpress blog templates out there for a bargain, a sleek looking website was just a few clicks away. Nevertheless, something kept telling me to build a website myself - let me tell you why. Most importantly, the Wordpress site was beautiful on the outside, but rather bulky inside. I wanted to build a faster and a more light-weight website. Wordpress offered thousands of useful plugins (which is great), but having to update them constantly (for security purposes) was another downside of owning a Wordpress site. Last but certainly not the least, I thought &amp;ldquo;why not take this chance to brush up my web development skills?&amp;rdquo; This portfolio site is by no means greatly complex, but it still has all the basic structures of a full-fledged web application. So this simple site came to be, and for its creation, I mainly utilized Python&amp;rsquo;s microframework, Flask (and some BootStrap4 to make things look decent effortlessly).&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-what-is-flask&#34;&gt;1. What is Flask?&lt;/h2&gt;&#xA;&lt;p&gt;Flask is a lightweight microframework written in Python that is both intuitive and beginner friendly. I have a bit of prior experience in Flask development (mostly with APIs), but this was my first time building a Flask app completely from scratch, and I found the basics to be fairly easy to grasp. What I found was particularly convenient about Flask was Jinja2, a templating engine that comes with Flask (upon installation, literally). Jinja2 made it easy to create nested pages, by using a common layout page that all the nested pages could be built into. I decided to serve static Markdown files for my blog posts, instead of updating and editing directly from the website through a form. To achieve this, I used Flask&amp;rsquo;s FlatPages module, which essentially serves static Markdown files from a designated directory (all you need to do is upload a Markdown file to the directory, and it will be up on your website!)&lt;/p&gt;&#xA;&lt;h2 id=&#34;2-lets-get-started&#34;&gt;2. Let&amp;rsquo;s Get Started!&lt;/h2&gt;&#xA;&lt;h3 id=&#34;setting-up-your-virtual-environment&#34;&gt;Setting Up Your Virtual Environment&lt;/h3&gt;&#xA;&lt;p&gt;All developers who have any level of experience with Python will probably agree that no matter what type of project you are working on, installing packages systemwide is not a good idea. If you are new to Python, and don&amp;rsquo;t have a clue about what a virtual environment is, here&amp;rsquo;s an overly simplified explanation: Essentially, virtual environments help keep your dependencies (i.e. libraries/packages) separate for each project, so that each project knows exactly what versions of which extensions it needs to run properly. For more detailed information on Python&amp;rsquo;s virtual environment and best practices, I found &lt;a href=&#34;https://realpython.com/python-virtual-environments-a-primer/&#34;&gt;this&lt;/a&gt; article particularly resourceful.&lt;/p&gt;&#xA;&lt;p&gt;Create a virtual environment inside your project directory (assuming you are using Python 3):&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mkdir my_flask_app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; my_flask_app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ touch run.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ python3 -m venv venv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; venv/bin/activate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;a-quickstart-to-your-basic-flask-application&#34;&gt;A Quickstart to Your Basic Flask Application&lt;/h3&gt;&#xA;&lt;p&gt;Install the following dependencies using pip (assuming you have pip installed):&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pip install Flask Flask-FlatPages Pygments&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have the bare minimum to start building a website like the one you are looking at right now!&#xA;Inside &lt;code&gt;run.py&lt;/code&gt;, write some code along the lines of the following:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;flask&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Flask&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Flask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@app.route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Yes! Just Cooked Some Flask!&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#If running directly with Python, just set debug mode to TRUE&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;debug&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have the most basic application ready to go, so let&amp;rsquo;s run it:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ python run.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; * Debug mode: on&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; * Running on http://127.0.0.1:5000/ &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For official documentation and more details on Flask, take a look &lt;a href=&#34;http://flask.palletsprojects.com/en/1.1.x/quickstart/#a-minimal-application&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;For those of you who need extra guidance, I highly recommend &lt;a href=&#34;https://www.youtube.com/watch?v=MwZwr5Tvyxo&amp;amp;list=PL-osiE80TeTs4UjLw5MM6OjgkjFeUxCYH&#34;&gt;this&lt;/a&gt; Flask tutorial series. (It&amp;rsquo;s free!)&lt;/p&gt;&#xA;&lt;h2 id=&#34;3-serve-the-app-but-how&#34;&gt;3. Serve the App, but How?&lt;/h2&gt;&#xA;&lt;p&gt;We have a very basic Flask application running, with the few lines of code we wrote above. If we are going to develop this baby Flask application into anything meaningful, we need to render pages on it (by pages, I mean blog posts, and not other pages; they can be written in pure HTML). There are a couple of ways to handle this task at hand. Something that would be a sensible thing to do for a dynamic website that needs to handle updates and deletes real-time would be to utilize Flask-SQLAlchemy, and use a relational database (such as SQLite). However, I was looking to do something a little bit different here. Since this website was meant to serve as a private online space, I figured that a relational database wasn&amp;rsquo;t necessary. Then I came across Flask-FlatPages, which made possible to serve static Markdown files to push onto the website. Setting up Flask-FlatPages shouldn&amp;rsquo;t be too difficult, let&amp;rsquo;s take a look.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;flask&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Flask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;url_for&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;flask_flatpages&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FlatPages&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Flask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#Initialize flatpages&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;flatpages&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FlatPages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#Configure FLATPAGES settings&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;DEBUG&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;FLATPAGES_AUTO_RELOAD&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DEBUG&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;FLATPAGES_EXTENSION&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;.md&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;FLATPAGES_ROOT&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;POST_DIR&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;posts&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@app.route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Yes! Just Cooked Some Flask!&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#Configure URL routing for each blog post by the name of Markdown file&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@app.route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/post/&amp;lt;name&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;POST_DIR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;post&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flatpages&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_or_404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#Note that we don&amp;#39;t have blog_post.html ready yet, so this won&amp;#39;t work yet!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render_template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;blog_post.html&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;debug&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code above should get your application ready to serve your Markdown files. All you need to do is put your Markdown files inside &lt;code&gt;./content/posts&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; content&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; posts&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ touch my_first_post.md&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ vim my_first_post.md&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Inside &lt;code&gt;my_first_post.md&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-md&#34; data-lang=&#34;md&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;author: Jeff Yang&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;title: My First Blog Post&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;published: 2019-10-26&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;### This is a Heading&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;This is my &lt;span class=&#34;gs&#34;&gt;**first**&lt;/span&gt; blog post.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&#34;ge&#34;&gt;Let&amp;#39;s Celebrate in a Blockquote!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;`def this_is_python_code():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;    print(&amp;#34;you can even write code!&amp;#34;)`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For further information on Markdown and its syntax, please check &lt;a href=&#34;https://www.markdownguide.org/cheat-sheet/&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Now we just need a HTML template to render this Markdown file into. As I mentioned above, Flask&amp;rsquo;s default templating engine Jinja2 is equipped with a simple but powerful &amp;ldquo;template inheritance&amp;rdquo; feature that makes life easy for us.&lt;/p&gt;&#xA;&lt;p&gt;One level under &lt;code&gt;my_flask_app&lt;/code&gt; directory, let&amp;rsquo;s create a directory called &lt;code&gt;template&lt;/code&gt; and create &lt;code&gt;layout.html&lt;/code&gt; and &lt;code&gt;blog_post.html&lt;/code&gt; inside it:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mkdir templates&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ touch layout.html&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ touch blog_post.html&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;layouthtml&#34;&gt;layout.html&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;My Flask App&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;meta&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;charset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;meta&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;width=device-width, initial-scale=1, shrink-to-fit=no&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;h1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ url_for(&amp;#39;home&amp;#39;) }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Home&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;h1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        {% block content %}{% endblock content %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;blog_posthtml&#34;&gt;blog_post.html&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% extends &amp;#34;layouts.html&amp;#34; %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% block content %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;h3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;strong&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;{{ post.title }}&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;strong&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;h3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Published: {{ post.published }}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    {{ post.html|safe }}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% endblock %} &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code for the HTML files above look a lot like pure HTML, but there are some oddities. Let&amp;rsquo;s take a look at some of the key features that the Jinja2 templating engine comes with.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;{% extends &amp;quot;layouts.html&amp;quot; %}&lt;/code&gt;: &lt;code&gt;blog_post.html&lt;/code&gt; is nested into &lt;code&gt;layouts.html&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;{% block content %}{% endblock content %}&lt;/code&gt;: specifies the location of where your nested html file should be added to&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;{{ post.published }}&lt;/code&gt;: renders metadata from your Markdown file&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;{{ post.html|safe }}&lt;/code&gt;: renders the content of your Markdown file&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Now your Markdown file content should be on your website, through the endpoint you configured before:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#Configure URL routing for each blog post by the name of Markdown file&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@app.route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/post/&amp;lt;name&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;POST_DIR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;post&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flatpages&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_or_404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#Now that we have blog_post.html ready to go, it should work!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render_template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;blog_post.html&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For more detailed explanation on templating, please check the &lt;a href=&#34;https://jinja.palletsprojects.com/en/2.10.x/&#34;&gt;documentation&lt;/a&gt; for Jinja2.&lt;/p&gt;&#xA;&lt;h2 id=&#34;4-want-to-insert-code-snippets-for-blog-posts&#34;&gt;4. Want to Insert Code Snippets for Blog Posts?&lt;/h2&gt;&#xA;&lt;p&gt;I knew from the moment I started building this website that I would need a way to insert code snippets inside a code block, with syntax highlighting. Using Wordpress, it was easy. I just had to install a plugin. Trying to add this feature to my own website, however, was a slightly different story. It&amp;rsquo;s extremely easy to find code snippets on the internet, they are on essentially every tech blog, we can see them on Stack Overflow, but it wasn&amp;rsquo;t as easy as I thought it would be to find exactly what I needed for this site. To save you from some trouble in case you are looking to add code snippets to your Flask application, here&amp;rsquo;s how to do it.&lt;/p&gt;&#xA;&lt;p&gt;We already have most of the tools needed to insert beautiful and customized code blocks into the blog posts. Flask-FlatPages comes with Markdown, which comes with an extension called &lt;em&gt;codehilite&lt;/em&gt; that supports syntax highlighting. However, we need to add some simple configurations to get it to work. We first need to install &lt;em&gt;Pygments&lt;/em&gt; (but we already installed it in the very first pip install execution we did in the beginning of this article). If you didn&amp;rsquo;t install &lt;em&gt;Pygments&lt;/em&gt;, go ahead and install it. Once &lt;em&gt;Pygments&lt;/em&gt; is installed, we need to do the following:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;flask&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Flask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;url_for&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;flask_flatpages&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FlatPages&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Flask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;flatpages&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FlatPages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;DEBUG&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;FLATPAGES_AUTO_RELOAD&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DEBUG&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;FLATPAGES_EXTENSION&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;.md&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;FLATPAGES_ROOT&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#You need the extensions below for your code snippet to be properly displayed&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;FLATPAGES_MARKDOWN_EXTENSIONS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;fenced_code&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;codehilite&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@app.route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Yes! Just Cooked Some Flask!&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#Configure URL routing for each blog post by the name of Markdown file&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@app.route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/post/&amp;lt;name&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;POST_DIR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;post&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flatpages&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_or_404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render_template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;blog_post.html&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To customize how your code block looks, do something like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pygmentize -L&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above code will print a list of built in themes available for your code blocks. To check out what the different themes look like (or customize the css based on your personal needs), run the following command inside &lt;code&gt;my_flask_app&lt;/code&gt; directory:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mkdir static&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; static&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pygmentize -S THEMENAMEOFYOURCHOICE -f html -a .codehilite &amp;gt; pygs.css&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should be able to see that a new pygs.css file is created inside the &lt;code&gt;static&lt;/code&gt; directory. It should surely be working now! Ah, wait a minute, we forgot to link this CSS file to our HTML templates.&lt;/p&gt;&#xA;&lt;h3 id=&#34;layoutshtml&#34;&gt;layouts.html&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;My Flask App&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;meta&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;charset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;meta&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;width=device-width, initial-scale=1, shrink-to-fit=no&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;link&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;rel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;text/css&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ url_for(&amp;#39;static&amp;#39;, filename=&amp;#39;pygs.css&amp;#39;) }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That should do it. We finally have the backbones of a functional personal blog built with Flask! Let&amp;rsquo;s do a short recap of what we went through.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Created a virtual environment before we started to install anything needed to build this blog&lt;/li&gt;&#xA;&lt;li&gt;Initialized a very basic Flask application&lt;/li&gt;&#xA;&lt;li&gt;Created simple HTML templates powered by Jinja2&lt;/li&gt;&#xA;&lt;li&gt;Went through how to serve static Markdown files through the HTML templates using Flask-Flatpages&lt;/li&gt;&#xA;&lt;li&gt;How to insert prettified code blocks into our blog posts through some simple configurations of FlatPages-Markdown-Codehilite-Pygments&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Congratulations! Now we are cookin&amp;rsquo; with Flask!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Shipping Code for a Business – Is Your Application Scalable?</title>
      <link>https://www.jeffyang.io/posts/knowru-internship-reflection/</link>
      <pubDate>Mon, 09 Sep 2019 00:00:00 +0000</pubDate>
      <guid>https://www.jeffyang.io/posts/knowru-internship-reflection/</guid>
      <category>Personal Reflection</category>
      <description>&lt;p&gt;&lt;em&gt;Reflecting on the 2019 summer internship at Knowru&lt;/em&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&#34;https://www.jeffyang.io/posts/knowru-internship-reflection/knowru_internship_cover_hu_c49ee3ccdb16cc08.webp&#34; srcset=&#34;https://www.jeffyang.io/posts/knowru-internship-reflection/knowru_internship_cover_hu_b3361c6a16c03250.webp 640w, https://www.jeffyang.io/posts/knowru-internship-reflection/knowru_internship_cover_hu_c49ee3ccdb16cc08.webp 1280w&#34; sizes=&#34;(max-width: 660px) 100vw, 620px&#34; width=&#34;1280&#34; height=&#34;720&#34; alt=&#34;&#34; loading=&#34;eager&#34; decoding=&#34;async&#34;&gt;&lt;/figure&gt;&#xA;&#xA;&lt;h3 id=&#34;reflecting-on-2019-summer-internship-knowru&#34;&gt;Reflecting on 2019 Summer Internship @Knowru&lt;/h3&gt;&#xA;&lt;p&gt;This summer, I had the privilege of returning to and working at Knowru, a startup that primarily focuses on helping loan processing companies through a suite of innovative software. I had worked at Knowru in the summer of 2017 before enlisting in the Korean Army, and over the next 2 years of my military service, the company saw remarkable growth (most notably, the backing of a multimillion-dollar investment from Hong Kong). Back at Knowru as a software engineering intern, I had to navigate through problems that came from the company’s rapidly growing customer-base: writing code with scalability in mind.&lt;/p&gt;&#xA;&lt;p&gt;Our team was tasked with the development of a web application that would allow loan processing companies access to foolproof verification of their customers (i.e. loan applicants). How? Our application would connect the loan agent and the customer through a video call, allow access (with agreement) to the GPS location of the customer, enable the loan agent to request certain photos, and provide agents a space to ask questions pertinent to determining the validity of a loan application. We built the prototype and sent it off to a Chinese micro-lending company for testing, and we waited—a few days later, we heard the ring of a message on Slack. (As we are software engineers working in office, it is often difficult to grasp the bigger picture, especially if there is a lack of knowledge on the business-side of the industry—therefore, such insight from testing is imperative). We were asked to re-design the application to support multiple companies and multiple loan products, as the application was only suitable for use by a single company.&lt;/p&gt;&#xA;&lt;p&gt;As our lead frontend developer left the company soon after I joined, I became the main frontend engineer on the project. I discussed with the backend developer on how to best solve the problem. Our application was working like a charm—but only if there was a single company using the software. The code we wrote was simply not scalable, and from a business perspective, this was the prototype’s biggest flaw. Various companies would want varying fields for applicant’s personal information, request fields for different kinds of photos, and want their loan agents to ask different sets of questions to their customers. In other words, we needed to dynamically render the agent-side page of our web application so that it would suit each company’s wants and needs. I actively took advantage of the flexibility that React/Redux provided.&lt;/p&gt;&#xA;&lt;p&gt;Through an API request, fetch JSON data containing specifications needed to dynamically render the page&#xA;Store the specifications in the state of the application (Redux)&#xA;For each React component, fetch necessary data needed and pass it as a prop&#xA;Now we are fully equipped to render any page / component dynamically as long as we have the specifications provided by the company!&lt;/p&gt;&#xA;&lt;p&gt;With the above, our application was now able to support any number of companies and loan products, just as long as we had the information provided by each company on what they wanted included in their own customized web application. The re-designing process was not too difficult or complicated, but I realized how important thinking out the design of the software is, especially when shipping code for business purposes. At the end, software engineers exist to solve complicated technical problems, and building a scalable product is integral for the final product to have a greater impact on the world.&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>
