<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Danielle&apos;s Blog</title><description>Danielle&apos;s Blog</description><link>https://danielleheberling.xyz/</link><item><title>I Rewrote My Step Function as a Durable Function</title><link>https://danielleheberling.xyz/blog/durable-functions/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/durable-functions/</guid><description>My journey rewriting and old project to learn about Lambda durable functions</description><pubDate>Sun, 01 Mar 2026 12:12:03 GMT</pubDate><content:encoded>&lt;p&gt;Ever since AWS announced &amp;lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Lambda durable functions&amp;lt;/a&amp;gt; at re:Invent 2025, I&apos;ve been wanting to try them on a real project. Not a cookie cutter tutorial, but something where I could actually compare the two approaches. I already had the perfect candidate: my &amp;lt;a href=&quot;https://github.com/deeheber/weather-site&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;weather site&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;h2&gt;Quick Background&lt;/h2&gt;
&lt;p&gt;If you haven&apos;t read &lt;a href=&quot;/blog/serverless-weather-reporting/&quot;&gt;my original blog post&lt;/a&gt;, here&apos;s the short version. I built &amp;lt;a href=&quot;https://isitsnowinginhillsboro.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;isitsnowinginhillsboro.com&amp;lt;/a&amp;gt; because the existing Portland snow site wasn&apos;t accurate for my area. It&apos;s a serverless workflow that checks the &amp;lt;a href=&quot;https://openweathermap.org/api&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;OpenWeatherMap API&amp;lt;/a&amp;gt; every 10 minutes and updates a static S3 site with a YES or NO answer. The original uses a Step Function triggered by EventBridge Scheduler.&lt;/p&gt;
&lt;h2&gt;Why Rewrite It?&lt;/h2&gt;
&lt;p&gt;I think a lot of us have been wondering when you&apos;d pick durable functions over Step Functions. This project was small enough to rewrite quickly, but complex enough to exercise real patterns like API calls, branching logic, parallel execution, and multiple AWS services. Building the same thing both ways seemed like the best way to form my own opinion.&lt;/p&gt;
&lt;p&gt;The durable function version is &amp;lt;a href=&quot;https://github.com/deeheber/durable-function-weather-site&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here on GitHub&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;h2&gt;How the Two Approaches Compare&lt;/h2&gt;
&lt;p&gt;The workflow logic is identical in both versions. Same end result. But the developer experience is pretty different.&lt;/p&gt;
&lt;h3&gt;Step Functions&lt;/h3&gt;
&lt;p&gt;You define your workflow as a state machine, chaining together states like &lt;code&gt;LambdaInvoke&lt;/code&gt;, &lt;code&gt;Choice&lt;/code&gt;, &lt;code&gt;Pass&lt;/code&gt;, and direct SDK integrations in CDK. The visual representation in the console is nice for understanding the flow at a glance, and it feels very &quot;managed&quot; in the best sense. The service handles state tracking, retries, and transitions for you.&lt;/p&gt;
&lt;p&gt;The tradeoff is that CDK code for Step Functions can get verbose. Passing data between states and setting up error handling means understanding how Step Functions manages its JSON payload. You&apos;re giving up some control for the convenience of the service doing the heavy lifting.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/step-function-view.png&quot; alt=&quot;step function view&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Durable functions&lt;/h3&gt;
&lt;p&gt;With durable functions, you write your workflow as plain TypeScript. Wrap your logic in &lt;code&gt;ctx.step()&lt;/code&gt; calls, use regular &lt;code&gt;if&lt;/code&gt; statements for branching, &lt;code&gt;ctx.parallel()&lt;/code&gt; for parallel execution. Need to call an AWS service? Just use the SDK like you normally would inside a step.&lt;/p&gt;
&lt;p&gt;Here&apos;s the thing that surprised me: this version felt more natural to write. I wasn&apos;t thinking about &quot;states&quot; or &quot;transitions.&quot; I was just writing code. Since it&apos;s TypeScript with a thin SDK wrapper, it also feels more portable. If another cloud provider or open source project adopted a similar checkpoint/replay pattern, the mental model (and probably a good chunk of the code) would transfer over.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/durable-function-view.png&quot; alt=&quot;durable function view&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Side by Side&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Step Functions&lt;/th&gt;
&lt;th&gt;Durable functions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Workflow definition&lt;/td&gt;
&lt;td&gt;JSON/YAML state machine (ASL)&lt;/td&gt;
&lt;td&gt;Plain TypeScript code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State management&lt;/td&gt;
&lt;td&gt;Managed by the service&lt;/td&gt;
&lt;td&gt;Automatic checkpointing via SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS service calls&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CallAwsService&lt;/code&gt; task or direct integration&lt;/td&gt;
&lt;td&gt;Regular AWS SDK calls inside &lt;code&gt;ctx.step()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP calls&lt;/td&gt;
&lt;td&gt;&lt;code&gt;HttpInvoke&lt;/code&gt; task + Connection resource&lt;/td&gt;
&lt;td&gt;Standard &lt;code&gt;fetch()&lt;/code&gt; inside &lt;code&gt;ctx.step()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditional logic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Choice&lt;/code&gt; state&lt;/td&gt;
&lt;td&gt;Plain &lt;code&gt;if&lt;/code&gt; statement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parallel execution&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Parallel&lt;/code&gt; state&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ctx.parallel([...])&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduling&lt;/td&gt;
&lt;td&gt;EventBridge Scheduler -&amp;gt; Step Function&lt;/td&gt;
&lt;td&gt;EventBridge Scheduler -&amp;gt; Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure&lt;/td&gt;
&lt;td&gt;State Machine + Connection + Lambda(s)&lt;/td&gt;
&lt;td&gt;Single Lambda function&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugging&lt;/td&gt;
&lt;td&gt;Step Function execution history (visual)&lt;/td&gt;
&lt;td&gt;Durable execution history&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;The Gotcha That Got Me&lt;/h2&gt;
&lt;p&gt;Durable functions require a &lt;strong&gt;qualified ARN&lt;/strong&gt; for invocation, meaning a published version or alias. When I first wired up EventBridge Scheduler, I was using the unqualified ARN and couldn&apos;t figure out why it wasn&apos;t working.&lt;/p&gt;
&lt;p&gt;The issue? The CDK &lt;code&gt;LambdaInvoke&lt;/code&gt; scheduler target uses &lt;code&gt;functionArn&lt;/code&gt; under the hood, which for a plain &lt;code&gt;Function&lt;/code&gt; is the unqualified ARN — no version or alias suffix. Durable functions reject unqualified ARNs entirely.&lt;/p&gt;
&lt;p&gt;What ended up working was creating a Lambda alias in CDK and passing that to the scheduler target instead. Since an alias implements &lt;code&gt;IFunction&lt;/code&gt;, the construct picks up its qualified ARN automatically.&lt;/p&gt;
&lt;p&gt;If you&apos;re getting started with durable functions and things aren&apos;t behaving as expected, check your invocation ARN first.&lt;/p&gt;
&lt;h2&gt;So Which One Should You Use?&lt;/h2&gt;
&lt;p&gt;In my opinion, both approaches are solid for a workflow like this. It really comes down to developer experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you prefer writing workflows as code&lt;/strong&gt;, durable functions are going to feel great. The code reads like a normal application, the infrastructure footprint is smaller, and the patterns are more transferable if you ever need to move beyond AWS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you value visual debugging and a fully managed experience&lt;/strong&gt;, Step Functions has the edge right now. The console gives you a step-by-step view of every execution with clear status indicators. Durable functions have a visual for execution history too, but I prefer how Step Functions handles it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you need Distributed Map for massive fan-out scenarios&lt;/strong&gt; (up to 10,000 concurrent executions) or rely heavily on Step Functions&apos; 220+ native service integrations, Step Functions is the better option.&lt;/p&gt;
&lt;p&gt;AWS also has &amp;lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/durable-step-functions.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;official guidance&amp;lt;/a&amp;gt;. One thing I found interesting is they mention that hybrid architectures are totally valid, using durable functions for application-level logic while Step Functions handles higher-level cross-service coordination.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;If you&apos;re curious about durable functions, I&apos;d encourage trying it with a project you already have. You&apos;ll learn way more than a tutorial can teach you.&lt;/p&gt;
&lt;p&gt;Both repos are open source:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Step Functions version:&lt;/strong&gt; &amp;lt;a href=&quot;https://github.com/deeheber/weather-site&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;github.com/deeheber/weather-site&amp;lt;/a&amp;gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Durable functions version:&lt;/strong&gt; &amp;lt;a href=&quot;https://github.com/deeheber/durable-function-weather-site&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;github.com/deeheber/durable-function-weather-site&amp;lt;/a&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&apos;d love to hear from anyone else who&apos;s been experimenting with durable functions. Let me know what your experience has been! 🚀&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Let an AI Agent Do Your Job Searching</title><link>https://danielleheberling.xyz/blog/job-search-agent/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/job-search-agent/</guid><description>An AI agent that monitors your dream companies for open roles and emails you the results.</description><pubDate>Sun, 08 Feb 2026 09:12:03 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@markuswinkler?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Markus Winkler&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/a-typewriter-with-a-job-application-printed-on-it-XKKuY4ottJ0?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;/assets/job-application-typewriter.jpg&quot; alt=&quot;Job Application Typewriter&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quick disclaimer before I get into this: I&apos;m no longer actively job hunting. I started building this project during my last job search and decided to finish what I started. Partly because I wanted to learn more about agents and partly because I think it might be useful for someone else out there. With that out of the way, here&apos;s the story.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Ever since I started my last job search, one thing kept bugging me. I had a list of &quot;dream companies&quot; that I wanted to work at, and I&apos;d manually go check their career pages every few days to see if anything new popped up. Sometimes daily. Sometimes I&apos;d forget for a week and miss a posting entirely.&lt;/p&gt;
&lt;p&gt;Here&apos;s the thing: job searching is already stressful enough without having to remember to check 10+ career pages on a regular cadence. I&apos;d open a browser tab, search around, get distracted by Slack or email, and then forget where I left off. Multiply that by several companies and it becomes a real time sink.&lt;/p&gt;
&lt;p&gt;I kept thinking there has to be a better way. What if something could just watch those companies for me in the background and send me an email when there&apos;s an opening that matches what I&apos;m looking for?&lt;/p&gt;
&lt;p&gt;That&apos;s when I decided to build it myself.&lt;/p&gt;
&lt;h2&gt;What I Built&lt;/h2&gt;
&lt;p&gt;The &amp;lt;a href=&quot;https://github.com/deeheber/job-search-agent&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;job-search-agent&amp;lt;/a&amp;gt; is an AI agent built with &amp;lt;a href=&quot;https://strandsagents.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Strands Agents&amp;lt;/a&amp;gt; and deployed to &amp;lt;a href=&quot;https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agents-tools-runtime.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Amazon Bedrock AgentCore Runtime&amp;lt;/a&amp;gt;. You give it a company name (and optionally a job title or location filter), and it searches the web for open positions at that company. Then it returns what it finds with links to the actual job postings.&lt;/p&gt;
&lt;p&gt;You can also set it up to run on a schedule and optionally send you email alerts when a company is hiring. That&apos;s the part I was most excited about.&lt;/p&gt;
&lt;h2&gt;Design Decisions&lt;/h2&gt;
&lt;p&gt;Here&apos;s how the architecture works at a high level:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/job-search-architecture.png&quot; alt=&quot;Job Search Architecture&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EventBridge Scheduler&lt;/strong&gt; kicks things off on whatever cadence you configure. This triggers the agent running in &lt;strong&gt;AgentCore Runtime&lt;/strong&gt;, which is where the Strands agent lives. The agent does its thing (searching for jobs at the companies you&apos;ve specified), and if you&apos;ve provided one or more email addresses in the environment variables and there is a job posting, it sends the results via &lt;strong&gt;SNS&lt;/strong&gt; as an email notification.&lt;/p&gt;
&lt;p&gt;The infrastructure is all defined in CDK (TypeScript), and the agent code is Python. I talked about why I use that particular combo in my &amp;lt;a href=&quot;https://danielleheberling.xyz/blog/strands-agent-template/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;strands-agent-template post&amp;lt;/a&amp;gt;, but the short version is that each framework is strongest in its native language right now.&lt;/p&gt;
&lt;p&gt;Is this a bit over-engineered for what it does? Most likely. But I wanted to use the opportunity to build something I&apos;d actually use while learning about agents and AgentCore. In my experience, the best way to learn new tech is to solve a real problem with it, even if the solution is fancier than it needs to be.&lt;/p&gt;
&lt;h2&gt;Challenges&lt;/h2&gt;
&lt;p&gt;When I first started building this, I took what felt like the simplest approach. I put instructions in the agent&apos;s system prompt to construct the career page URLs for each company. Something along the lines of &quot;for company X, try going to company.com/careers and look for job postings.&quot;&lt;/p&gt;
&lt;p&gt;This didn&apos;t work well at all.&lt;/p&gt;
&lt;p&gt;The agent would guess at URLs that didn&apos;t exist, hallucinate job listings, and burn through a ton of tokens trying to figure out where the careers page actually was. Some companies have their postings on Greenhouse. Others use Lever, Ashby, or their own custom systems. There&apos;s no standard for this, and asking an LLM to guess the right URL is a recipe for frustration.&lt;/p&gt;
&lt;p&gt;So I decided to build a tool for the agent to use instead. During that research, I came across &amp;lt;a href=&quot;https://www.tavily.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Tavily&amp;lt;/a&amp;gt;, which is a search API built specifically for AI agents. Even better, Strands already had a pre-built Tavily tool available. I plugged it in, and the difference was night and day. Instead of guessing URLs, the agent now searches the web for actual career pages and job boards, then extracts the relevant information from those results.&lt;/p&gt;
&lt;p&gt;This was a good reminder for me that agents are only as useful as the tools you give them. The LLM is great at reasoning and formatting responses, but it shouldn&apos;t be doing the heavy lifting of web searching through prompt instructions alone.&lt;/p&gt;
&lt;p&gt;There was one more quirk worth mentioning. EventBridge Scheduler supports &amp;lt;a href=&quot;https://docs.aws.amazon.com/scheduler/latest/UserGuide/managing-targets-universal.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;universal targets&amp;lt;/a&amp;gt;, which let you call pretty much any AWS API action directly without needing a Lambda function in between. You just give it the service ARN and the request payload, and it makes the API call for you. There&apos;s a &amp;lt;a href=&quot;https://docs.aws.amazon.com/scheduler/latest/UserGuide/managing-targets-universal.html#unsupported-api-actions&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;list of unsupported action prefixes&amp;lt;/a&amp;gt; in the docs (things like &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;describe&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;). Interestingly, &lt;code&gt;invokeModel&lt;/code&gt; is explicitly blocked, but &lt;code&gt;invokeAgentRuntime&lt;/code&gt; is not.&lt;/p&gt;
&lt;p&gt;So I set up EventBridge Scheduler to call the Bedrock AgentCore &lt;code&gt;invokeAgentRuntime&lt;/code&gt; action on a schedule. And it works. The agent gets invoked, does its thing, and returns results. But for some reason, EventBridge Scheduler &lt;em&gt;thinks&lt;/em&gt; the invocation failed. Every single time.&lt;/p&gt;
&lt;p&gt;If you leave retries enabled, the scheduler will retry the invocation multiple times even though the first call succeeded. That means your agent runs (and costs you money) three or four times instead of once. Also you may get duplicate email notifications. Not great.&lt;/p&gt;
&lt;p&gt;My workaround was to disable retries on the scheduler and set up a Dead Letter Queue (DLQ) to capture these &quot;failed&quot; invocations so I can monitor them. I&apos;m honestly not sure if this is something I&apos;m doing wrong or if it&apos;s a quirk on the AWS side that&apos;ll get fixed eventually. AgentCore is still pretty new, so it&apos;s possible this just hasn&apos;t been ironed out yet. If you&apos;ve run into something similar, I&apos;d love to hear about it.&lt;/p&gt;
&lt;h2&gt;The Outcome&lt;/h2&gt;
&lt;p&gt;After getting Tavily integrated, the agent started returning real job listings with real links. I set up EventBridge to run the searches on a schedule and configured SNS to email me the results.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/job-search-email.png&quot; alt=&quot;Job Search Results Email&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The time savings were immediately obvious. Instead of spending 30+ minutes bouncing between career pages every few days, I&apos;d get an email in my inbox with a summary of who&apos;s hiring and links directly to the postings. Even when nothing new showed up, it was nice to not wonder if I&apos;d missed something.&lt;/p&gt;
&lt;h2&gt;A Note on Cost&lt;/h2&gt;
&lt;p&gt;If you decide to deploy this yourself, keep in mind that it will cost you money. Specifically:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bedrock usage&lt;/strong&gt; is the main cost driver here. Every time the agent runs, it&apos;s making calls to a foundation model, and those tokens add up. How much depends on how many companies you&apos;re monitoring and how often the scheduler runs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tavily&lt;/strong&gt; has a free tier that&apos;s pretty generous for personal use, but if you&apos;re running searches frequently or monitoring a lot of companies, you could exceed it. Keep an eye on your usage.&lt;/p&gt;
&lt;p&gt;I&apos;d recommend starting with a low frequency schedule (maybe once a day or even once a week) and a small list of companies to get a feel for the costs before scaling up.&lt;/p&gt;
&lt;p&gt;Everything else in the stack (AgentCore Runtime, EventBridge Scheduler, SNS) will likely fall within the AWS free tier for personal use. But &quot;likely&quot; isn&apos;t a guarantee, so check the &amp;lt;a href=&quot;https://aws.amazon.com/pricing/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;pricing pages&amp;lt;/a&amp;gt; against the usage you expect before deploying.&lt;/p&gt;
&lt;h2&gt;What I Learned&lt;/h2&gt;
&lt;p&gt;I think the biggest takeaway from this project is that building something you actually need is still the best way to learn new tech. I could have followed a tutorial to build a generic chatbot, but because this was solving a real problem for me, I was way more motivated to push through the frustrating parts.&lt;/p&gt;
&lt;p&gt;If you&apos;re curious about agents but not sure where to start, I&apos;d suggest finding a small, annoying, repetitive task in your life and trying to automate it. It doesn&apos;t have to be fancy. The learning happens in the process of figuring out what works and what doesn&apos;t.&lt;/p&gt;
&lt;h2&gt;Give It a Try&lt;/h2&gt;
&lt;p&gt;The full source code is on &amp;lt;a href=&quot;https://github.com/deeheber/job-search-agent&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;GitHub&amp;lt;/a&amp;gt;. It includes instructions for running locally and deploying to AWS.&lt;/p&gt;
&lt;p&gt;If you&apos;re currently job hunting and this looks useful, take it for a spin. If you find bugs or have ideas for improvements, open an issue or submit a PR. I&apos;m curious to hear what other folks build with it.&lt;/p&gt;
&lt;p&gt;Good luck out there 💪&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>A Strands Agent Template (For the Impatient)</title><link>https://danielleheberling.xyz/blog/strands-agent-template/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/strands-agent-template/</guid><description>A Template for Deploying Strands Agents on AWS</description><pubDate>Fri, 23 Jan 2026 10:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/puzzle.jpg&quot; alt=&quot;Puzzle&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@ttepavac?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Tanja Tepavac&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/a-piece-of-a-puzzle-with-a-missing-piece-cWMhxNmQVq0?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ever since AWS announced &amp;lt;a href=&quot;https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agents-tools-runtime.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Bedrock AgentCore Runtime&amp;lt;/a&amp;gt; back in July 2025, I&apos;ve been experimenting with deploying agents to production. The hype around &quot;agentic AI&quot; is everywhere, but most examples I found were either too simple or way too complex for what I actually needed.&lt;/p&gt;
&lt;p&gt;Here&apos;s what I wanted 👉 a way to go from zero to a deployed agent on AWS without spending three days on infrastructure setup. So I built a template that does exactly that.&lt;/p&gt;
&lt;h2&gt;What I Built&lt;/h2&gt;
&lt;p&gt;The &amp;lt;a href=&quot;https://github.com/deeheber/strands-agent-template&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;strands-agent-template&amp;lt;/a&amp;gt; repo is a GitHub template that gives you everything you need to deploy a &amp;lt;a href=&quot;https://strandsagents.com/latest/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Strands agent&amp;lt;/a&amp;gt; to Amazon Bedrock AgentCore Runtime. Click &quot;Use this template&quot; and you&apos;re most of the way there.&lt;/p&gt;
&lt;p&gt;What&apos;s included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ Production-ready CDK infrastructure with IAM, logging, and tracing&lt;/li&gt;
&lt;li&gt;✅ Local development setup so you can test before deploying&lt;/li&gt;
&lt;li&gt;✅ GitHub Actions for CI/CD&lt;/li&gt;
&lt;li&gt;✅ OpenTelemetry observability built in&lt;/li&gt;
&lt;li&gt;✅ Testing, linting, and formatting configured&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why Python + TypeScript?&lt;/h2&gt;
&lt;p&gt;I went with Python for the agent code and TypeScript for infrastructure. This isn&apos;t because I love context switching (I don&apos;t), but because each framework is strongest in its native language. Strands has great Python support and documentation, while CDK is cleanest in TypeScript.&lt;/p&gt;
&lt;p&gt;As these frameworks mature, I might consolidate to one language. But for now, this combo gives you access to the best libraries and examples for both.&lt;/p&gt;
&lt;h2&gt;Quick Start&lt;/h2&gt;
&lt;p&gt;If you just want to see it work after setting up the AWS CLI and configuring credentials:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Test locally
cd agent &amp;amp;&amp;amp; source .venv/bin/activate &amp;amp;&amp;amp; python src/agentcore_app.py
# Test in another terminal
curl -X POST http://localhost:8080/invocations -H &quot;Content-Type: application/json&quot; -d &apos;{&quot;prompt&quot;: &quot;What is 42 * 137?&quot;}&apos;
# Deploy to AWS
cd cdk &amp;amp;&amp;amp; npm install &amp;amp;&amp;amp; npm run build &amp;amp;&amp;amp; npm run cdk:deploy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The template comes with a basic example agent. Replace it with your own logic and you&apos;re done. Under 10 minutes from clone to deployed.&lt;/p&gt;
&lt;h2&gt;What You Actually Get&lt;/h2&gt;
&lt;p&gt;Here&apos;s what stood out to me after using this in a few projects.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Local testing that doesn&apos;t lie.&lt;/strong&gt; You can run your agent locally before deploying anything to AWS. This matters more than you&apos;d think. I&apos;ve wasted too much time debugging issues that only showed up after deployment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Observability without the extra work.&lt;/strong&gt; CloudWatch logs and OpenTelemetry tracing are configured out of the box. When something breaks (and it will), you can actually figure out why.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CI/CD that works.&lt;/strong&gt; GitHub Actions run your tests on every push. Both Python and TypeScript tooling is configured. It catches the obvious stuff before you deploy.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;If You&apos;re Just Getting Started&lt;/h2&gt;
&lt;p&gt;If this is your first time deploying an agent to AWS, here&apos;s what I&apos;d focus on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start by running the example locally. Don&apos;t deploy anything yet. Get familiar with how Strands agents work.&lt;/li&gt;
&lt;li&gt;Then make a tiny change to the agent logic. See how it behaves. Break something on purpose and watch what happens.&lt;/li&gt;
&lt;li&gt;Only then deploy to AWS. Use the CloudWatch dashboard to see what your agent is actually doing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The template includes detailed deployment docs in the DEPLOYMENT.md file. I tried to write them for someone who hasn&apos;t used CDK or AgentCore before.&lt;/p&gt;
&lt;h2&gt;What&apos;s Next&lt;/h2&gt;
&lt;p&gt;I&apos;m actively using this template for a few projects, so it&apos;ll keep evolving based on what I learn. If you find gaps or have suggestions, open an issue or submit a PR. The CONTRIBUTING.md file has guidelines.&lt;/p&gt;
&lt;p&gt;This is meant to be a starting point, not a finished product. Take it, modify it, make it yours.&lt;/p&gt;
&lt;p&gt;You can find the template on &amp;lt;a href=&quot;https://github.com/deeheber/strands-agent-template&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;GitHub&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;Let me know if you use it. I&apos;m curious what works and what doesn&apos;t for other folks trying to deploy agents on AWS.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Use Social Media Mindfully</title><link>https://danielleheberling.xyz/blog/mindful-social-media/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/mindful-social-media/</guid><description>Danielle&apos;s approach to social media in 2026</description><pubDate>Sat, 17 Jan 2026 10:12:03 GMT</pubDate><content:encoded>&lt;p&gt;I quit Facebook in 2020 when a former coworker was spreading misinformation about what was happening in Portland, OR. He&apos;d never been there and had no plans to visit. I was literally living in Portland at the time, telling him what I was seeing firsthand, but that didn&apos;t matter to him. That was it for me. I miss it sometimes, but mostly I don&apos;t.&lt;/p&gt;
&lt;p&gt;Here&apos;s what I&apos;ve noticed since then: the heyday of social media feels like it&apos;s behind us. In my opinion, Facebook peaked in 2008. Back then, it was about connecting with friends, sharing actually interesting updates about our lives. Minimal ads. It felt genuine.&lt;/p&gt;
&lt;p&gt;Now? Wannabe influencers everywhere. More ads and brand accounts in your timeline than content from people you actually know. Bots running campaigns to get engagement through false things or distortions of reality. It&apos;s exhausting.&lt;/p&gt;
&lt;p&gt;But here&apos;s the thing: I&apos;m not saying abandon social media entirely. I&apos;m saying use it differently.&lt;/p&gt;
&lt;h2&gt;How I Think About Social Media Now&lt;/h2&gt;
&lt;p&gt;I&apos;m not scrolling feeds endlessly anymore. No traps of getting lost in reels or stories. I use &amp;lt;a href=&quot;https://buffer.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Buffer&amp;lt;/a&amp;gt; to schedule posts, which keeps me from even looking at a timeline. I check in with intention when I need to, then I&apos;m out. This one&apos;s harder than it sounds, but it makes a real difference in how much time you lose to these platforms.&lt;/p&gt;
&lt;p&gt;With that said, social media still works for connections. DMs are good. Having actual conversations in comments is good. Longer discussions where you&apos;re genuinely exchanging ideas? Even better. This is where I think the platforms still have value if you&apos;re intentional about it.&lt;/p&gt;
&lt;p&gt;I try to share things that might help someone else. Good articles I&apos;ve read. Things I&apos;m learning. Mistakes I&apos;ve made. If it could save one person some time or frustration, it&apos;s worth sharing. The stuff you&apos;ve learned the hard way, the patterns you&apos;re seeing in your day job...not to build a personal brand or chase engagement metrics, but because someone else is probably dealing with the same problems.&lt;/p&gt;
&lt;p&gt;If you&apos;re job hunting, LinkedIn especially can help you connect with the right people. It&apos;s not your whole career strategy, but it&apos;s a useful tool when you need it.&lt;/p&gt;
&lt;p&gt;Here&apos;s where I think we&apos;ve lost the plot though: we&apos;ve forgotten that coffee with friends to catch up beats any social media interaction. Travel somewhere to see people you care about. Those face-to-face conversations are what actually matter.&lt;/p&gt;
&lt;h2&gt;What This Actually Looks Like&lt;/h2&gt;
&lt;p&gt;For me, this means I spend my time learning, reading, and building things. When I do post, it&apos;s usually because I want to hear what other people think about something I&apos;m working through. Or I&apos;ve hit a problem that took me way too long to solve and I figure sharing it might save someone else the trouble.&lt;/p&gt;
&lt;p&gt;I&apos;m not trying to go viral. I&apos;m not optimizing for engagement. I&apos;m definitely not checking how many likes something got.&lt;/p&gt;
&lt;p&gt;The goal is simple: be helpful. Connect with people who are thinking about similar problems. Have real conversations, even if they happen in threads or comment sections. That&apos;s what matters and what I&apos;m focused on in 2026.&lt;/p&gt;
&lt;p&gt;Spend your time learning, reading, and building things. Use social media when it serves a purpose. Skip it when it doesn&apos;t.&lt;/p&gt;
&lt;p&gt;What&apos;s your approach? I&apos;d be curious to hear how other people are thinking about this stuff.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>How You Do It Matters</title><link>https://danielleheberling.xyz/blog/how-matters/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/how-matters/</guid><description>How You Do It Matters</description><pubDate>Wed, 19 Nov 2025 15:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/winding-path.jpg&quot; alt=&quot;Winding path&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@jack_anstey?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Jack Anstey&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/aerial-photography-of-road-zS4lUqLEiNA?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Looking back on my career, the connections and opportunities I value most weren&apos;t the result of optimizing for speed. They grew from taking the long view.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;How&lt;/em&gt; you do it matters just as much as &lt;em&gt;what&lt;/em&gt; you do.&lt;/p&gt;
&lt;p&gt;Take professional networking, for example. I&apos;ve found that showing up consistently to community events, contributing even when I don&apos;t need anything, has led to the most meaningful connections and opportunities. That might look like speaking at a meetup, volunteering to help organize one, reviewing someone&apos;s resume, or answering career questions in Slack. Similarly, I&apos;m drawn to organizations where solving real user problems is at the heart of what we build.&lt;/p&gt;
&lt;p&gt;I once worked at a startup where the CEO&apos;s departing words really stuck with me. This man was far from perfect, but I enjoyed working with him professionally and did it twice, mostly because of our alignment on what matters.&lt;/p&gt;
&lt;p&gt;Here&apos;s what he said upon his departure.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As you continue to grow without me, I&apos;d ask you to keep one thing in mind: the way we do it matters. We haven&apos;t gotten here because we&apos;re the cleverest copywriters, the fastest programmers, or the slickest sales people. We don&apos;t work sixteen hour days, and we&apos;re not ruthless or cutthroat. We&apos;re kind. We care about each other. We bring out the best of each other&apos;s strengths, and we bridge each other&apos;s weaknesses. We create opportunities to experiment and take risks. We&apos;re humble. And we&apos;re better for it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Of course, pragmatic tradeoffs are part of the job. I can move fast when needed, whether that&apos;s shipping a quick fix to unblock progress or meeting a tight deadline. The key is being intentional: planning to address the root cause rather than letting temporary solutions pile up. It&apos;s about being honest with yourself and your team about what you&apos;re doing and why.&lt;/p&gt;
&lt;p&gt;In an age of viral videos for quick wins, I know it can be challenging to play the long game...but I encourage you to consider it.&lt;/p&gt;
&lt;p&gt;This approach has led me to opportunities and relationships I wouldn&apos;t trade for anything. If you&apos;re wondering whether it&apos;s worth it to take a more intentional approach - I&apos;d say give it a shot and see where it takes you.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Learning Outside Your Specialty | Why I Got a Kubernetes Cert</title><link>https://danielleheberling.xyz/blog/why-kubernetes/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/why-kubernetes/</guid><description>On learning K8s as a serverless developer and why specialization doesn&apos;t mean staying in one lane forever</description><pubDate>Sun, 12 Oct 2025 10:12:03 GMT</pubDate><content:encoded>&lt;p&gt;Earlier this month I took the exam for the &amp;lt;a href=&quot;https://training.linuxfoundation.org/certification/kubernetes-cloud-native-associate/&quot; target=&quot;_blank&quot;&amp;gt;Kubernetes and Cloud Native Associate (KCNA) certification&amp;lt;/a&amp;gt; and passed.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;flex justify-center&quot;&amp;gt;
&amp;lt;div data-iframe-width=&quot;150&quot; data-iframe-height=&quot;270&quot; data-share-badge-id=&quot;f26feb24-7931-4b02-b6cc-c60c77a24f83&quot; data-share-badge-host=&quot;https://www.credly.com&quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;script type=&quot;text/javascript&quot; async src=&quot;//cdn.credly.com/assets/utilities/embed.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;After years of working in AWS Serverless, this raised some eyebrows. People asked: why?&lt;/p&gt;
&lt;p&gt;Here&apos;s the honest answer: I&apos;ve passed on applying to jobs because I didn&apos;t know Kubernetes (K8s). That needed to change.&lt;/p&gt;
&lt;h2&gt;The Reality of Job Postings&lt;/h2&gt;
&lt;p&gt;Look at DevOps and platform engineering roles today. Many of them want AWS experience, serverless knowledge, AND K8s expertise. Not all, but enough that it&apos;s a pattern.&lt;/p&gt;
&lt;p&gt;Specializing deeply in serverless has been valuable for my career. But at some point, gaps in your knowledge start closing doors. When I&apos;m scanning job postings and thinking &quot;I could do this role, except for that one requirement,&quot; it&apos;s time to consider filling the gap.&lt;/p&gt;
&lt;p&gt;This isn&apos;t about chasing every new technology. It&apos;s about recognizing when a skill has become common enough in your field that not having it limits options.&lt;/p&gt;
&lt;h2&gt;What &quot;Best Tool for the Job&quot; Actually Means&lt;/h2&gt;
&lt;p&gt;I still believe in &quot;serverless first.&quot; But here&apos;s what I&apos;ve learned: the best tool depends on context.&lt;/p&gt;
&lt;p&gt;At the end of the day, users care that what you build is reliable, works, and solves their problem. Unless they&apos;re an engineer or tech geek, they don&apos;t care if you used K8s or serverless. They care about the outcome.&lt;/p&gt;
&lt;p&gt;Tech decisions are made by people. People make choices based on their experiences and what they know. A lot of teams use K8s. Whether they &quot;need&quot; it is a value judgment that depends entirely on your perspective and experience. That&apos;s not what this post is about.&lt;/p&gt;
&lt;p&gt;What I&apos;ve realized: if I&apos;m working with a team that&apos;s chosen K8s, fighting against that to use serverless creates friction. Understanding their world helps me work with them effectively, even if I&apos;d make different choices.&lt;/p&gt;
&lt;p&gt;This isn&apos;t about one technology being better than the other. It&apos;s about understanding enough of both to collaborate with different teams and make informed decisions when I do have a choice.&lt;/p&gt;
&lt;h2&gt;K8s Is Complex, But Learnable&lt;/h2&gt;
&lt;p&gt;People aren&apos;t wrong when they say K8s is complex. It is. There are a lot of moving parts.&lt;/p&gt;
&lt;p&gt;But here&apos;s what studying for the KCNA taught me: if you take the time to understand the components and how they fit together, it&apos;s not magic. It&apos;s learnable. Breaking it down into pieces makes it manageable.&lt;/p&gt;
&lt;p&gt;The complexity isn&apos;t a reason to avoid it. It&apos;s just a reason to approach it systematically.&lt;/p&gt;
&lt;h2&gt;My Advice: Depth First, Then Breadth&lt;/h2&gt;
&lt;p&gt;If someone asked me &quot;should I learn K8s?&quot; a year ago, I would have said: &lt;strong&gt;get really good at one thing first.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Master the fundamentals of whatever stack you&apos;re working in. Build that depth. Then expand your toolbelt.&lt;/p&gt;
&lt;p&gt;Trying to learn everything at once means you&apos;ll be mediocre at many things instead of excellent at one. Depth first, breadth later.&lt;/p&gt;
&lt;p&gt;Disclaimer: this is what works for me. Your situation might be different, and that&apos;s okay.&lt;/p&gt;
&lt;p&gt;That&apos;s what this K8s certification represents for me. Not abandoning serverless or becoming a &quot;K8s person.&quot; Just adding another tool to solve different problems.&lt;/p&gt;
&lt;h2&gt;What&apos;s Next&lt;/h2&gt;
&lt;p&gt;The plan is to complete the &amp;lt;a href=&quot;https://cloudresumechallenge.dev/docs/extensions/kubernetes-challenge/&quot; target=&quot;_blank&quot;&amp;gt;Kubernetes Resume Challenge&amp;lt;/a&amp;gt; and build some personal projects. The goal is practical knowledge, not just cert credentials.&lt;/p&gt;
&lt;p&gt;Honestly, it&apos;s been fun to be a beginner again.&lt;/p&gt;
&lt;p&gt;I&apos;d love to hear from you: Have you learned outside your specialty? What pushed you to do it, and what did you learn in the process?&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Most Valuable When Least Visible | The Security Paradox</title><link>https://danielleheberling.xyz/blog/security/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/security/</guid><description>Security -- The Most Important Work You’ll Never See in a Sprint Demo</description><pubDate>Wed, 18 Jun 2025 10:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/computer-padlock.jpg&quot; alt=&quot;Computer Lock&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@flyd2069?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;FlyD&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/pink-and-silver-padlock-on-black-computer-keyboard-F7aZ8G7gGBQ?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Tale as old as time: the friction between building out new features and the foundations necessary to deliver those features. By foundations, I&apos;m referring to things such as but not limited to: security, testing, Continuous Integration/Continuous Delivery, observability, and Infrastructure as Code.&lt;/p&gt;
&lt;p&gt;Recently I attended &lt;a href=&quot;https://reinforce.awsevents.com/&quot;&gt;AWS re:Inforce&lt;/a&gt;, an AWS security focused conference. Speaking with fellow attendees it was very common for people ask some variant of &quot;how can I get people in my organization to care about security?&quot;&lt;/p&gt;
&lt;p&gt;Disclaimer that I do not consider myself a security person™️, but I do champion security initiatives often. Security is everyone&apos;s responsibility. Sometimes in the real world people get excited about new features and forget about less visible things such as security.&lt;/p&gt;
&lt;p&gt;Here are some of my thoughts in the form of unsolicited advice on how I attempt to navigate this.&lt;/p&gt;
&lt;h2&gt;1. Just Do It&lt;/h2&gt;
&lt;p&gt;If you work in an organization that gives you the freedom to allocate your time to projects beyond what is officially committed to in the sprint, just do it. Then once it&apos;s finished, you can share with your team what you did, how it works, and why it&apos;s important.&lt;/p&gt;
&lt;p&gt;It is often faster to just do it rather than spend the same amount of time asking for permission. Especially at smaller early stage startups, delivering results speak louder than words.&lt;/p&gt;
&lt;h2&gt;2. View Yourself as an Enabler&lt;/h2&gt;
&lt;p&gt;Security is often (and mistakenly) seen as an obstacle to rapid feature development. I suggest reframing the conversation to highlight how security actually empowers the team to deliver features more quickly.&lt;/p&gt;
&lt;p&gt;Many security measures may not seem urgent now, but by the time they are, it’s often too late. Focus on communicating the value of being proactive instead of reactive.&lt;/p&gt;
&lt;h2&gt;3. Learn Incentives&lt;/h2&gt;
&lt;p&gt;Incentives influence our perspectives and priorities. Invest time in understanding key stakeholders, especially decision-makers, to learn what drives them and what matters most to them.&lt;/p&gt;
&lt;p&gt;This understanding will help you frame your arguments more effectively when proposing needs that don’t have the allure of a shiny new feature.&lt;/p&gt;
&lt;h2&gt;Bonus: Get Involved in the Community&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/reinforce-aws-community.jpg&quot; alt=&quot;AWS Community at Reinforce&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As tech professionals, it’s easy to find ourselves working in silos within our organizations. But the more I connect with colleagues outside of my current company, the more I discover that the challenges I’m facing have often already been solved by someone else.&lt;/p&gt;
&lt;p&gt;Why reinvent the wheel when we can learn from each other’s experiences - and share our own insights to help others grow? Engage in conversations, exchange ideas, and don’t hesitate to ask questions, even if they seem basic. Often, those “simple” questions lead to the most valuable discussions and solutions.&lt;/p&gt;
&lt;p&gt;In person conferences aren&apos;t your thing? Shameless plug to check out the &lt;a href=&quot;https://www.believeinserverless.com/&quot;&gt;Believe in Serverless Community on Discord&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;Ultimately, trust is the foundation of successful software. When it’s present, it’s invisible; when it’s missing, it becomes impossible to ignore. Once broken, trust is incredibly difficult to rebuild—especially for early-stage startups still working to establish their reputation. If users can’t trust your product, they won’t use it.&lt;/p&gt;
&lt;p&gt;I’m not claiming to have all the answers when it comes to security, but I hope sharing these thoughts sparks reflection or helps someone else feel less alone in facing these challenges. Your efforts to prioritize security truly matter, even when they go unseen. Keep pushing forward and championing the importance of strong foundations.&lt;/p&gt;
&lt;p&gt;I’d love to hear from you: What strategies or insights have helped you balance the tension between shipping new features and building secure, reliable products?
&amp;lt;br /&amp;gt;
&amp;lt;br /&amp;gt;&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>AWS Fargate on a Budget</title><link>https://danielleheberling.xyz/blog/fargate-on-a-budget/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/fargate-on-a-budget/</guid><description>AWS Fargate on a Budget</description><pubDate>Tue, 18 Feb 2025 11:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/budget.jpg&quot; alt=&quot;Money counting&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@sharonmccutcheon?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Alexander Grey&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/focus-photography-of-person-counting-dollar-banknotes--8a5eJ1-mmQ?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The Business Problem&lt;/h2&gt;
&lt;p&gt;At the day job, there&apos;s a new application that needs to be deployed in a manner that meets these requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the code is server side and it needs to run longer than 15 minutes&lt;/li&gt;
&lt;li&gt;this is an internal use app, it can tolerate occasional disruptions&lt;/li&gt;
&lt;li&gt;non-engineering stakeholders should be able to access this application&lt;/li&gt;
&lt;li&gt;the application will be used by fewer then 10 people and there will be periods of time where no one will be using it&lt;/li&gt;
&lt;li&gt;it is faster to get approval if we use Amazon Web Services, because we currently use AWS heavily&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A Solution&lt;/h2&gt;
&lt;p&gt;For non-disclosure agreement and security reasons this is a variation of the solution, not the exact solution.&lt;/p&gt;
&lt;p&gt;It includes the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;One Fargate Spot instance behind an Application Load Balancer&lt;/li&gt;
&lt;li&gt;Two EventBridge Scheduler schedules that turn the Fargate Spot instances on at a specified time and off at a specified time&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Disclaimer: this is not the only way to accomplish this.&lt;/p&gt;
&lt;h2&gt;Architecture Diagram and Example Code&lt;/h2&gt;
&lt;p&gt;Here&apos;s the high level architecture diagram of this solution.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/fargate-scheduler-arch-diagram.png&quot; alt=&quot;Fargate Scheduler Architecture&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Example code with instructions on how to deploy this into your AWS account, can be found in &lt;a href=&quot;https://github.com/deeheber/fargate-on-a-budget-demo&quot;&gt;this GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer: in this example, we are using a demo Docker image. If you are setting up an application that contains information that shouldn&apos;t be open to the public internet, ensure that authentication is in place. Authentication has been intentionally omitted, because it is not the focus of this article.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s zoom in a bit closer on the two major components of this solution:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fargate Spot&lt;/li&gt;
&lt;li&gt;EventBridge Scheduler&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Fargate Spot&lt;/h2&gt;
&lt;p&gt;With Fargate there are two capacity provider types: Fargate and Fargate Spot.&lt;/p&gt;
&lt;p&gt;Fargate is the standard which gives you on-demand access to containerized compute.&lt;/p&gt;
&lt;p&gt;Fargate Spot is similar to Fargate, but cheaper (advertised as up to 70% discounted) and can be interrupted by AWS. The reason for this is because AWS operates at a massive scale and lots of times there are instances available that will run and cost AWS money regardless. In order to make money off of this extra capacity, AWS offers this extra capacity as Spot instances at a discounted rate. Because AWS might need this capacity back as demand rises, they reserve the right to give you a two minute warning before shutting down your instance to put it back into the regular Fargate on-demand pool.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/blogs/aws/aws-fargate-spot-now-generally-available/&quot;&gt;The launch blog post&lt;/a&gt; has an excellent overview for an AWS official description.&lt;/p&gt;
&lt;p&gt;Launching a Fargate Spot instance involves 1. launching it and 2. setting up the container to handle a graceful shutdown given a two minute warning from AWS.&lt;/p&gt;
&lt;h3&gt;Launch the Instance&lt;/h3&gt;
&lt;p&gt;The example code uses the &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns.ApplicationLoadBalancedFargateService.html&quot;&gt;&lt;code&gt;ApplicationLoadBalancedFargateService&lt;/code&gt;&lt;/a&gt; CDK construct.&lt;/p&gt;
&lt;p&gt;You can specify one or multiple capacity providers with the &lt;code&gt;capacityProviderStrategies&lt;/code&gt; property with weights. The higher the weight, the more often that launch type will be utilized.&lt;/p&gt;
&lt;p&gt;The example code sets things up to prefer Spot, but to use a regular Fargate launch type in case there is no Spot availability.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;capacityProviderStrategies: [
          {
            capacityProvider: &apos;FARGATE_SPOT&apos;,
            weight: 50,
          },
          // Backup in case FARGATE_SPOT is not available
          {
            capacityProvider: &apos;FARGATE&apos;,
            weight: 1,
          },
        ],
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Handle Graceful Shutdowns&lt;/h3&gt;
&lt;p&gt;In the example code, this is done in two places:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Adds a &lt;code&gt;stopTimeout&lt;/code&gt; to the container on the Fargate Task for 120 seconds (needs to be &amp;lt;= 2 minutes)&lt;/li&gt;
&lt;li&gt;Sets the deregistration delay on the Application Load Balancer at 30 seconds to give the instance time to separate from the Load Balancer before terminating (also needs to be &amp;lt;= 2 minutes)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;EventBridge Scheduler&lt;/h2&gt;
&lt;p&gt;In the example, there are two EventBridge Scheduler schedules - &quot;up&quot; and &quot;down&quot;.&lt;/p&gt;
&lt;p&gt;This is accomplished by using an AWS CLI command to set the &lt;code&gt;desiredCount&lt;/code&gt; on the ECS Service to either &lt;code&gt;1&lt;/code&gt; (on/up) or &lt;code&gt;0&lt;/code&gt; (off/down). Here&apos;s &lt;a href=&quot;https://awscli.amazonaws.com/v2/documentation/api/2.1.21/reference/ecs/update-service.html&quot;&gt;the official documentation for the command&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If this command were run on the command line via the AWS CLI it would look something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;aws ecs update-service --cluster &amp;lt;cluster-name&amp;gt; --service &amp;lt;service-name&amp;gt; --desired-count &amp;lt;desired-count-int&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For this app, we&apos;re using &lt;a href=&quot;https://docs.aws.amazon.com/scheduler/latest/UserGuide/managing-targets-universal.html&quot;&gt;EventBridge&apos;s Universal Targets&lt;/a&gt; feature. Universal Targets allow you to run most (not all - see the link to view unsupported commands) AWS CLI commands directly in an EventBridge Schedule without the need to add (and pay for) compute (such as Lambda).&lt;/p&gt;
&lt;p&gt;In our example, we have one Schedule that sets the &lt;code&gt;desiredCount&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt; (on/up) at 9am PT Mon-Fri and another that sets the &lt;code&gt;desiredCount&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt; (off/down) at 5pm PT Mon-Fri.&lt;/p&gt;
&lt;p&gt;These cron expressions can be adjusted...be sure to ask your stakeholder(s) when they plan to use the app to ensure it is available.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;This was a walkthrough of a solution that allows us to run a Fargate instance on a budget. The solution utilizes Fargate Spot and EventBridge Scheduler to periodically shut down the Fargate Tasks during periods of no usage.&lt;/p&gt;
&lt;p&gt;The example code repository can be found &lt;a href=&quot;https://github.com/deeheber/fargate-on-a-budget-demo&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What tips and tricks do you have for saving money with Fargate?&lt;/p&gt;
&lt;p&gt;&amp;lt;br /&amp;gt;
&amp;lt;br /&amp;gt;&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Ephemeral Jobs Longer than the Lambda Timeout</title><link>https://danielleheberling.xyz/blog/ecs-run-task/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/ecs-run-task/</guid><description>Ephemeral Jobs Longer than the Lambda Timeout</description><pubDate>Thu, 07 Nov 2024 15:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/horse-race.jpg&quot; alt=&quot;horse race&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@cadop?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Mathew Schwartz&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/equestrian-riding-horse-at-daytime-5qRWQEdK7Sg?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;There&apos;s an ad-hoc job that runs in the background that doesn&apos;t have a client waiting for a synchronous response.&lt;/p&gt;
&lt;p&gt;You could run this in AWS Lambda to save money. A benefit with an ephemeral job run is that AWS only charges for when the Lambda is running.&lt;/p&gt;
&lt;p&gt;Problem is that this specific job runs longer than the Lambda timeout (at the time of writing this it is 15 minutes). We could write extra logic to make it work, but that adds unnecessary complexity.&lt;/p&gt;
&lt;p&gt;You still want to run this ephemerally to save money and resources...so what can you do?&lt;/p&gt;
&lt;h2&gt;A Solution&lt;/h2&gt;
&lt;p&gt;One option is to use &lt;a href=&quot;https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_RunTask.html&quot;&gt;ECS run-task&lt;/a&gt; with a &lt;a href=&quot;https://aws.amazon.com/fargate/&quot;&gt;Fargate&lt;/a&gt; launch type.&lt;/p&gt;
&lt;p&gt;AWS Resources Needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html&quot;&gt;An ECS Cluster&lt;/a&gt; - a logical grouping of tasks or services (in my example we&apos;re using the &lt;code&gt;default&lt;/code&gt; cluster that comes with new AWS accounts)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html&quot;&gt;A Task Definition&lt;/a&gt; - a blueprint for your application which contains one or more containers and parameters to run&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/what-is/cloud-containers/&quot;&gt;A Container&lt;/a&gt; - a lightweight, standalone, executable package of software that includes everything needed to run an application. This is commonly used with &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once those resources are deployed, the container can be triggered to run on demand with &lt;code&gt;ECS run-task&lt;/code&gt;. Once the code is done running and the container exits, then the running ECS Task will disappear. The benefit is that you&apos;ll no longer be charged money for a running Fargate task.&lt;/p&gt;
&lt;p&gt;There&apos;s a few ways to run an ECS task on demand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS CLI&lt;/li&gt;
&lt;li&gt;AWS SDK (could invoke from within Lambda etc)&lt;/li&gt;
&lt;li&gt;In a Step Function state&lt;/li&gt;
&lt;li&gt;EventBridge Scheduler&lt;/li&gt;
&lt;li&gt;AWS Console&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&apos;s an example of what this command looks like via the AWS CLI:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;aws ecs run-task \
  --cluster default \
  --task-definition my-task \
  --launch-type FARGATE \
  --network-configuration &quot;awsvpcConfiguration={subnets=[subnet1,subnet2],securityGroups=[securityGroup1],assignPublicIp=ENABLED}&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Show Me the Code&lt;/h2&gt;
&lt;p&gt;There&apos;s a &lt;a href=&quot;https://github.com/deeheber/ecs-run-task-demo&quot;&gt;full code example&lt;/a&gt; of how to deploy these cloud resources and invoke the deployed task via ecs run-task with the AWS CLI.&lt;/p&gt;
&lt;p&gt;Take a look at &lt;a href=&quot;https://github.com/deeheber/ecs-run-task-demo/blob/main/README.md&quot;&gt;the &lt;code&gt;README&lt;/code&gt; file&lt;/a&gt; for directions.&lt;/p&gt;
&lt;h2&gt;Bonus&lt;/h2&gt;
&lt;p&gt;There are a few more benefits to ECS run-task that I won&apos;t mention, but one notable one is the ability to override specific things in the task definition.&lt;/p&gt;
&lt;p&gt;For example, if you want to override the &lt;code&gt;CMD&lt;/code&gt; defined in the &lt;code&gt;Dockerfile&lt;/code&gt; used to build the Container Image for the task, you could do this by adding the &lt;code&gt;--overrides&lt;/code&gt; flag.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;aws ecs run-task \
  --cluster default \
  --task-definition my-task \
  --launch-type FARGATE \
  --network-configuration &quot;awsvpcConfiguration={subnets=[subnet1,subnet2],securityGroups=[securityGroup1],assignPublicIp=ENABLED}&quot; \
  --overrides &apos;{&quot;containerOverrides&quot;:[{&quot;name&quot;:&quot;my-container&quot;,&quot;command&quot;:[&quot;npm&quot;, &quot;run&quot;, &quot;migrate&quot;]}]}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;ve seen this used for one off commands that need to run every now and then such as database migrations, though I&apos;m certain there are other good use cases. Here&apos;s &lt;a href=&quot;https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_TaskOverride.html&quot;&gt;the full list&lt;/a&gt; of what can be overridden.&lt;/p&gt;
&lt;p&gt;Special thanks to &lt;a href=&quot;https://www.linkedin.com/in/chasedouglas/&quot;&gt;Chase Douglas&lt;/a&gt; for giving me this idea. 🙌🏻&lt;/p&gt;
&lt;p&gt;&amp;lt;br /&amp;gt;
&amp;lt;br /&amp;gt;&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Playdate Review</title><link>https://danielleheberling.xyz/blog/playdate-review/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/playdate-review/</guid><description>Playdate review</description><pubDate>Sat, 28 Sep 2024 05:12:03 GMT</pubDate><content:encoded>&lt;h2&gt;What is a Playdate?&lt;/h2&gt;
&lt;p&gt;The Playdate is a small hand held game system with a crank, buttons (D-pad, a, b), and a small black and white screen (similar to e-paper).&lt;/p&gt;
&lt;p&gt;With dimensions of 76 × 74 × 9 mm, the console can easily fit into most pockets for on the go game playing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/playdate.jpeg&quot; alt=&quot;playdate&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://panic.com/&quot;&gt;Panic&lt;/a&gt; is the company that created the Playdate. You might have heard of Panic from playing &lt;a href=&quot;https://goose.game/&quot;&gt;Untitled Goose Game&lt;/a&gt; or &lt;a href=&quot;https://www.firewatchgame.com/&quot;&gt;Firewatch&lt;/a&gt;. Or you might have used Mac apps such as &lt;a href=&quot;https://panic.com/transmit/&quot;&gt;Transmit&lt;/a&gt; or &lt;a href=&quot;https://nova.app/&quot;&gt;Coda (renamed to Nova)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The CEO and co-founder of Panic gave &lt;a href=&quot;https://gdcvault.com/play/1034707/The-Playdate-Story-What-Was&quot;&gt;an in depth talk&lt;/a&gt; about the story behind the creation of the Playdate which is entertaining and informative if you would like to go deeper.&lt;/p&gt;
&lt;h2&gt;Nice!&lt;/h2&gt;
&lt;p&gt;These are some things that I enjoyed about the Playdate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You get 24 free games (also called &quot;season one&quot;) 2 games delievered each week until you have them all. It&apos;s delightful to see that notification light that there&apos;s new games to play at the beginning of owning this game system.&lt;/li&gt;
&lt;li&gt;Games aren&apos;t limited to their catalog, you can also build your own games using &lt;a href=&quot;https://play.date/dev/&quot;&gt;their SDK&lt;/a&gt; or side load games from places such as &lt;a href=&quot;https://itch.io/&quot;&gt;itch.io&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The crank as an input device is surpisingly delightful and unique.&lt;/li&gt;
&lt;li&gt;Most games have enhanced details. We&apos;re talking beautiful storytelling, visuals, and music.&lt;/li&gt;
&lt;li&gt;This game system is minimally connected to the internet to allow downloading of games, but otherwise there aren&apos;t distractions that would exist on a smart phone.&lt;/li&gt;
&lt;li&gt;The battery life has been great so far. Marketing copy for this product boasts 8 hours when playing games and 14 days while in standby clock mode. This has matched my experience. They also use a generic usb-c charger as well, so it&apos;s likely you already have the plug/cord for it (though it comes with one).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Oh.&lt;/h2&gt;
&lt;p&gt;These are some things that I think could be improved with the Playdate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This game system does not have a backlight, so if you don&apos;t have good lighting it will be hard to see the screen.&lt;/li&gt;
&lt;li&gt;I&apos;m left handed and would love to have a &quot;lefty&quot; version with the crank on the left side of the game system.&lt;/li&gt;
&lt;li&gt;Right now the only body color is yellow which is great, but I would like more color options especially when having multiple Playdates in my house.&lt;/li&gt;
&lt;li&gt;There is not a way to shut it off. It will go in standby clock mode which is fine, but I would like the ability to fully shut the game system off without needing to let the battery die.&lt;/li&gt;
&lt;li&gt;Right now for sound, it is wired headphones only. Looking through their website it appears that bluetooth is likely coming soon with a software update. To my knowledge the wifi chip they have in the game system should also double as bluetooth, so I don&apos;t think a hardware update is needed for this.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Where and how to buy&lt;/h2&gt;
&lt;p&gt;I&apos;m seeing a few resellers, but the official place to buy and where I bought one was from &lt;a href=&quot;https://play.date/shop/&quot;&gt;the official playdate shop&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;My opinion&lt;/h2&gt;
&lt;p&gt;I would highly recommend buying this game system. The Playdate is a lovely distraction from a world full of advertisements, pop ups, and notifications.&lt;/p&gt;
&lt;p&gt;As a kid, I could sit for hours and play video games, but as an adult I find that I do not have much time to devote to playing video games. The games on this system are perfect for that. You pick up the game system, play for 15-30 min here and there plus the controls aren&apos;t complicated...so you&apos;re not spending time trying to remember how to play the game.&lt;/p&gt;
&lt;p&gt;Also anything Panic creates, I know will be high quality and well thought out.&lt;/p&gt;
&lt;h2&gt;Side story&lt;/h2&gt;
&lt;p&gt;Panic holds a special place in my heart. Back when I recently relocated to Portland, OR I was looking to get out of my current company and saw that they had an open customer support role. I was currently working in customer support, but moonlighting by taking night/weekend classes at a local code school to switch into being a software engineer. I went into their office and did the full interview round (including an interview with their CEO, Cabel Sasser). The next day I received the nicest rejection email from Cabel stating that they really liked me, but think that I would be better suited to becoming a software engineer and recommended that I continue down that path.&lt;/p&gt;
&lt;p&gt;Anyway I highly recommend the Playdate and anything that Panic creates, because I know it will be high quality in terms of the aesthetics and story telling.
&amp;lt;br /&amp;gt;
&amp;lt;br /&amp;gt;&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Devs are Not the Target Market for AI</title><link>https://danielleheberling.xyz/blog/devs-not-target-market/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/devs-not-target-market/</guid><description>Devs are Not the Target Market for AI</description><pubDate>Fri, 30 Aug 2024 12:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/robotDevs.jpg&quot; alt=&quot;robot devs&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;The year is 2024 in the tech industry. Layoffs and downsizing are everywhere. It is a very challenging job seeker market. There are lots of recent code school and computer science degree graduates seeking their first software engineer job. Amidst all of this noise, there is massive hype about &quot;generative AI.&quot;&lt;/p&gt;
&lt;h2&gt;More on Artificial Intelligence (AI)&lt;/h2&gt;
&lt;p&gt;In my part of the world, software engineers are tired of hearing about AI and feel like it is mostly a solution looking for a problem. Everyday users I interact with also echo these sentiments.&lt;/p&gt;
&lt;p&gt;To contast this, I&apos;m seeing two groups of people that are very excited about AI 👉🏻 people selling things and business executives/investors at various companies.&lt;/p&gt;
&lt;p&gt;The motivations of people trying to sell you something is pretty straighforward. They want to sell things to make money for their business.&lt;/p&gt;
&lt;p&gt;The executives/investor&apos;s motivations are less clear. Here&apos;s my theory, many of them view employing humans as a liability both because dealing with humans can be messy but also because paying employees a salary is expensive and they think that AI can be a substitute.&lt;/p&gt;
&lt;p&gt;A harsh truth is that many businesses exist to make money and to be profitable. If they no longer need to have the expense of paying employee salaries, this can help them move closer to that goal. Most companies &lt;strong&gt;do not&lt;/strong&gt; exist for the purpose of providing jobs to people.&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;/p&gt;
&lt;h2&gt;My Opinion on AI&lt;/h2&gt;
&lt;p&gt;I have concerns over AI ethics. Bias in the models, creator&apos;s work getting stolen for use in AI models without compensation, and environmental concerns of needing to run many servers to train these huge models. Many members of the United States government do not understand how technology works, so I don&apos;t trust that they can effectively regulate.&lt;/p&gt;
&lt;p&gt;I think that these tools are quite good, but they&apos;re not sophisticated enough yet to fully replace human software engineering labor on a wider scale.&lt;/p&gt;
&lt;p&gt;I have concerns over the ability for human or AI software engineers to be able to effectively troubleshoot and maintain unvetted AI generated code that gets pushed to production.&lt;/p&gt;
&lt;p&gt;I am excited about the potential for AI to do boring and mundane tasks for us humans, so we can focus on imaginative creativity. An example: I&apos;d like AI to do the dishes for me, so I can write my next novel, paint my next portrait, or compose my next song. I haven&apos;t seen many examples of this yet.&lt;/p&gt;
&lt;h2&gt;My Opinion Ultimately Doesn&apos;t Matter&lt;/h2&gt;
&lt;p&gt;What does matter if you want to be employed as a software engineer in this industry is the opinion of those who are in positions of power that make the budget and hiring decisions.&lt;/p&gt;
&lt;h2&gt;Unsolicited Advice&lt;/h2&gt;
&lt;p&gt;If you want to be hired and maintain a job in this industry as a software engineer, you need to figure out the motivations of people in positions of power and do the work to be able to effectively and genuinely sell yourself to them.&lt;/p&gt;
&lt;h2&gt;Some Ideas&lt;/h2&gt;
&lt;p&gt;Here&apos;s some themes I&apos;ve seen lately with people in positions of power at various companies. Note that my bias leans toward small/medium size businesses since that is where the majority of my social network is employed. Your experience may be different.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;They want to do more with less&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I would advise being curious. When something isn&apos;t working as expected, be willing to adventure outside of your comfort zone to figure why, fix it, and learn new things.&lt;/p&gt;
&lt;p&gt;On the web development front, more places desire to hire &quot;fullstack&quot; engineers. Just doing frontend is not enough for many companies.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;They want to deliver solutions&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Focus on shipping. It&apos;s not enough to only write code, you need to know how to get a full working application into production that satisfies all product requirements. You also need to know how to troubleshoot and diagnose bugs that happen to your users in production.&lt;/p&gt;
&lt;p&gt;It&apos;s also important to be someone who can be trusted to follow through with what you say you will deliver. If you get stuck on something, it&apos;s your responsiblity to ask for help to get the assistance you need to follow through all the way.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;They&apos;re excited about AI and some want to replace us devs with AI because it is cheaper than paying a salary&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I would advise investing time in learning and using these new AI tools. Learn what they&apos;re good at and use them to accelerate your output. Learn what they&apos;re bad at and invest the time in learning how to do these things well by yourself. Learning those skills is a good way to emphasize your value to executives as to why they should hire you and pay you a salary instead of using AI.&lt;/p&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;Anyway, these are some thoughts I&apos;ve had over the past few months. I&apos;m just another person on the internet with opinions, giving unsolicited advice. What you do or do not do with it is up to you. I welcome polite engaged feedback and conversation on this topic.&lt;/p&gt;
&lt;p&gt;&amp;lt;br /&amp;gt;
&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt; Note that this is not commentary from me saying that I like the status quo. This is a comment plainly stating the status quo.&lt;/p&gt;
&lt;p&gt;&amp;lt;br /&amp;gt;
&amp;lt;br /&amp;gt;&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>I&apos;m Worried About Generative AI</title><link>https://danielleheberling.xyz/blog/generative-ai/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/generative-ai/</guid><description>I&apos;m Worried About Generative AI</description><pubDate>Sat, 04 May 2024 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/drawer.jpg&quot; alt=&quot;Clouds Image&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@dearseymour?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Ksenia Makagonova&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/opened-white-wooden-drawer-bngKirnA1EE?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&apos;m worried about generative AI. I say this as someone who uses AI to help with work tasks daily. I do believe there are some neat things that AI can do and there is high potential for it in the future as it improves, but I have concerns.&lt;/p&gt;
&lt;p&gt;No this post isn&apos;t about the common objections I&apos;ve been seeing in the discourse. Things like bias in the models, hallucinations, and people&apos;s jobs being replaced. Although they are very important things to monitor.&lt;/p&gt;
&lt;p&gt;I&apos;m concerned with AI monopolizing the focus of companies.&lt;/p&gt;
&lt;p&gt;Take the big tech companies for example. There are reports everywhere about multiple layoff rounds but also news of higher rates of investment (both money and people resources) in AI than ever before. These large companies have huge ecosystems that many folks depend on. Does this mean since the main focus is AI that there will be less focus on their non-AI products and services? Does this mean that service reliability will degrade as time goes on because engineers who previously responded to outages were moved to teams working on AI or were laid off?&lt;/p&gt;
&lt;p&gt;On the other end of the spectrum we have tech startups. I&apos;m seeing many chatbots being built and their marketing tells me this is the solution to my problems (that I didn&apos;t know I had). I&apos;m excited that this opens up opportunity for new ideas and businesses to take shape, but when the core of a product is centered on what is essentially a black box that might be very wrong sometimes...this doesn&apos;t feel like a good business model to me until the tech matures. My overall impression is that AI is a magic word that these companies can use to attract more customers who like shiny new things and also to attract venture capital funding.&lt;/p&gt;
&lt;p&gt;I&apos;ve also seen lots of companies throwing AI at things when it wasn&apos;t needed. An example: a company who will remain nameless was demoing a new tool that uses AI to help users troubleshoot error messages that surface in their app. A developer watching the demo yelled out &quot;or you could consider writing better error messages that users will understand instead of bringing AI into this.&quot;&lt;/p&gt;
&lt;p&gt;These are my opinions based off of observation of the world around me. I do not have data to present as evidence. This is all based off of vibes. I sure hope my vibes are wrong, that I&apos;m overreacting to all of this, and that I should take off my tin foil hat.&lt;/p&gt;
&lt;p&gt;Time will tell.&lt;/p&gt;
&lt;p&gt;AI has promise and it does have the potential to be helpful for specific types of tasks, but let&apos;s not forget that we should attempt to solve problems that already exist before bringing in tools that have the potential to cause even more problems.&lt;/p&gt;
&lt;p&gt;What are your thoughts?&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Tips for Getting Started in Cloud</title><link>https://danielleheberling.xyz/blog/get-started-in-cloud/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/get-started-in-cloud/</guid><description>Tips for Getting Started in Cloud</description><pubDate>Fri, 12 Apr 2024 09:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/clouds.jpg&quot; alt=&quot;Clouds Image&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@billy_huy?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Billy Huynh&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/cloudy-sky-at-daytime-v9bnfMCyKbg?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Who am I?&lt;/h2&gt;
&lt;p&gt;I graduated from a code school in 2016 in the United States and have been employed as a software engineer ever since. Because I like to give back to the communities that helped me get started, I&apos;ve done paid contracts and volunteered in positions where I mentor folks who would like to take a similar path.&lt;/p&gt;
&lt;p&gt;After years of sharing my knowledge in the wider AWS community, I was recognized as an AWS Serverless Hero in 2023.&lt;/p&gt;
&lt;h2&gt;My goal in writing this&lt;/h2&gt;
&lt;p&gt;I often surround myself with people in the code school community who have expertise in &quot;Full Stack JavaScript&quot; (it&apos;s mostly Frontend focused from my observation). Because of this, I am often asked the questions like &quot;how can I get started in Cloud&quot; or &quot;how can I learn AWS.&quot; It is my hope that this post can serve as a reference for folks who ask me those questions.&lt;/p&gt;
&lt;h2&gt;My biases&lt;/h2&gt;
&lt;p&gt;We all have biases, myself included. I encourage you to do what you think is best for you. Do not take my advice as an absolute, &quot;I must do everything on this list.&quot;&lt;/p&gt;
&lt;p&gt;Cloud-wise I have mostly worked in AWS, so my opinions and advice will likely be AWS centric. It is my personal opinion that I wish that there was more Cloud competition (specifically one that offers the same uptime/choices of Serverless services AWS offers but with a better developer experience), but in my part of the world most of the jobs available are fully AWS or AWS heavy in terms of Cloud usage. This is why I&apos;ve chosen to focus on this Cloud and not the others.&lt;/p&gt;
&lt;p&gt;I also started my career as a &quot;Full Stack JavaScript&quot; developer and thus was originally very Frontend focused. At the time, I mostly cared about what the customers experience and the implementation details (while important) were less interesting to me. To that end, I was drawn to using managed services that AWS offers (sometimes called &quot;Serverless&quot;) rather than rolling my own by using solutions such as Kubernetes or writing lots of Dockerfiles (sometimes referred to as &quot;Cloud Native&quot;).&lt;/p&gt;
&lt;h2&gt;Should I get cerfitications or do hands-on projects?&lt;/h2&gt;
&lt;p&gt;This is often the first question I am asked and many folks (some with monetary incentives) have shared their advice on the internet. Here is my biased opinion.&lt;/p&gt;
&lt;p&gt;As a Frontend focused engineer, I had no idea what any of the AWS alphabet soup of managed services did or their names. I started my journey by obtaining the &lt;a href=&quot;https://aws.amazon.com/certification/certified-solutions-architect-associate/&quot;&gt;AWS Certified Solutions Architect - Associate&lt;/a&gt; certification.&lt;/p&gt;
&lt;p&gt;I used my knowledge gained from studying for that exam as a jumping off point to start building my own projects. Lately, I&apos;ve mostly been learning new things through hands on projects.&lt;/p&gt;
&lt;p&gt;I view certifications as the &quot;theory&quot; and hands on as the &quot;practice.&quot; Getting certifications without the hands on practice will not get you the job. From my experience most employers care more about what you have done and can do hands on inside of AWS.&lt;/p&gt;
&lt;p&gt;For me a 30% focus on certifications (because my employer and various AWS community programs will pay for the exam) and 70% building side projects for fun is what has worked the best for my learning.&lt;/p&gt;
&lt;p&gt;If I&apos;m curious about an AWS service that I&apos;ve never used, I will build a side project focused on it to learn. These side projects do not need to be public or used as a portfolio piece for job interviews. With these side projects, I like to take advantage of having a smaller codebase and a controlled environment (my own AWS account and not a production app for work) to experiment and learn.&lt;/p&gt;
&lt;h2&gt;Infrastructure as code&lt;/h2&gt;
&lt;p&gt;Lots of certification prep material and getting started content will encourage you to click through the AWS console to create your Cloud resources. This is informally known as &quot;click ops.&quot;&lt;/p&gt;
&lt;p&gt;I would highly recommend you &lt;strong&gt;don&apos;t do this&lt;/strong&gt; and learn to use an infrastructure as code (IaC) tool while building side projects for learning. This is much closer to how things will look once you have a job and are working on a team.&lt;/p&gt;
&lt;p&gt;There&apos;s lots of arguments across the internet about which one is the best. Some folks weirdly get cult-ish about it. My opinion is to pick one and use it! The skills are transferrable across the different IaC tools from my experience.&lt;/p&gt;
&lt;p&gt;Some commonly used ones with AWS are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.terraform.io/&quot;&gt;Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/serverless/sam/&quot;&gt;AWS SAM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/cdk/&quot;&gt;AWS CDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/application-composer/&quot;&gt;AWS Application Composer&lt;/a&gt; might also be a good starting point because it is a drag and drop interface for your cloud resources, but you also have full access to the IaC that it generates.&lt;/p&gt;
&lt;p&gt;Along the lines of IaC, you should also look into &lt;a href=&quot;https://martinfowler.com/articles/continuousIntegration.html&quot;&gt;CI/CD&lt;/a&gt;. There are lots of tools to accomplish this, but one that I&apos;ve been using lately on the job has been &lt;a href=&quot;https://github.com/features/actions&quot;&gt;GitHub Actions&lt;/a&gt;. Again, I recommend you find one and use it. The principles and higher level ideas are transferrable across different CI/CD platforms.&lt;/p&gt;
&lt;h2&gt;Recommended resources&lt;/h2&gt;
&lt;p&gt;Here&apos;s a list of resources that I recommend. As new material comes out, I will do my best to keep this updated. Disclaimer that these are all things that I&apos;ve used or are created by folks whom I respect. No one paid, sponsored, or asked me to add their content to this list. Also note that some of these resources have &lt;a href=&quot;https://discord.com/&quot;&gt;Discord&lt;/a&gt; communities where you can interact with fellow learners and ask for help.&lt;/p&gt;
&lt;h3&gt;Free resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=zA8guDqfv40&quot;&gt;AWS Cloud Complete Bootcamp Course&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://openupthecloud.com/start-here/&quot;&gt;Open up the Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learntocloud.guide/&quot;&gt;Learn to Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Zr6fnhvJKlw&quot;&gt;Event Driven Architecture on AWS - Course for Beginners&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Mix of free and paid resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cloudresumechallenge.dev/&quot;&gt;The Cloud Resume Challenge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tutorialsdojo.com/&quot;&gt;Tutorials Dojo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://awsnewbies.com/&quot;&gt;AWS Newbies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.exampro.co/&quot;&gt;Exam Pro&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Paid resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cloudacademy.com/&quot;&gt;Cloud Academy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pluralsight.com/cloud-guru&quot;&gt;A Cloud Guru&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;I hope you found my biased opinion on this topic helpful! Cloud and AWS are big things to learn, so be patient with yourself and join some communities where you can ask for help. I has taken me years to learn what I know and I still don&apos;t know everything.&lt;/p&gt;
&lt;p&gt;Do you have any getting started in the Cloud resources you&apos;d like to share? Please do!&lt;/p&gt;
&lt;p&gt;Good luck out there. I can&apos;t wait to see what you build!&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>The Collab Lab Team 66 Recap</title><link>https://danielleheberling.xyz/blog/tcl-66-recap/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/tcl-66-recap/</guid><description>The Collab Lab Team 66 Recap</description><pubDate>Sun, 07 Apr 2024 22:12:03 GMT</pubDate><content:encoded>&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://the-collab-lab.codes/&quot;&gt;The Collab Lab&lt;/a&gt; is a non-profit organization that runs a program where early career developers can apply to work together on a team to complete a project led by mentors who work in the field.&lt;/p&gt;
&lt;h2&gt;The Team&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s9ye4cuh77old5ec8pe2.png&quot; alt=&quot;A screenshot of us in a sync&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Participants&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/aloenelson/&quot;&gt;Aloe Nelson&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/emilio-campos-jr/&quot;&gt;Emilio Campos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/hannah-wohl-machado/&quot;&gt;Hannah Wohl-Machado&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/stefanie-caffarel-888209113/&quot;&gt;Stefanie Caffarel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Mentors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/deeheber/&quot;&gt;Danielle Heberling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/jeremiah-fallin/&quot;&gt;Jeremiah Fallin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/nickzanetti/&quot;&gt;Nick Zanetti&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Project&lt;/h2&gt;
&lt;p&gt;We were tasked to build a &quot;smart&quot; shopping list over the course of 10 weeks. During this time there was lots of pair programming, async communication over slack, code review, demos, and retrospectives.&lt;/p&gt;
&lt;h3&gt;Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Login and customized views based off of whom logged in&lt;/li&gt;
&lt;li&gt;Create a new list&lt;/li&gt;
&lt;li&gt;Add an item to the list and set the urgency level (soon, kind of soon, not so soon)&lt;/li&gt;
&lt;li&gt;Delete an item from the list&lt;/li&gt;
&lt;li&gt;Mark an item as purchased&lt;/li&gt;
&lt;li&gt;Search a list for an item&lt;/li&gt;
&lt;li&gt;Share a list with another user&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Tech Stack&lt;/h3&gt;
&lt;p&gt;JavaScript
&lt;a href=&quot;https://react.dev/&quot;&gt;React&lt;/a&gt;
&lt;a href=&quot;https://flowbite.com/&quot;&gt;Flowbite&lt;/a&gt;
&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind&lt;/a&gt;
&lt;a href=&quot;https://firebase.google.com/&quot;&gt;Firebase&lt;/a&gt; - Auth, Database, and Hosting
&lt;a href=&quot;https://vitejs.dev/&quot;&gt;Vite&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;The Finished Project&lt;/h2&gt;
&lt;h3&gt;Login Page&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h0tyrbjmrs7eq3j1wdze.png&quot; alt=&quot;login page&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Landing Page - select a list to view/edit&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m9mzi71tldh18rio5dfw.png&quot; alt=&quot;landing page&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;A Specific List Page&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s671ot4yj9xh9ue21wsq.png&quot; alt=&quot;view a list&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The source code for the app can be found on &lt;a href=&quot;https://github.com/the-collab-lab/tcl-66-smart-shopping-list&quot;&gt;our GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can view this app live at https://tcl-66-smart-shopping-list.web.app/.&lt;/p&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;We had a great 10 weeks learning from each other and are looking forward to continuing to build fun projects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you&apos;re looking to hire early career developers, I would highly encourage you to take a look at these folks&apos; LinkedIn profiles.&lt;/strong&gt;&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>How To Become An AWS Hero</title><link>https://danielleheberling.xyz/blog/how-to-hero/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/how-to-hero/</guid><description>How To Become An AWS Hero</description><pubDate>Wed, 03 Apr 2024 05:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/hero-neon.jpg&quot; alt=&quot;We can be heroes neon sign&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@gabrielbassino?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Gabriel Bassino&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/yellow-neon-light-signage-zEawlLdVloo?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;Ever since I was named an AWS Hero, lots of people have been asking me how they can become an AWS Hero.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In my opinion, this is the wrong question to be asking.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I never had the goal to become an AWS Hero. I did and will continue to do what I&apos;m doing now regardless of the AWS Hero title. Don&apos;t get me wrong, I am very humbled and honored to have been recognized as one by AWS, but this is not why I do what I do.&lt;/p&gt;
&lt;p&gt;In my opinion, &lt;strong&gt;the correct question to be asking is what are the rough edges and challenges in the current ecosystem and how can I share my knowledge and experiences to make it easier for others?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For ideas and inspiration, I recommend looking at what AWS Heroes and AWS Community Builders are doing. Also, remember that you can do something completely new too!&lt;/p&gt;
&lt;p&gt;You don’t need permission or a fancy title to help others and to make a difference. If you aren&apos;t sharing your knowledge and experiences in public yet and would like to, please join us!&lt;/p&gt;
&lt;p&gt;I&apos;m looking forward to seeing what you share.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Disclaimer: This post reflects my personal opinions. It may or may not be representative of the opinions of other AWS Heroes or AWS. You will need to ask them for theirs. 😀&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Dear New Code School Grad</title><link>https://danielleheberling.xyz/blog/career-change-advice/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/career-change-advice/</guid><description>Dear New Code School Grad</description><pubDate>Thu, 22 Feb 2024 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/diploma.jpg&quot; alt=&quot;Diploma&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@rutmiit?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;RUT MIIT&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/people-in-black-academic-dress-standing-on-green-grass-field-during-daytime-hpRGrfOIybc?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Over the years, many people have come to me for advice on doing a career change into software engineering. Here are my thoughts written down relevant to doing this in the year 2024 in the United States.&lt;/p&gt;
&lt;h2&gt;Who am I?&lt;/h2&gt;
&lt;p&gt;I graduated from a code school in 2016 in the United States and have been employed as a software engineer ever since. I&apos;ve held both formal and informal mentor titles. The formal ones were through side jobs working for code schools or being a mentor for nonprofits that help folks break into tech. The informal ones are via warm intros from network connections with recent code school grads seeking help.&lt;/p&gt;
&lt;h2&gt;My goal in writing this&lt;/h2&gt;
&lt;p&gt;I want you to succeed. We need more people in this industry that bring diverse life experiences to make our products truly reflect the public that we serve.&lt;/p&gt;
&lt;p&gt;There are a lot of folks on the internet and in real life that want to sell you the idea that a career switch and learning how to code is easy. I&apos;m here to tell you it is not.&lt;/p&gt;
&lt;h2&gt;Trends in 2024&lt;/h2&gt;
&lt;p&gt;The hiring market has evolved since I was looking for my first software job in 2016. It is much more challenging now. Here&apos;s a list of trends that I&apos;m seeing in my part of the industry:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;More code schools exist than did in 2016&lt;/li&gt;
&lt;li&gt;Most of these code schools teach full stack JavaScript with an emphasis on the frontend (react in particular)&lt;/li&gt;
&lt;li&gt;Due to higher enrollment in these programs 👉🏻 more people are looking for their first job as a full stack JS or frontend developer&lt;/li&gt;
&lt;li&gt;Many hiring managers are only interested in hiring people who have experience. It doesn&apos;t seem good enough to be &quot;senior&quot; anymore, lots are looking for &quot;staff&quot; or higher levels (what these levels actually mean can vary from company to company)&lt;/li&gt;
&lt;li&gt;Due to &quot;macroeconomic conditions&quot; many companies are conducting layoffs including software engineering staff&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Unsolicited advice&lt;/h2&gt;
&lt;p&gt;It is harder than ever to break into tech. Know that a large part of finding a job in any market is about being in the right place at the right time. Part of it is putting in the work to land that position...but if the job doesn&apos;t exist then you don&apos;t have a chance to land it. Be kind to yourself and know some of this is outside of your control.&lt;/p&gt;
&lt;p&gt;In my opinion, the most important thing to do is find ways to differentiate yourself. How are you different from the thousands of applications to this one open position and why should they hire you over the other applicants? Due to the high number of career changers seeking jobs, the story of a career change is no longer enough to make yourself stand out.&lt;/p&gt;
&lt;p&gt;A good way to start is by looking at job postings and taking note of specific technologies and positions that are in high demand. If you don&apos;t know those things 👉🏻 put in the work to learn it. We live in a golden age where there is tons of content (paid and unpaid) online.&lt;/p&gt;
&lt;p&gt;One caveat: there are lots of &quot;influencers&quot; trying to sell you something. Do your homework to investigate that individual or organization&apos;s background before purchasing anything. Try to find some LinkedIn profiles or ask folks in your network for a second opinion. It is possible they lied on their LinkedIn, but if they are qualified there should be artifacts somewhere in public spaces. Just because someone is loud and all over social media, it doesn&apos;t necessarily mean that they are an expert.&lt;/p&gt;
&lt;p&gt;Back to in demand skills. This is my highly biased opinion, but in my part of the industry there is high demand for people who work with the cloud. In all of my recent jobs, there were lots of frontend engineers and not enough people to do things on AWS side of our stack. This might look like a software engineer that builds apps that integrate with cloud managed services or it could be more of a &quot;DevOps&quot; type of person. (Side rant: DevOps should never be a job title in my opinion as it is a philosophy that everyone should follow despite their official title, but that could be another post.)&lt;/p&gt;
&lt;p&gt;A mistake that I see new graduates do is focus too much on making their LinkedIn profile and resume perfect. It is important to have a good online presence and resume, I&apos;m not debating that. But equally important is finding people (specifically people who are hiring/have influence in hiring decisions) to read your profiles/website/resume. From my experience, the best way to do this is by networking. I know most career advice says this, but it is true. All of my software engineering jobs have come from some form of networking and I&apos;m an introvert. It will be very uncomfortable, but if you want this badly enough you will need to acknowledge that unfortunately we live in a world where this is what you have to do to get people with influence to pay attention to your candidacy.&lt;/p&gt;
&lt;p&gt;Ideally this would be done via a warm introduction from a common connection, but don&apos;t worry about sending messages to people you don&apos;t know. As long as you&apos;re directly asking for what you want and respecting people&apos;s time, they will most likely reply and want to help you. Worst case is that they don&apos;t reply. View relationship building as a long term strategy and not as a one time transaction. You never know if you might one day be able to help this individual in return. Anecdotally, there have been many times when people whom I&apos;ve formerly mentored were able to help me out too later on. We&apos;re all in this together as a community and remember to give as much as you take in the long run.&lt;/p&gt;
&lt;p&gt;Another mistake that I see new graduates do often is they start a conversation with a hiring manager by stating they&apos;re a beginner or looking for a junior role. In this hiring climate, many hiring managers are seeking people with prior experience (see bullet point above), so starting out the conversation this way will most likely result in them losing interest in your candidacy. I&apos;m not suggesting you lie or misrepresent your experience at all. I&apos;d recommend talking about some things that you&apos;ve built, the the technologies you&apos;ve worked with, what parts of tech you&apos;re excited about, and what type of role you&apos;re looking for. If the question of previous professional experience comes up in conversation then tell the truth. &quot;Junior&quot; and &quot;entry level&quot; can mean different things to different hiring managers. Allow them to make that judgement after getting to know you a bit and hearing about what you&apos;ve been working on.&lt;/p&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;Keep in mind that some of these thoughts are very specific what&apos;s going on in the year 2024 in the United States, so if you&apos;re reading this in the future or from a different location some of it might not be relevant.&lt;/p&gt;
&lt;p&gt;I am not trying to sell you anything and really want you to succeed. This post is a compilation of my thoughts that many in my network have asked for, so I hope you find me publicly sharing this helpful.&lt;/p&gt;
&lt;p&gt;It is totally up to you if you want to follow this advice, because at the end of the day I&apos;m just another random person on the internet with opinions.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>The Serverless Mindset and Infrastructure as Code</title><link>https://danielleheberling.xyz/blog/serverless-mindset-iac/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/serverless-mindset-iac/</guid><description>The Serverless Mindset and Infrastructure as Code</description><pubDate>Tue, 05 Dec 2023 12:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/app-composer-canvas.png&quot; alt=&quot;AWS Application Composer Canvas&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Originally, I wanted to blog about my favorite &lt;a href=&quot;https://reinvent.awsevents.com/&quot;&gt;re:Invent&lt;/a&gt; announcements. But as I started, I saw a higher level theme and decided to write on that instead.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer that this post contains my individual opinions and yours might differ. I welcome your polite/engaged feedback and encourage you to continue this conversation.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Relevant Announcements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/blogs/aws/aws-step-functions-workflow-studio-is-now-available-in-aws-application-composer/&quot;&gt;AWS Step Functions Workflow Studio is now available in AWS Application Composer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/blogs/aws/ide-extension-for-aws-application-composer-enhances-visual-modern-applications-development-with-ai-generated-iac/&quot;&gt;AWS Application Composer IDE extension&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A Lightbulb Moment&lt;/h2&gt;
&lt;p&gt;My time spent as a software engineer at &lt;code&gt;$day_job&lt;/code&gt; is mostly writing Infrastructure as Code (IaC). When reflecting on the &lt;a href=&quot;https://ben11kehoe.medium.com/serverless-is-a-state-of-mind-717ef2088b42&quot;&gt;Serverless mindset&lt;/a&gt;, I believe that some aspects of this mindset conflict with how I spend most of my time at &lt;code&gt;$day_job&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;✅ &lt;em&gt;Pros of the status quo:&lt;/em&gt; writing IaC is repeatable, scalable, fun, and I&apos;m good at it! 🙂&lt;/p&gt;
&lt;p&gt;🚫 &lt;em&gt;Con of the status quo:&lt;/em&gt; I find myself wanting to spend more time focusing on providing business value and less time thinking about tooling or specific settings for my cloud resources.&lt;/p&gt;
&lt;p&gt;Then a thought occurred to me: &lt;strong&gt;What if we didn&apos;t have to write IaC? Why not have a deployable diagram of the application instead?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is where I think tools such as &lt;a href=&quot;https://aws.amazon.com/application-composer/&quot;&gt;AWS Application Composer&lt;/a&gt; and &lt;a href=&quot;https://docs.aws.amazon.com/step-functions/latest/dg/workflow-studio.html&quot;&gt;AWS Step Functions Workflow Studio&lt;/a&gt; are headed in a positive direction via their drag and drop visual interfaces.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/danielle-tweet.png&quot; alt=&quot;Danielle Tweet&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;To a Brighter Future&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.allthingsdistributed.com/about.html&quot;&gt;Werner Vogels&lt;/a&gt; said in his keynote that &quot;the most dangerous phrase in the English language is: &apos;we&apos;ve always done it this way&apos;.&quot; I think this applies to how we&apos;ve historically approached IaC with Serverless development.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/hopper-quote.jpg&quot; alt=&quot;Grace Hopper Quote&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Anecdotally, I&apos;ve noticed that some experienced builders are ignoring updates to tools like this because they already know how to write IaC and think they don&apos;t need a visual interface. I politely disagree.&lt;/p&gt;
&lt;p&gt;To me, being able to more quickly collaborate, share, and deploy cloud resources is a huge win for everyone, regardless of experience level or job title. The primary focus can then be providing business value rather than figuring out how to write IaC for a specific cloud resource.&lt;/p&gt;
&lt;p&gt;We have so much more to offer than expertise in the tools that we use. Embrace the Serverless mindset and demand a brigher future. 😎&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/future.png&quot; alt=&quot;Optimistic future&quot; /&gt;&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>EventBridge Emoji Event Patterns</title><link>https://danielleheberling.xyz/blog/eventbridge-emoji/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/eventbridge-emoji/</guid><description>EventBridge Emoji Event Patterns</description><pubDate>Sun, 24 Sep 2023 07:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/emoji.jpg&quot; alt=&quot;emoji&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@denic?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Denis Cherkashin&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/qIKSsOMIhpM?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;The other day, I was answering some questions a coworker had about EventBridge. He then jokingly said &quot;I wonder if I can use emojis for an event pattern because they are UTF-8.&quot;&lt;/p&gt;
&lt;p&gt;After that conversation, I couldn&apos;t stop thinking about it. So I threw together a quick CDK stack and it works!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/cw-log.png&quot; alt=&quot;Lambda Cloudwatch logs&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Try it yourself&lt;/h2&gt;
&lt;p&gt;I&apos;ve made my quick CDK stack public &lt;a href=&quot;https://github.com/deeheber/eventbridge-emoji&quot;&gt;on Github&lt;/a&gt;. You&apos;re welcome to test with mine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This stack contains&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A rule with emojis only in the Event Pattern on the &lt;code&gt;default&lt;/code&gt; event bus&lt;/li&gt;
&lt;li&gt;A Lambda as the target of the rule that will be invoked and logs the event&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Steps&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Clone the repo&lt;/li&gt;
&lt;li&gt;Configure your AWS credentials locally if you have not yet&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm i &amp;amp;&amp;amp; npm run cdk deploy&lt;/code&gt; to deploy the stack&lt;/li&gt;
&lt;li&gt;Put events on the default event bus with either the &lt;a href=&quot;https://awscli.amazonaws.com/v2/documentation/api/latest/reference/events/put-events.html&quot;&gt;CLI&lt;/a&gt;, and AWS SDK, or in the AWS console. The event pattern is &lt;code&gt;detailType: [&apos;💩&apos;]&lt;/code&gt; and &lt;code&gt;source: [&apos;🐶&apos;]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Check the Lambda&apos;s CloudWatch logs to see that it has been invoked&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;You can also update the code to contain other emojis as well. From my testing, I found having multiple emojis in a single event pattern works as well.&lt;/p&gt;
&lt;p&gt;Not 100% practical in all cases, but thought I would share these findings publicly. Now go forth and update your EventBridge event patterns. 🎉&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>My Personal Blog Site&apos;s CI/CD</title><link>https://danielleheberling.xyz/blog/blog-ci-cd/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/blog-ci-cd/</guid><description>My Personal Blog Site&apos;s CI/CD</description><pubDate>Mon, 17 Jul 2023 22:10:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/tools.jpg&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@dancristianpaduret?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Dan Cristian Pădureț&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/XC7lc8biINg?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Goals&lt;/h2&gt;
&lt;p&gt;In 2018, I set out to create &lt;a href=&quot;https://www.danielleheberling.xyz/&quot;&gt;this blog&lt;/a&gt;. &lt;a href=&quot;https://medium.com/&quot;&gt;Medium&lt;/a&gt; and other blogging platforms existed; however, I wanted to have more control and ownership of my content. To me, it&apos;s fine to syndicate blog blog to other places for more reach, but I wanted my own space on the internet.&lt;/p&gt;
&lt;p&gt;I also had the other following goals in mind:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Low maintence&lt;/li&gt;
&lt;li&gt;Low cost (or free)&lt;/li&gt;
&lt;li&gt;Use technology that I wanted to learn&lt;/li&gt;
&lt;li&gt;Write blog blog in a transferrable format, such as markdown&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Tech Choices&lt;/h2&gt;
&lt;p&gt;In 2018, the hype of &lt;a href=&quot;https://jamstack.org/&quot;&gt;JAMstack&lt;/a&gt; and &lt;a href=&quot;https://www.gatsbyjs.com/&quot;&gt;Gatsby&lt;/a&gt; was in the air. Upon taking a closer look at Gatsby, I learned that it utilized &lt;a href=&quot;https://graphql.org/&quot;&gt;GraphQL&lt;/a&gt; (tech I didn&apos;t know at the time and wanted to learn), &lt;a href=&quot;https://react.dev/&quot;&gt;React&lt;/a&gt;, and blog blog were written in markdown. After choosing Gatsby, I did some searching for the easiest way to deploy it and landed on &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To this day, my blog is still Gatsby hosted on Netlify and you &lt;a href=&quot;https://github.com/deeheber/danielle-heberling-dot-xyz&quot;&gt;can view the code source on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;h3&gt;CI/CD&lt;/h3&gt;
&lt;p&gt;When a new site is created on Netlify, you can &lt;a href=&quot;https://docs.netlify.com/configure-builds/repo-permissions-linking/#link-a-git-repository&quot;&gt;link it to a GitHub repository&lt;/a&gt;. You then have the options to set up CI/CD to perform build, deploys, or previews whenever you push or merge to a specified branch.&lt;/p&gt;
&lt;p&gt;For my site, I decided to keep it simple and stuck with the defaults. Whenever I put up a Pull Request against the &lt;code&gt;main&lt;/code&gt; branch, Netlify runs a build and gives me a preview link to view the site with changes. Whenever I merge into the &lt;code&gt;main&lt;/code&gt; branch, Netlify builds and deploys my site for me.&lt;/p&gt;
&lt;h3&gt;Linking the deploy to updating my README&lt;/h3&gt;
&lt;p&gt;Later in my journey, I wanted to learn about &lt;a href=&quot;https://github.com/features/actions&quot;&gt;GitHub actions&lt;/a&gt; and built an action that pulls the three most recent blog blog down from my blog&apos;s RSS feed (included with Gatsby) and writes them to the markdown file in my &lt;a href=&quot;https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/managing-your-profile-readme&quot;&gt;profile README repo&lt;/a&gt;. You can read about the process of creating it in &lt;a href=&quot;https://www.danielleheberling.xyz/blog/github-actions/&quot;&gt;my past blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A feature that I wanted was a connection between deploying my blog to prod and having the GitHub action scrape my RSS feed to update my README file. In the past, I had it running as a recurring Cron job, but I wanted something that would have a guarantee of the latest three blog blog being up to date. I had looked into using Netlify&apos;s &lt;a href=&quot;https://docs.netlify.com/site-deploys/notifications/#outgoing-webhooks&quot;&gt;outgoing webhook feature&lt;/a&gt;, but found it to be challenging to customize the POST request to match what was needed to trigger a GitHub action.&lt;/p&gt;
&lt;p&gt;I realized that I always wait for the Netlify deploy to prod to complete, manually check the site, and delete the branch...so I decided to utilize the branch deletion action to trigger the scrape of the RSS feed to update my README repo. I then discovered that the GitHub action &lt;a href=&quot;https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow&quot;&gt;repository_dispatch&lt;/a&gt; would be helpful in triggering the scrape of my RSS feed on branch delete.&lt;/p&gt;
&lt;p&gt;I first created a token to save as a secret in my &lt;code&gt;danielle-heberling-dot-xyz&lt;/code&gt; (blog) repository that gave it read/write permissions to my &lt;code&gt;deeheber&lt;/code&gt; (README) repository. I then added a workflow to my &lt;code&gt;danielle-heberling-dot-xyz&lt;/code&gt; repo to post an event to my &lt;code&gt;deeheber&lt;/code&gt; repo via &lt;code&gt;repository_dispatch&lt;/code&gt;. It looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: alert README repo of changes on main

on:
  workflow_dispatch:
  # Runs on git reference deletion (branch or tag)
  delete:

jobs:
  dispatch:
    runs-on: ubuntu-latest
    steps:
      - name: Update README in deeheber/deeheber repo
        run: |
          curl -H &quot;Accept: application/vnd.github.everest-preview+json&quot; \
          -H &quot;Authorization: token ${{ secrets.DISPATCH_TOKEN }}&quot; \
          --request POST \
          --data &apos;{&quot;event_type&quot;: &quot;scrape&quot;, &quot;client_payload&quot;: {&quot;ref&quot;: &quot;${{ github.ref }}&quot;}&apos; https://api.github.com/repos/deeheber/deeheber/dispatches
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I noticed there were some pre-made actions that can do this for me, but I decided that since this is a simple &lt;code&gt;POST&lt;/code&gt; request that I can do this via &lt;code&gt;curl&lt;/code&gt; without the extra dependency of a 3rd party maintained GitHub action.&lt;/p&gt;
&lt;p&gt;Then on the receiving repo (&lt;code&gt;deeheber&lt;/code&gt;), I added an action to listen for that &lt;code&gt;scrape&lt;/code&gt; &lt;code&gt;event_type&lt;/code&gt; to trigger scraping my RSS feed and writing the three most recent blog blog/links to the &lt;code&gt;README&lt;/code&gt; file. The &lt;code&gt;on&lt;/code&gt; section is the only part that I had to change for this to work:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: Build README

on:
  workflow_dispatch:
  repository_dispatch:
    types: [scrape]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Check out repo
        uses: actions/checkout@v3
      - name: Use Node
        uses: actions/setup-node@v3
        with:
          node-version: &quot;18.x&quot;
      - name: Install node dependencies
        run: npm install
      - name: Check for RSS feed updates
        run: npm run scrape
      - name: Commit and push
        run: |-
          git diff
          git config --global user.email &quot;actions@users.noreply.github.com&quot;
          git config --global user.name &quot;README-bot&quot;
          git add -A
          git commit -m &quot;Update content&quot; || git commit --allow-empty -m &quot;Empty commit&quot;
          git push
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On both actions, I decided to keep the &lt;a href=&quot;https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow&quot;&gt;workflow_dispatch&lt;/a&gt; event in place, so I could manually run these workflows.&lt;/p&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;There are many different ways that I could&apos;ve accomplished this, but this is what I ended up implementing. It is not perfect, but more than good enough for a personal blog site in my opinion.&lt;/p&gt;
&lt;p&gt;Since I&apos;m already using Netlify for hosting, I figured it was easiest to utilize their built in CI/CD services. I could&apos;ve spent time rolling this myself using various AWS services or other things, but I wanted something that I didn&apos;t need to spend that much time on with the initial setup and/or maintence.&lt;/p&gt;
&lt;p&gt;Thank you to Netlify, Gatsby, and GitHub for having a very permissive free tier that enables me to keep my blog up and running. I only need to pay for my custom domain name (danielleheberling.xyz) in order to keep things running.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Edit October 8, 2023:
Sadly Netlify is amongst the many tech companies &lt;a href=&quot;https://www.netlify.com/blog/ceo-announcement-to-the-netlify-team/&quot;&gt;that laid off a lot of employees&lt;/a&gt; including folks on the Gatsby team. My thoughts are with those who lost their jobs. I hope they find new opportunities soon. I&apos;m not sure what this means for the future of Netlify and Gatsby, but I hope they can continue to provide a great service to their customers. Time will tell.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Edit July 8, 2024
Update: The blog website mentioned in this post has been retired. It has been rebuilt using Astro and deployed to Cloudflare Pages.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Career Reflections</title><link>https://danielleheberling.xyz/blog/career-refelctions/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/career-refelctions/</guid><description>Career Reflections</description><pubDate>Fri, 07 Jul 2023 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/hands.jpg&quot; alt=&quot;Hands&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@claybanks?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Clay Banks&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/LjqARJaJotc?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Growing up, I was given advice from adults often. It was typically some variant of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to college&lt;/li&gt;
&lt;li&gt;Respect authority&lt;/li&gt;
&lt;li&gt;Work hard&lt;/li&gt;
&lt;li&gt;You&apos;ll be rewarded&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Early in my career, I followed this advice and it didn&apos;t work for me. I could write an entire book on why that was the case, but for now I&apos;ll spare you the details.&lt;/p&gt;
&lt;p&gt;On top of this bad advice, I found that in American culture the first thing people will often ask when meeting someone for the first time is what they do for a living. How you answer this question will result in judgement that may or may not be accurate.&lt;/p&gt;
&lt;p&gt;As my career progressed, I realized that I aspire to have a legacy beyond the company that I work for and my job title. As a result, I came up with these five core principles. Here they are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Be kind to people despite their position in the overall hierarchy.&lt;/strong&gt; This includes both official heiarchy such as at a company and social heiarchy in a community setting. You never know when circumstances might change and when you might run into an individual again. I&apos;ve mentored people who have exceeded my technical level of expertise as well as seen people who worked for competitors become my coworker.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Work in public.&lt;/strong&gt; And don&apos;t limit what you publicly share to wins. Also share your challenges, so people can have a full picture of the work that went into the finished product. A rising tide lifts all boats.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Approach disagreement with a curious mind.&lt;/strong&gt; Don&apos;t join a conversation expecting to &quot;be right.&quot; Ask questions if someone else&apos;s perspective doesn&apos;t match yours. You might not end up agreeing in the end, but it&apos;s likely you&apos;ll learn something new.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Don&apos;t jump to technology to solve a problem unless it&apos;s the best tool for the job.&lt;/strong&gt; I&apos;ve worked many places where tech was thrown at problems in an attempt to solve them. This ended up causing more confusion while the root cause of the original problems were unaddressed. Sometimes problems can be resolved by training people or by updating a process.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Leave things better than you found them.&lt;/strong&gt; Most individuals apspire to change the world, but it is challenging to do this at the individual level. At the very least, we can do our part by improving the current situation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;While thinking through these core principles, I&apos;ve realized that people and community are why I&apos;m still here in tech. To me, community looks like current and former coworkers, colleagues I met at community events, and people who use the things I build. I&apos;ve gotten to where I am because people were willing to take a chance on me. I&apos;d love to pay it forward by taking a chance on others and doing whatever I can to lift others up.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>AWS Speakers Directory - Adding AI Functionality</title><link>https://danielleheberling.xyz/blog/hackathon-ai/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/hackathon-ai/</guid><description>AWS Speakers Directory - Adding AI Functionality</description><pubDate>Thu, 29 Jun 2023 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/color-mountains.jpg&quot; alt=&quot;Mountains&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@8moments?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Simon Berger&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/s/photos/rainbow-mountain?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;In June 2023, &lt;a href=&quot;https://dev.to/lockhead&quot;&gt;Johannes Koch&lt;/a&gt;, &lt;a href=&quot;https://dev.to/elthrasher&quot;&gt;Matt Morgan&lt;/a&gt;, &lt;a href=&quot;https://dev.to/jumic&quot;&gt;Julian Michel&lt;/a&gt;, and myself participated in the AI themed hackathon for the AWS Community Builders. The hackathon challenge was to create a tool using the &lt;a href=&quot;https://huggingface.co/docs/transformers/main/transformers_agents&quot;&gt;Transformer Tools&lt;/a&gt; framework.&lt;/p&gt;
&lt;h2&gt;The Project&lt;/h2&gt;
&lt;p&gt;Johannes had the idea that we agreed to implement. We ended up building an AWS community focused &lt;a href=&quot;https://speakers.awscommunitybuilders.org/#/&quot;&gt;speaker directory website&lt;/a&gt;. Think an awesome AWS focused version of &lt;a href=&quot;https://sessionize.com/&quot;&gt;sessionize&lt;/a&gt; where event organizers can create an event. Speakers can create a speaker profile and use the site to submit talks to the various events. For more details on the project, take a look at &lt;a href=&quot;https://dev.to/aws-builders/presenting-aws-speakers-directory-an-ai-hackathon-project-19je&quot;&gt;Matt&apos;s overview post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This post focuses on how we utilized the Hugging Face transformers agent to add functionality to our speaker directory.&lt;/p&gt;
&lt;p&gt;Here&apos;s links to blog by my teammates on other topics related to the project&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/aws-builders/presenting-aws-speakers-directory-an-ai-hackathon-project-19je&quot;&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/aws-builders/amplify-sdk-for-flutter-developer-experience-and-challenges-in-a-hackathon-2e15&quot;&gt;How we used Amplify/Flutter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/aws-builders/a-real-project-with-codecatalyst-our-hackathon-gave-us-a-good-insight-into-what-works-and-what-doesnt-1e79&quot;&gt;How we used CodeCatalyst&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/aws-builders/appsync-merged-api-our-real-project-experience-as-part-of-our-hackathon-2m96&quot;&gt;How we used AppSync merged APIs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;AI Functionality&lt;/h2&gt;
&lt;p&gt;The AI functionality that we added to enhance the speaker directory was&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Generate an image whenever a speaker adds a new talk&lt;/li&gt;
&lt;li&gt;Generate appropriate tags and add tags to newly added talks&lt;/li&gt;
&lt;li&gt;Allow event organizers to get recommendations of talks that fit their event based off of a set of tags&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Implementation Overview&lt;/h2&gt;
&lt;h3&gt;Architecture&lt;/h3&gt;
&lt;p&gt;Features #1 and #2 were set this up in a similar way. We have a DynamoDB (DDB) event source that triggers both Functions via a DDB stream whenever a new talk is added via an AppSync GraphQL (GQL) mutation. The two Functions then do the work of generating images and dynamically adding tags to the talk.&lt;/p&gt;
&lt;p&gt;The final step of the tag generation Function (feature #2) is to insert the generated tags into our DDB table. We then have another DDB stream that triggers when a tag is inserted that then triggers a Function that does some aggregation for us to support feature #3.&lt;/p&gt;
&lt;p&gt;For feature #3, we set up a GQL mutation that accepts an array of tags and queries our DDB table to retrieve a list of talks (recommendations) that also contain those tags.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/architecture-diagram.png&quot; alt=&quot;High level architecture diagram&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;More on the Functions&lt;/h3&gt;
&lt;p&gt;Everyone on the team was new to the world of AI/ML, one thing we quickly realized was that these Functions were very heavy in terms of the size/number of dependencies needed and in terms of the compute power needed to complete these complex AI processing tasks.&lt;/p&gt;
&lt;p&gt;Inspired by &lt;a href=&quot;https://aws.amazon.com/blogs/compute/hosting-hugging-face-models-on-aws-lambda/&quot;&gt;this blog post&lt;/a&gt;, we decided to utilize an EFS file system to cache some artifacts to lessen cold start on subsequent requests to these AI processor Functions. Due to the number of dependencies needed, we decided to take advantage of packing our Function code using Docker containers instead of .zip files to take advantage of the &lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html&quot;&gt;higher size limit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Matt also learned that sometimes maintainers are not great at updating the dependencies in their Docker images, and he ended up creating a custom Docker image based off of the image used in that linked blog post with updated Python and gradio-tools (we needed it for image generation).&lt;/p&gt;
&lt;h3&gt;Function code - Generate Image&lt;/h3&gt;
&lt;p&gt;This Function responds to the DDB insert event when a new talk is added, calls the Hugging Face agent to generate an image based off of the description of the talk, and puts the generated image into an S3 bucket.&lt;/p&gt;
&lt;p&gt;The new part for us was working with the Hugging Face agent. We first needed to login using credentials stored in Secrets Manager and generate the image using stable diffusion/gradio tools.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from gradio_tools import StableDiffusionPromptGeneratorTool
from huggingface_hub import login
import boto3
from transformers import HfAgent, Tool

gradio_tool = StableDiffusionPromptGeneratorTool()
tool = Tool.from_gradio(gradio_tool)
ssm = boto3.client(&quot;ssm&quot;)

agent_token = ssm.get_parameter(
    Name=&quot;/community-speakers/agent-token&quot;, WithDecryption=True
)
login(agent_token[&quot;Parameter&quot;][&quot;Value&quot;])

agent = HfAgent(
    &quot;https://api-inference.huggingface.co/models/bigcode/starcoder&quot;,
    additional_tools=[tool],
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The entire Function code looked like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import boto3

import os
from gradio_tools import StableDiffusionPromptGeneratorTool
from huggingface_hub import login
from io import BytesIO
from transformers import HfAgent, Tool
import json

BUCKET_NAME = os.getenv(&quot;BUCKET_NAME&quot;)
gradio_tool = StableDiffusionPromptGeneratorTool()
s3 = boto3.resource(&quot;s3&quot;)
ssm = boto3.client(&quot;ssm&quot;)
tool = Tool.from_gradio(gradio_tool)

agent_token = ssm.get_parameter(
    Name=&quot;/community-speakers/agent-token&quot;, WithDecryption=True
)
login(agent_token[&quot;Parameter&quot;][&quot;Value&quot;])
agent = HfAgent(
    &quot;https://api-inference.huggingface.co/models/bigcode/starcoder&quot;,
    additional_tools=[tool],
)


def handler(event, context):
    print(json.dumps(event))

    image = event[&quot;Records&quot;][0][&quot;dynamodb&quot;][&quot;NewImage&quot;]

    image = agent.run(
        &quot;Generate an image of the `prompt` after improving it.&quot;,
        remote=True,
        prompt=f&quot;{image[&apos;description&apos;][&apos;S&apos;]} {image[&apos;title&apos;][&apos;S&apos;]}&quot;,
    )
    buffer = BytesIO()

    image.to_raw().save(buffer, &quot;png&quot;)
    buffer.seek(0)

    prefix = &quot;ai-generated&quot;
    newId = (
        event[&quot;Records&quot;][0][&quot;dynamodb&quot;][&quot;Keys&quot;][&quot;pk&quot;][&quot;S&quot;]
        .replace(&quot;#&quot;, &quot;&quot;)
        .replace(&quot; &quot;, &quot;&quot;)
    )
    s3.Bucket(BUCKET_NAME).put_object(Body=buffer, Key=f&quot;{prefix}/{str(newId)}.png&quot;)
    response = {&quot;statusCode&quot;: 200, &quot;body&quot;: &quot;ok&quot;}
    return response
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Function Code - Generate Tags&lt;/h3&gt;
&lt;p&gt;This function utilized a similar pattern in terms of connecting to Hugging Face; however, we utilized a different tools for this - the Bart model.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import boto3
import os
from datetime import datetime
from transformers import pipeline

oracle = pipeline(model=&quot;facebook/bart-large-mnli&quot;)

tableName = os.getenv(&quot;TABLE_NAME&quot;)

client = boto3.client(&quot;dynamodb&quot;)


def handler(event, context):
    tags = get_tags()

    candidate_labels = [t[&quot;tagName&quot;][&quot;S&quot;].lower() for t in tags]
    image = event[&quot;Records&quot;][0][&quot;dynamodb&quot;][&quot;NewImage&quot;]

    # Use both the title and the description of the talk to identify tags
    identifiedTags = oracle(
        f&apos;{image[&quot;title&quot;][&quot;S&quot;].lower()} {image[&quot;description&quot;][&quot;S&quot;].lower()}&apos;,
        candidate_labels=candidate_labels,
    )

    labels = identifiedTags[&quot;labels&quot;]
    scores = identifiedTags[&quot;scores&quot;]

    selectedTags = [t for index, t in enumerate(labels) if scores[index] &amp;gt; 0.3]

    save_tags(image, selectedTags)

    response = {&quot;statusCode&quot;: 200, &quot;body&quot;: identifiedTags}
    return response


# Query all tags to get a list of label candidates.
def get_tags():
    response = client.query(
        ExpressionAttributeNames={&quot;#pk&quot;: &quot;pk&quot;},
        ExpressionAttributeValues={&quot;:tag&quot;: {&quot;S&quot;: &quot;tag&quot;}},
        KeyConditionExpression=&quot;#pk = :tag&quot;,
        TableName=tableName,
    )

    tags = response[&quot;Items&quot;]

    # Need the loop to get all tags.
    while &quot;LastEvaluatedKey&quot; in response:
        response = client.query(
            ExclusiveStartKey=response[&quot;LastEvaluatedKey&quot;],
            ExpressionAttributeNames={&quot;#pk&quot;: &quot;pk&quot;},
            ExpressionAttributeValues={&quot;:tag&quot;: {&quot;S&quot;: &quot;tag&quot;}},
            KeyConditionExpression=&quot;#pk = :tag&quot;,
            TableName=tableName,
        )
        tags.update(response[&quot;Items&quot;])

    return tags


# We&apos;re going to save any tags that the creator may have neglected to apply.
def save_tags(image, tags):
    tagList = list(set([t[&quot;S&quot;] for t in image[&quot;tags&quot;][&quot;L&quot;]] + tags))
    print(tagList)

    # Update the original talk with any new tags
    client.update_item(
        ExpressionAttributeNames={
            &quot;#tags&quot;: &quot;tags&quot;,
            &quot;#_md&quot;: &quot;_md&quot;,
        },
        ExpressionAttributeValues={
            &quot;:tags&quot;: {&quot;L&quot;: [{&quot;S&quot;: t} for t in tagList]},
            &quot;:_md&quot;: {&quot;S&quot;: datetime.now().isoformat()},
        },
        Key={
            &quot;pk&quot;: {&quot;S&quot;: image[&quot;pk&quot;][&quot;S&quot;]},
            &quot;sk&quot;: {&quot;S&quot;: image[&quot;sk&quot;][&quot;S&quot;]},
        },
        UpdateExpression=&quot;SET #tags = :tags, #_md = :_md&quot;,
        TableName=tableName,
    )

    # Save any new tags
    for tag in tags:
        try:
            client.put_item(
                ConditionExpression=&quot;attribute_not_exists(pk) and attribute_not_exists(sk)&quot;,
                Item={
                    &quot;pk&quot;: {&quot;S&quot;: image[&quot;pk&quot;][&quot;S&quot;]},
                    &quot;sk&quot;: {&quot;S&quot;: f&quot;tag#{tag}&quot;},
                    &quot;gsi1pk&quot;: {&quot;S&quot;: f&quot;tag#{tag}&quot;},
                    &quot;gsi1sk&quot;: {&quot;S&quot;: image[&quot;pk&quot;][&quot;S&quot;]},
                    &quot;tagName&quot;: {&quot;S&quot;: tag},
                    &quot;title&quot;: {&quot;S&quot;: image[&quot;title&quot;][&quot;S&quot;]},
                    &quot;_et&quot;: {&quot;S&quot;: &quot;TAG&quot;},
                    &quot;_ct&quot;: {&quot;S&quot;: datetime.now().isoformat()},
                    &quot;_md&quot;: {&quot;S&quot;: datetime.now().isoformat()},
                },
                TableName=tableName,
            )
        except client.exceptions.ConditionalCheckFailedException:
            print(&quot;Tag already existed!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Function Code - Aggregate Tags&lt;/h3&gt;
&lt;p&gt;This is triggered whenever a new tag is inserted into our table. As you&apos;ll see in the recommend talks Function, these aggregations support that feature. Because we don&apos;t need to utilize the Hugging Face transformers in this Function, we decided to do this one in Typescript rather than Python.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { UpdateCommand } from &quot;@aws-sdk/lib-dynamodb&quot;
import { type DynamoDBStreamEvent } from &quot;aws-lambda&quot;

import { docClient, tableName } from &quot;../util/docClient&quot;

export const handler = async (event: DynamoDBStreamEvent): Promise&amp;lt;void&amp;gt; =&amp;gt; {
  for (const record of event.Records) {
    const image = record.dynamodb?.NewImage
    if (!image?.tagName.S) continue

    const tagName = image.tagName.S

    const command = new UpdateCommand({
      ExpressionAttributeNames: {
        &quot;#quantity&quot;: &quot;quantity&quot;,
        &quot;#tagName&quot;: &quot;tagName&quot;,
        &quot;#_et&quot;: &quot;_et&quot;,
        &quot;#_ct&quot;: &quot;_ct&quot;,
        &quot;#_md&quot;: &quot;_md&quot;,
      },
      ExpressionAttributeValues: {
        &quot;:one&quot;: 1,
        &quot;:tagName&quot;: tagName,
        &quot;:timestamp&quot;: new Date().toISOString(),
        &quot;:zero&quot;: 0,
        &quot;:_et&quot;: &quot;TAG_COUNT&quot;,
      },
      Key: {
        pk: &quot;tag&quot;,
        sk: `tag#${tagName}`,
      },
      TableName: tableName,
      UpdateExpression: `SET #quantity = :one + if_not_exists(#quantity, :zero),
                             #tagName = :tagName,
                             #_et = :_et,
                             #_ct = if_not_exists(#_ct, :timestamp),
                             #_md = :timestamp
                         `,
    })

    await docClient.send(command)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Function Code - Recommend Talks&lt;/h3&gt;
&lt;p&gt;This Function is set up as a GQL resolver that is called from our Flutter frontend. We decided to set this one up as a Lambda resolver rather than a JS pipeline resolver because this feature is making multiple queries to our table. This Function also does not utilize the Hugging Face transformers, so we decided to write it using Typescript rather than Python.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { QueryCommand } from &quot;@aws-sdk/lib-dynamodb&quot;
import { type AppSyncResolverEvent } from &quot;aws-lambda&quot;

import { type Event, type Talk } from &quot;../API&quot;
import { docClient, tableName } from &quot;../util/docClient&quot;

export const handler = async (
  event: AppSyncResolverEvent&amp;lt;{ event: Event }&amp;gt;,
): Promise&amp;lt;Talk[]&amp;gt; =&amp;gt; {
  const eventTags = event.arguments.event.tags
  const talks: Array&amp;lt;Talk &amp;amp; { matches?: number }&amp;gt; = []
  for (const eventTag of eventTags) {
    const queryTagsCommand = new QueryCommand({
      ExpressionAttributeNames: {
        &quot;#gsi1pk&quot;: &quot;gsi1pk&quot;,
      },
      ExpressionAttributeValues: {
        &quot;:gsi1pk&quot;: `tag#${eventTag}`,
      },
      KeyConditionExpression: &quot;#gsi1pk = :gsi1pk&quot;,
      IndexName: &quot;gsi1&quot;,
      TableName: tableName,
    })
    const tagsResult = await docClient.send(queryTagsCommand)
    if (!tagsResult.Items) continue

    for (const tagResult of tagsResult.Items) {
      const queryTalksCommand = new QueryCommand({
        ExpressionAttributeNames: {
          &quot;#pk&quot;: &quot;pk&quot;,
          &quot;#sk&quot;: &quot;sk&quot;,
        },
        ExpressionAttributeValues: {
          &quot;:pk&quot;: tagResult.gsi1sk,
          &quot;:talk&quot;: &quot;talk&quot;,
        },
        KeyConditionExpression: &quot;#pk = :pk and begins_with(#sk, :talk)&quot;,
        TableName: tableName,
      })
      const talksResult = await docClient.send(queryTalksCommand)
      if (talksResult.Items) {
        talksResult.Items.forEach((talk) =&amp;gt; {
          if (!talks.find((t) =&amp;gt; t.pk === talk.pk)) {
            talks.push(talk as Talk)
          }
        })
      }
    }
  }

  talks.forEach((talk) =&amp;gt; {
    const matchCount = (talk.tags || []).filter((t) =&amp;gt;
      eventTags.includes(t),
    ).length
    talk.matches = matchCount
  })

  return talks
    .filter((t) =&amp;gt; t.matches)
    .sort((a, b) =&amp;gt; (a.matches &amp;amp;&amp;amp; b.matches &amp;amp;&amp;amp; a.matches &amp;lt; b.matches ? 1 : -1))
    .slice(0, 10)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Trade offs&lt;/h2&gt;
&lt;p&gt;We noticed that generative AI takes a lot longer to process than your typical CRUD-like API against a database. For this reason a lot of our processing was conducted in an async manner. Not necessarily a trade off, but we did need to change our mental model in how we approached this as it was not going to be a good user experience if we stuck with the more traditional request/response model.&lt;/p&gt;
&lt;p&gt;An issue that we ran into was that the shared AWS account we were using for the hackathon was considered &quot;new&quot; and had some additional usage limits on it. In our case, the Lambda Functions had an upper limit of &lt;code&gt;3008&lt;/code&gt; mb. Most of the time, this is more than enough memory for a request/response API endpoint that interfaces with a database; however, with AI/ML having more memory would&apos;ve helped with our slow responses during the processing.&lt;/p&gt;
&lt;p&gt;We wrote a ticket to AWS support and were told that these limits are in place on newer accounts to prevent surprise bills. Then they told us they couldn&apos;t raise the limit until they saw more activity in the account. I can&apos;t speak for my teammates, but it is my personal hope that AWS can improve this process and live up to the &quot;customer obsession&quot; leadership principle. We ran into other issues with the &quot;new&quot; account limits, but that is the only one I&apos;ll mention here since it is directly related to our AI integrations.&lt;/p&gt;
&lt;h3&gt;Post Hackathon Plans&lt;/h3&gt;
&lt;p&gt;Given the timeline for this hackathon (30 days) and the fact that no one on this team had extensive experience with AI/ML, we took some shortcuts when it came to the talk recommendations feature. Overall, I&apos;m happy with how it came out. Post hackathon, we plan to explore creating and training our own custom data model based off of what was entered into our DDB table in order to see if we can further lean on AI/ML and get more specific (beyond tags) recommendations.&lt;/p&gt;
&lt;p&gt;I would&apos;ve liked to go beyond the Hugging Face transformers and see if some of this could be implemented using &lt;a href=&quot;https://docs.aws.amazon.com/sagemaker/latest/dg/realtime-endpoints.html&quot;&gt;SageMaker endpoints&lt;/a&gt; or another AI platform. Potential benefits that I saw with SageMaker endpoints over our implementation was that this is in the AWS ecosystem for a (hopefully) tighter integration and being able to speed up processing. I learned from this hackathon that Hugging Face is great, but there might also be other tools out there that better suit our needs. We won&apos;t know for sure until we try!&lt;/p&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;Overall this was a fun collaboration. It feels good to contribute to building something that the community will use and to learn from my peers within the community while doing so.&lt;/p&gt;
&lt;p&gt;On a personal level, life got in the way and I did not have as much time as I would&apos;ve liked to have to dedicate to this project. I thank my team mates for their hard work via ideas and implementation. Major props to Matt for doing a lot of the AI implementation. 🙌🏻&lt;/p&gt;
&lt;p&gt;If you would like to participate with us building out the &lt;a href=&quot;https://speakers.awscommunitybuilders.org/#/&quot;&gt;AWS Speaker Directory&lt;/a&gt; to help AWS User Group leaders find speakers, please reach out of any of us.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>The Problem with &quot;It Works on My Computer&quot;</title><link>https://danielleheberling.xyz/blog/works-on-my-computer/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/works-on-my-computer/</guid><description>The Problem with &quot;It Works on My Computer&quot;</description><pubDate>Mon, 20 Mar 2023 10:12:03 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Disclaimer: This is an opinionated post based off of my own experience. Your experience and opinions may differ and that is okay. I welcome your respectful and engaged conversation. Most of my cloud experience is with Amazon Web Services, so this post will be AWS heavy. A lot of this applicable to other cloud providers though.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Scenario #1: Working on a serverless first team&lt;/h2&gt;
&lt;p&gt;We hire a new person that&apos;s new to serverless. Regardless of how long they&apos;ve been a professional software developer, the first question is often some variation of:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;How do I run this locally on my computer?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are many tools out there that can emulate AWS on your computer, but I&apos;ve experienced some issues with this approach.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The tools are not always accurate or up to date with the latest features available in the cloud.&lt;/li&gt;
&lt;li&gt;The environment on your computer vs in the cloud will be different. Some common examples include: environment variables, database connections, and permissions to access resources.&lt;/li&gt;
&lt;li&gt;Maintaining the tooling required to emulate can sometimes be more work than deploying your changes and testing in the cloud. A lot of them require knowledge of Docker and Docker networking. My biased opinion: at that point why go serverless in the first place?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Scenario #2: Working on a non-serverless first team&lt;/h2&gt;
&lt;p&gt;Some companies use more traditional infrastructure such as EC2 instances and RDS. Running a local version of a Node.js server against a local Postgres database is great for fast, iterative development and works well most of the time.&lt;/p&gt;
&lt;p&gt;Where this falls apart is when integrating another AWS service such as S3. I&apos;ve seen software developers who do not have much cloud experience, but are great at writing code do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Authenticate with AWS using administrative access credentials locally on their computer.&lt;/li&gt;
&lt;li&gt;Run a server locally on their computer which uploads a file to an S3 bucket located in a non-prod AWS account.&lt;/li&gt;
&lt;li&gt;It works!&lt;/li&gt;
&lt;li&gt;They submit their code and it gets merged into the main branch.&lt;/li&gt;
&lt;li&gt;The code gets deployed to a non-prod environment (staging or something similar).&lt;/li&gt;
&lt;li&gt;They test uploading to the S3 bucket and it fails on the non-prod environment.&lt;/li&gt;
&lt;li&gt;&quot;I don&apos;t know what&apos;s going on because it worked locally for me!&quot; 🤔&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/assets/confused-face.jpg&quot; alt=&quot;Clouds Image&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@sammywilliams?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Sander Sammy&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/ufgOEVZuHgM?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Someone more experienced with IAM recognizes that the permissions on the cloud and the local environment differ.&lt;/li&gt;
&lt;li&gt;The more experienced person fixes the permissions issues in the cloud. 🚀&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Unsolicited advice&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Have a non-prod cloud environment where you can test code changes before deploying to prod. It&apos;s good to catch problems before your end users do. ❤️&lt;/li&gt;
&lt;li&gt;If you&apos;re running servers locally as part of your development loop, authenticate locally using similar permissions that the production server uses. This will help catch permissions issues sooner. 🤓&lt;/li&gt;
&lt;li&gt;If you&apos;re working on a serverless first team, do not attempt to run your application locally. Instead, deploy your code to a sandbox account and test it there. One AWS account per developer. It&apos;s also a great way to get more familiar with troubleshooting in the cloud. 🙌🏻&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Reflections&lt;/h2&gt;
&lt;p&gt;I&apos;ve seen these scenarios play out on many different teams at different companies. The reason is due to developers needing to change their mindset.&lt;/p&gt;
&lt;p&gt;Today it is very common for software applications to use cloud services. &lt;strong&gt;We as developers should also evolve our development environments to match the new paradigm.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s build better software together. Good luck out there. 💪🏻&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Serverless Weather Reporting with AWS Step Functions and CDK</title><link>https://danielleheberling.xyz/blog/serverless-weather-reporting/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/serverless-weather-reporting/</guid><description>Serverless Weather Reporting with AWS Step Functions and CDK</description><pubDate>Sun, 05 Mar 2023 10:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/snow-trees.jpg&quot; alt=&quot;Snow on evergreen trees&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@devjustesen?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Devin Justesen&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/photos/QrL-aRyuf_8?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;I live in the &lt;a href=&quot;https://en.wikipedia.org/wiki/Pacific_Northwest&quot;&gt;Pacific Northwest&lt;/a&gt;. Approximately 1-2 times per year, it snows. When this happens, people act shocked because &quot;it never happens here.&quot;* Our local governments do not have many snow plows nor do they salt the roads much. This means that when it snows, the roads are highly dangerous. The city of Portland, Oregon often shuts down for multiple days.&lt;/p&gt;
&lt;p&gt;Former residents of Portland created a website, &lt;a href=&quot;http://isitsnowinginpdx.com/&quot;&gt;isitsnowinginpdx.com&lt;/a&gt;. I cannot speak for their motivations, but I find this site both entertaining and informative.&lt;/p&gt;
&lt;p&gt;After moving out of Portland into the suburbs, I noticed that the site was not as accurate, so I decided to build my own site &lt;a href=&quot;http://isitsnowinginhillsboro.com/&quot;&gt;isitsnowinginhillsboro.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Architecture Overview&lt;/h2&gt;
&lt;p&gt;Behind the scenes, this site is using AWS Serverless technologies. &lt;a href=&quot;https://aws.amazon.com/s3/&quot;&gt;S3&lt;/a&gt; hosts site assets, a &lt;a href=&quot;https://aws.amazon.com/step-functions/&quot;&gt;Step Function&lt;/a&gt; updates the site, and &lt;a href=&quot;https://aws.amazon.com/eventbridge/scheduler/&quot;&gt;EventBridge Scheduler&lt;/a&gt; triggers the Step Function every 10 minutes.&lt;/p&gt;
&lt;p&gt;I made the decision to &lt;strong&gt;not&lt;/strong&gt; put &lt;a href=&quot;https://aws.amazon.com/cloudfront/&quot;&gt;CloudFront&lt;/a&gt; in front of this S3 Bucket, because this is a region specific website. Most people viewing this site live in the area and adding a CDN feels like unnecessary complexity given the use case. As a result, the site renders of &lt;code&gt;HTTP&lt;/code&gt; rather than &lt;code&gt;HTTPS&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&apos;s a breakdown of what the Step Function workflow does:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get the site&apos;s current status (&lt;code&gt;snow&lt;/code&gt; or &lt;code&gt;no snow&lt;/code&gt;) from a DynamoDB table&lt;/li&gt;
&lt;li&gt;Check the current weather using the &lt;a href=&quot;https://openweathermap.org/api&quot;&gt;OpenWeatherMap API&lt;/a&gt; via a Lambda Function&lt;/li&gt;
&lt;li&gt;A choice state compares the two values&lt;/li&gt;
&lt;li&gt;If the two values match, nothing happens and the workflow ends&lt;/li&gt;
&lt;li&gt;If the two values do not match, it updates the site to show the current weather obtained from the API
&lt;ul&gt;
&lt;li&gt;A Lambda Function generates the HTML and places that HTML file into an S3 bucket (more on this later)&lt;/li&gt;
&lt;li&gt;If the Lambda Function is successful, then it updates the site status in DynamoDB&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&apos;s a visual representation of the workflow:
&lt;img src=&quot;/assets/weather-workflow.png&quot; alt=&quot;Weather site workflow&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Challenges&lt;/h2&gt;
&lt;p&gt;Overall things went smoothly. I did run into a few tiny issues.&lt;/p&gt;
&lt;h3&gt;Issue 1: EventBridge Scheduler and CDK&lt;/h3&gt;
&lt;p&gt;EventBridge Scheduler is a new-ish feature and does not have an L2 construct. I was able to implement what I needed for this site using an L1 construct, but with an L1 construct you&apos;re writing Cloudformation without the benefits that an L2 construct provides. In this case, I had to define an &lt;a href=&quot;https://github.com/deeheber/weather-site/blob/blog-post/lib/weather-site-stack.ts#L219-L237&quot;&gt;IAM Role to allow the Scheduler to invoke the Step Function and an inline policy&lt;/a&gt;. This is likely a feature that would be included in an L2 construct.&lt;/p&gt;
&lt;p&gt;More info on L1 vs L2 constructs &lt;a href=&quot;https://docs.aws.amazon.com/cdk/v2/guide/constructs.html#constructs_l1_using&quot;&gt;here&lt;/a&gt;. I plan to keep an eye on this open &lt;a href=&quot;https://github.com/aws/aws-cdk-rfcs/issues/474&quot;&gt;RFC&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Issue 2: HTML Generation&lt;/h3&gt;
&lt;p&gt;Step Functions have &lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2021/09/aws-step-functions-200-aws-sdk-integration/&quot;&gt;direct integrations&lt;/a&gt; with many services. The advantage of this is you do not have write a Lambda Function to make those AWS SDK calls and can save money as well as enjoy the built in error/retry logic that comes with the Step Function service. I used the direct integration for the two calls to DynamoDB and it worked quite well.&lt;/p&gt;
&lt;p&gt;When it came to generating HTML and using the S3 PutObject, the direct integration added quotes around the HTML. This resulted in an HTML document that looked similar to &lt;code&gt;&quot;&amp;lt;h1&amp;gt;My site&amp;lt;h1&amp;gt;&quot;&lt;/code&gt;. The extra quotation marks caused the page to not render properly in the browser. I eventually got this working with a Lambda Function by &lt;a href=&quot;https://github.com/deeheber/weather-site/blob/blog-post/src/functions/update-site.ts#L68&quot;&gt;sending the Body as a Buffer&lt;/a&gt;, but I prefer the direct integration.&lt;/p&gt;
&lt;h2&gt;Your Turn&lt;/h2&gt;
&lt;p&gt;The code is open source and you can view it &lt;a href=&quot;https://github.com/deeheber/weather-site/tree/main&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can clone the repo following the instructions in the &lt;a href=&quot;https://github.com/deeheber/weather-site/blob/main/README.md&quot;&gt;README.md&lt;/a&gt; file to create your own weather site. Plug in the latitude and longitude of your city. Also, it&apos;s not limited to snow! Check out the &lt;code&gt;Main&lt;/code&gt; weather types &lt;a href=&quot;https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2&quot;&gt;here&lt;/a&gt; for all available options.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/deeheber/weather-site/blob/main/CONTRIBUTING.md&quot;&gt;Open source contributions are welcome&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks to the original creators of &lt;a href=&quot;http://isitsnowinginpdx.com/&quot;&gt;isitsnowinginpdx.com&lt;/a&gt; for the inspiration.&lt;/p&gt;
&lt;p&gt;* To be fair the last instance of snow in the area was &lt;a href=&quot;https://www.oregonlive.com/weather/2023/02/portland-records-snowiest-day-since-1943-landing-at-no-2-on-all-time-list.html&quot;&gt;more than usual&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;** &lt;strong&gt;Edit February 23, 2024&lt;/strong&gt;: I&apos;ve &lt;a href=&quot;https://github.com/deeheber/weather-site/issues/7&quot;&gt;added CloudFront&lt;/a&gt; to this. It&apos;s nice to have the SSL cert, but to me the work felt like overkill in terms of effort and the extra infra needed for SSL. Hopefully AWS can improve this experience.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Improved git workflows with graphite.dev</title><link>https://danielleheberling.xyz/blog/git-collaboration/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/git-collaboration/</guid><description>Improved git workflows with graphite.dev</description><pubDate>Sun, 02 Oct 2022 09:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/github-screen.jpg&quot; alt=&quot;Clouds Image&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@richygreat?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Richy Great&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/s/photos/github?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Current Landscape&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://git-scm.com/&quot;&gt;Git&lt;/a&gt; is a tool that many tech people use. It&apos;s great for the most part. There are some pain points that could improve the experience. My issues revolve around collaboration with others. Here&apos;s some examples.&lt;/p&gt;
&lt;p&gt;As someone requesting a code review, so I can merge my changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;waiting for someone to review my code&lt;/li&gt;
&lt;li&gt;merge conflicts due to a PR being open for very long&lt;/li&gt;
&lt;li&gt;sometimes the code in the PR is needed for next steps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As someone doing code reviews for my teammates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it can be unclear which PRs I need to review or re-review&lt;/li&gt;
&lt;li&gt;navigating very long PRs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Upon joining my most recent company, my onboarding buddy told me about how they use &lt;a href=&quot;https://graphite.dev/&quot;&gt;graphite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Graphite utilizes something they&apos;re calling a &quot;stack.&quot; What used to be one huge PR is now many branches that build off of each other with each branch being its own PR.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/graphite.png&quot; alt=&quot;Clouds Image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Sounds great as someone who has to review code, but as the code submitter I had some concerns about what happens when merging branches. Would I get lots of merge conflicts? So far while using graphite, the answer has been mostly no. Graphite has a cli command that pulls down changes from the upstream and automatically rebases all affected branches.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gt repo sync --restack
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A nice bonus is that this command also prompts to ask if you want to delete local branches that have been merged into the &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;
&lt;p&gt;Another benefit is that graphite has a nice web based UI where you can see which PRs need your input both as a PR submitter and reviewer.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Despite all of the custom cli commands, graphite uses git under the hood. If you want to do something with vanilla git, graphite will pass that command through to git directly. One con to using graphite is that it just appears to work with Github (for now?). Also, at the time of writing this, there&apos;s a waitlist to get on the app.&lt;/p&gt;
&lt;p&gt;There&apos;s many more cli commands and features that I did not cover. Much like git, people have their preferred workflows and there&apos;s many more commands/options than one would use in their daily workflow.&lt;/p&gt;
&lt;p&gt;Disclaimer that I was not paid nor asked to write this post. I personally found this tool helpful. Thought I&apos;d share about how it&apos;s helped me with the intent that it might also help you.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>AWS Application Composer, the App Building Future We Need</title><link>https://danielleheberling.xyz/blog/aws-application-composer/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/aws-application-composer/</guid><description>AWS Application Composer, the App Building Future We Need</description><pubDate>Wed, 21 Sep 2022 05:17:19 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/app-composer.png&quot; alt=&quot;AWS Application Composer&quot; /&gt;&lt;/p&gt;
&lt;p&gt;At re:Invent 2022, Werner Vogels announced the preview of &amp;lt;a href=&quot;https://aws.amazon.com/application-composer/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS Application Composer&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;I don&apos;t want to write an overview or &quot;getting started&quot; guide, because you can read &amp;lt;a href=&quot;https://dev.to/aws-builders/overview-of-aws-application-composer-3j34&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;fantastic ones from people in the community&amp;lt;/a&amp;gt;. The focus of this post will be an overview of the problems that I see this service solving and what I&apos;d want for the future.&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;A promise of serverless is being able focus on business logic instead of infrastructure. While I agree with this goal, I don&apos;t think it has been fully achieved yet in practice.&lt;/p&gt;
&lt;p&gt;Unless you&apos;re someone who has a background in operations or cloud, it can be difficult to get started. Once you get started, it can be challenging to move fast...let alone move fast with a team.&lt;/p&gt;
&lt;p&gt;A best practice is to utilize infrastructure as code (IaC) to manage and provision cloud resources, because this allows you to have a written/deployable representation of your resources that can be shared in version control with a team. This is wonderful, but if you don&apos;t know anything about the resources you&apos;re deploying it can be difficult to find a starting point when authoring these files.&lt;/p&gt;
&lt;p&gt;Not only is writing the IaC for individual resources challenging, so is figuring out how to connect multiple resources together for a real world application. Things like IAM policies, event triggers for resources such as Lambda, and being able to add environment variables to resources that reference other resources in your stack come to mind.&lt;/p&gt;
&lt;h2&gt;The Solution (so far)&lt;/h2&gt;
&lt;p&gt;Since the beginning of my cloud/serverless journey, there has always been a high demand for a visual way to drag/drop resources that generate deployable IaC. I&apos;ve seen individuals in the community and companies attempt to build a solution, but none have resulted in widespread adoption.&lt;/p&gt;
&lt;p&gt;This problem space is what drew me to work as a software engineer at Stackery. AWS&apos;s acquisition of Stackery is what began the journey to what would result in AWS Application Composer. I left the company shortly before the acquisition; however, I still believe strongly in the mission.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/butterfly.jpg&quot; alt=&quot;Butterfly&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Credit: &amp;lt;a href=&quot;https://twitter.com/annaspies&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Anna Spysz&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Features I&apos;d like to see post GA&lt;/h2&gt;
&lt;p&gt;Much like the aspiration of being able to focus on business logic instead of infrastructure, I&apos;d love to see a future where we don&apos;t have to write &lt;strong&gt;any&lt;/strong&gt; IaC. This way, we can focus on architecture instead of resource configuration. It also has the potential to enable non-engineering participation in application building. While we&apos;re far away from that goal, I think we&apos;re getting closer with AWS Application Composer.&lt;/p&gt;
&lt;p&gt;A popular criticism of AWS Application Composer has been that it outputs &amp;lt;a href=&quot;https://aws.amazon.com/serverless/sam/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS SAM&amp;lt;/a&amp;gt; and not &lt;code&gt;${preferred IaC}&lt;/code&gt;. While those are valid requests (Terraform support would be &lt;strong&gt;awesome&lt;/strong&gt; for my day job), I think the folks focusing on this (many of whom I respect) are missing the bigger picture.&lt;/p&gt;
&lt;p&gt;With that, here&apos;s a bulleted list of other things I&apos;d like to see in the future:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A way to use this outside of the AWS console - Some organizations restrict access to the console for regulatory reasons. Something like an IDE extension might help.&lt;/li&gt;
&lt;li&gt;Amazon VPC resource - RDS requires a VPC.&lt;/li&gt;
&lt;li&gt;AWS AppSync resource - GraphQL is amazing for mobile applications (and other use cases).&lt;/li&gt;
&lt;li&gt;AWS Step Function resource integration into Workflow Studio - Allows for easier usage of a service that is often recommended by AWS employees to use in serverless applications.&lt;/li&gt;
&lt;li&gt;One button push deployment from the console - It&apos;s great as is with
&amp;lt;a href=&quot;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/accelerate.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt; AWS SAM accelerate&amp;lt;/a&amp;gt;...but I&apos;d love to see it be easier.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have any suggestions, I recommend using the &quot;submit feedback&quot; link in the console with details as to why you want x feature. The team wants to hear your feedback and is actively using it to improve the product.&lt;/p&gt;
&lt;h2&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;I think AWS&apos;s acquisition of Stackery makes a lot of sense and hopefully being directly embedded as a service within AWS will make it even better.&lt;/p&gt;
&lt;p&gt;This team deeply cares about developer experience and reducing the friction that exists within AWS serverless. They are some of the most brilliant and humble folks I&apos;ve ever had the pleasure to call coworkers. The team members whom I&apos;ve interacted with who aren&apos;t former coworkers have been nothing but kind and helpful.&lt;/p&gt;
&lt;p&gt;I hope that the dysfunction that sometimes exists with individual egos in big name corporations does not mess up the larger vision that started at Stackery. For now, I have faith that AWS can give the team the resources that they need to scale out this vision further. I very much look forward to what the future holds.&lt;/p&gt;
&lt;p&gt;If you haven&apos;t yet, I encourage you to give it a try and provide feedback. It&apos;s still in preview and far from finished, but I think you&apos;ll be pleasantly surprised.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Making the Switch - Software Development on Pop!_OS</title><link>https://danielleheberling.xyz/blog/desktop-linux-development/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/desktop-linux-development/</guid><description>Making the Switch - Software Development on Pop!_OS</description><pubDate>Thu, 04 Aug 2022 10:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/pop_os.png&quot; alt=&quot;Pop_OS Logo&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;My Background&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Disclaimer:&lt;/em&gt; this post details my experiences using Pop!_OS for software development. Depending on your needs, it&apos;s possible your experience might differ. For this reason, here&apos;s the tools that I generally use for software development:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Node.js/JavaScript/TypeScript&lt;/li&gt;
&lt;li&gt;AWS Serverless services&lt;/li&gt;
&lt;li&gt;AWS SAM&lt;/li&gt;
&lt;li&gt;AWS CDK&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why Switch Away from macOS?&lt;/h2&gt;
&lt;p&gt;Apple does a wonderful job of controlling the entire ecosystem for its devices. This is can be good since things &quot;just work.&quot; But, if you want to go beyond that and want more flexibility it might not be the best choice.&lt;/p&gt;
&lt;p&gt;I was finding myself wanting to be able to upgrade my computer&apos;s RAM quite a few times. Many Apple devices solder it to the logic board. You then have the option of 1. Buying a new logic board (almost the cost of a new computer) or 2. Buying a new computer. I didn&apos;t like these choices and decided to check out alternatives.&lt;/p&gt;
&lt;p&gt;I should also mention that I worked for Apple. I feel conflicted supporting a corporation that did not treat me well during my time as an employee. No judgements on those who do choose to use Apple products since everyone has their reasons.&lt;/p&gt;
&lt;h2&gt;What is Pop!_OS?&lt;/h2&gt;
&lt;p&gt;The last time I used a Windows machine was Windows XP. A lot has changed since then, and I was hesitant to go back since I never enjoyed Windows anyway.&lt;/p&gt;
&lt;p&gt;This is what gave me the idea to check out Linux. It&apos;s open source and there&apos;s many different distros one can choose. Plus, I was already familiar with the command line.&lt;/p&gt;
&lt;p&gt;I settled on &lt;a href=&quot;https://pop.system76.com/&quot;&gt;Pop!_OS&lt;/a&gt; because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it&apos;s based on Ubuntu, a very common distro that has a lot of documentation and an active community&lt;/li&gt;
&lt;li&gt;it had a very similar UI to macOS - what I&apos;m already familiar with&lt;/li&gt;
&lt;li&gt;I could use &lt;code&gt;apt-get&lt;/code&gt; to install or I could use the Pop!_Shop that comes with this distro (like the Mac App store)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;My Setup&lt;/h2&gt;
&lt;p&gt;Here&apos;s what I installed on my new Pop!_OS machine for software development:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nvm-sh/nvm#installing-and-updating&quot;&gt;nvm&lt;/a&gt; - a tool to install and switch to different versions of node&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt; - a code editor that I downloaded from the Pop shop&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&quot;&gt;AWS CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install-linux.html&quot;&gt;SAM CLI&lt;/a&gt; - the docs suggest using Homebrew, but the idea of using Homebrew on a Linux machine feels wrong. I downloaded the file and ran the install that way. Eric Johnson, an AWS employee provided &lt;a href=&quot;https://gist.github.com/singledigit/5f00ef69393b3b6f5dbfcf6cfada345e&quot;&gt;this gist&lt;/a&gt; for install scripts on Linux as an option.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/install/ubuntu/&quot;&gt;Docker&lt;/a&gt; - I followed the instructions to install Docker Engine on Ubuntu; but, it looks like there&apos;s a Docker Desktop for Linux in GA now. 🎉 Also of note, &lt;code&gt;docker compose&lt;/code&gt; performs way better on Linux machines (if you use that feature).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you use similar tools that I do, you&apos;ll notice that not much has changed to install things. You have to follow the Linux (sometimes Ubuntu) specific directions.&lt;/p&gt;
&lt;p&gt;If you would also like to get out of Apple&apos;s &quot;walled garden,&quot; I&apos;d recommend trying Pop!_OS. I&apos;ve been using it as my daily driver now for about a year and have not had many issues. I&apos;ll admit, I was initially very hesitant since I was familiar with macOS...but this has been a great upgrade in my life.&lt;/p&gt;
&lt;p&gt;Perhaps next year will be the year of Linux on the desktop. 😝&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Are You Paying Attention?</title><link>https://danielleheberling.xyz/blog/-paying-attention/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/-paying-attention/</guid><description>Are You Paying Attention?</description><pubDate>Sun, 13 Mar 2022 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;Every time I go out in public, I&apos;ve noticed a trend. People are on their phones almost everywhere I look. We hike to a beautiful waterfall, only to find a crowd of people standing there recording it on their phones for whatever social media site is popular. I go to a live show and the phones are out recording.&lt;/p&gt;
&lt;p&gt;I&apos;ve noticed this change within myself as well. I&apos;m sitting in my living room and constantly feel compelled to look at my phone. Sometimes when I can&apos;t focus at work, I just open up Twitter to the endless scroll. At the end of the day, I find behaviors like this are making me feel less happy in life and more stressed/angry, so I&apos;ve been actively trying to reduce this behavior.&lt;/p&gt;
&lt;h2&gt;Apps Are Designed to be Addictive&lt;/h2&gt;
&lt;p&gt;The endless scrolling and constant notifications are designed to make users open up the app and engage more. As a software engineer, I&apos;ve been in conversations that sounded something like &quot;well how can we design this to encourage users to [insert action that makes the company money here].&quot; I get that it&apos;s a business and there&apos;s a need to make money, but this makes me feel gross. People are constantly bombarded with apps and notifications trying to control their attention, and I would rather not be contributing to that. I much prefer the question to be phrased something like &quot;how can we encourage users to love using our app&quot; or &quot;how can we make it easier for people to use our app.&quot;&lt;/p&gt;
&lt;p&gt;Free social media sites in particular are probably the worst offenders. The model of how they make money is through advertisers, so they&apos;re incentivized to keep you on their site as long as they can in order to collect as much data about you to share with advertisers. They&apos;re also hoping your eyes read the ads to prompt you to purchase something you normally wouldn&apos;t. In this case you&apos;re truly &quot;paying&quot; with your attention.&lt;/p&gt;
&lt;p&gt;Outrage and arguing drives the most engagement, so many of these sites will prioritize blog in your feed that will outrage you into engaging. On top of that, everything just feels dehumanized because we&apos;re communicating in text and can&apos;t actually see the person we are communicating with. Excluding the bots, there are humans on the other side of these blog. It&apos;s easy to forget that.&lt;/p&gt;
&lt;p&gt;As far as I&apos;m concerned, I&apos;d love to see surveilance capitalism end because it is ruining many people&apos;s ability to pay attention to what&apos;s really important in life. The first step is realizing what is going on, so we can work together to create meaningful change in society.&lt;/p&gt;
&lt;h2&gt;Coping Mechanisms I&apos;ve been Using&lt;/h2&gt;
&lt;p&gt;In the meantime, I still need to live in this world the way it is. Here&apos;s some techniques I&apos;ve used to help get some of my ability to focus on things that matter back. Perhaps they&apos;ll also help you?&lt;/p&gt;
&lt;h3&gt;Take a break from computer screens&lt;/h3&gt;
&lt;p&gt;It&apos;s super easy to start scrolling and just lose track of time. I really like the pomodoro timer app &lt;a href=&quot;https://flowapp.info/&quot;&gt;flow&lt;/a&gt; to remind me it&apos;s time to take a break.&lt;/p&gt;
&lt;h3&gt;Go on walks&lt;/h3&gt;
&lt;p&gt;Getting outside is great, but it&apos;s also good to do this without a phone. Just let your mind wander as you walk around and it&apos;ll help you to unwind as well as come up with some &lt;a href=&quot;https://www.therighttoshower.com/ethical-living/what-are-shower-thoughts-and-why-we-have-them&quot;&gt;shower thought&lt;/a&gt; ideas.&lt;/p&gt;
&lt;h3&gt;Meditation&lt;/h3&gt;
&lt;p&gt;Taking some time out of the day to just focus on breathing has been helpful for me. It calms me down and helps to center my mind. There&apos;s lots of ways you can do this. I personally enjoy using &lt;a href=&quot;https://www.headspace.com/&quot;&gt;headspace&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Read books and physically write down notes&lt;/h3&gt;
&lt;p&gt;The medium in which you consume content influences how you consume it. For example social media blog are generally short blips of thoughts. So your mind spends time hopping around from thought to thought. I&apos;ve found reading entire books to be helpful. A book is one cohesive thought and they&apos;re generally free of distractions such as advertising.&lt;/p&gt;
&lt;p&gt;Whenever I need to take notes, I find physically writing things down helps me to retain the information. If I type it out on my computer, I find myself opening a second tab and navigating to various websites.&lt;/p&gt;
&lt;p&gt;For reading and note taking I use a &lt;a href=&quot;https://onyxboox.com/boox_nova3&quot;&gt;Boox Nova 3&lt;/a&gt; with very few apps installed on it, because I don&apos;t like having many pieces of paper and notebooks laying around.&lt;/p&gt;
&lt;h3&gt;Chase flow states&lt;/h3&gt;
&lt;p&gt;Have you ever started working on a task and just had so much fun with it that time stood still? That&apos;s a flow state. For me a good flow state is sitting down in front of the piano and practicing some songs. Whenever I get caught up in the scroll of social media, I sit and think to myself &quot;what could I be doing now that would be more rewarding&quot; and then I go do it. Chase those flow states for maximum happiness in life.&lt;/p&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;If you&apos;re like me and are having troubles focusing on things that matter in your life, know that you&apos;re not alone and it&apos;s not completely your fault. There are small things that you can do; however, we need a big societal change as well.&lt;/p&gt;
&lt;p&gt;This post was inspired from my life experience as well as from reading &lt;a href=&quot;https://stolenfocusbook.com/&quot;&gt;Stolen Focus&lt;/a&gt;. If you&apos;re interested in learning more, this book is a great place to start. All of the products I mentioned in this post are things that have helped me in my life, and I am not getting compensated to endorse.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Upgrading to CDK v2 for Typescript</title><link>https://danielleheberling.xyz/blog/upgrade-to-cdk-v2/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/upgrade-to-cdk-v2/</guid><description>Upgrading to CDK v2 for Typescript</description><pubDate>Tue, 18 Jan 2022 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;As of re:Invent 2021, CDK v2 is &amp;lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2021/12/aws-cloud-development-kit-cdk-generally-available/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;now generally available&amp;lt;/a&amp;gt;. 🎉&lt;/p&gt;
&lt;p&gt;This post includes a brief walkthrough on upgrading a project from v1 -&amp;gt; v2 as well as some personal opinions. I&apos;ll be using &amp;lt;a href=&quot;https://www.danielleheberling.xyz/blog/appsync-cdk/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;this notes app&amp;lt;/a&amp;gt; that we build previously to demo. If you want to skip ahead, the finished source code is &amp;lt;a href=&quot;https://github.com/deeheber/note-service-next-generation/tree/blog-post-2&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;on Github&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;Here&apos;s we go. Let&apos;s upgrade the application!&lt;/p&gt;
&lt;h2&gt;Step 1: Disable feature flags in cdk.json&lt;/h2&gt;
&lt;p&gt;Many of the feature flags from v1 are now included in v2 and/or are no longer relevant, so let&apos;s disable all flags.&lt;/p&gt;
&lt;p&gt;Before&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;app&quot;: &quot;npx ts-node --prefer-ts-exts bin/note-service.ts&quot;,
  &quot;context&quot;: {
    &quot;@aws-cdk/core:enableStackNameDuplicates&quot;: &quot;true&quot;,
    &quot;aws-cdk:enableDiffNoFail&quot;: &quot;true&quot;,
    &quot;@aws-cdk/core:stackRelativeExports&quot;: &quot;true&quot;,
    &quot;@aws-cdk/aws-ecr-assets:dockerIgnoreSupport&quot;: true,
    &quot;@aws-cdk/aws-secretsmanager:parseOwnedSecretName&quot;: true,
    &quot;@aws-cdk/aws-kms:defaultKeyPolicies&quot;: true,
    &quot;@aws-cdk/aws-s3:grantWriteWithoutAcl&quot;: true
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;app&quot;: &quot;npx ts-node --prefer-ts-exts bin/note-service.ts&quot;,
  &quot;context&quot;: {
    &quot;@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId&quot;: false,
    &quot;@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021&quot;: false,
    &quot;@aws-cdk/aws-rds:lowercaseDbIdentifier&quot;: false,
    &quot;@aws-cdk/core:stackRelativeExports&quot;: false
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 2: Update package.json dependencies&lt;/h2&gt;
&lt;p&gt;One of the features of CDK v2 is having mostly everything in a single library, &lt;code&gt;aws-cdk-lib&lt;/code&gt;. Hopefully this can help alleviate some of the pain that developers experienced with keeping all packages up to date and in sync.&lt;/p&gt;
&lt;p&gt;Let&apos;s focus on the &lt;code&gt;devDependencies&lt;/code&gt; section of the &lt;code&gt;package.json&lt;/code&gt;. In v2, we also need to add a &lt;code&gt;peerDependencies&lt;/code&gt; section.&lt;/p&gt;
&lt;p&gt;Before&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &quot;devDependencies&quot;: {
    &quot;@aws-cdk/assert&quot;: &quot;^1.139.0&quot;,
    &quot;@aws-cdk/aws-appsync&quot;: &quot;^1.139.0&quot;,
    &quot;@aws-cdk/aws-dynamodb&quot;: &quot;^1.139.0&quot;,
    &quot;@aws-cdk/aws-lambda&quot;: &quot;^1.139.0&quot;,
    &quot;@aws-cdk/aws-lambda-nodejs&quot;: &quot;^1.139.0&quot;,
    &quot;@aws-cdk/aws-logs&quot;: &quot;^1.139.0&quot;,
    &quot;@aws-cdk/core&quot;: &quot;^1.139.0&quot;,
    &quot;@types/jest&quot;: &quot;^27.4.0&quot;,
    &quot;@types/node&quot;: &quot;^17.0.8&quot;,
    &quot;aws-cdk&quot;: &quot;^1.139.0&quot;,
    &quot;esbuild&quot;: &quot;^0.14.11&quot;,
    &quot;jest&quot;: &quot;^27.4.7&quot;,
    &quot;ts-jest&quot;: &quot;^27.0.2&quot;,
    &quot;ts-node&quot;: &quot;^10.4.0&quot;,
    &quot;typescript&quot;: &quot;^4.5.4&quot;
  },
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &quot;peerDependencies&quot;: {
    &quot;aws-cdk-lib&quot;: &quot;^2.8.0&quot;,
    &quot;constructs&quot;: &quot;^10.0.36&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@aws-cdk/assert&quot;: &quot;^2.8.0&quot;,
    &quot;@aws-cdk/aws-appsync-alpha&quot;: &quot;^2.8.0-alpha.0&quot;,
    &quot;@types/jest&quot;: &quot;^27.4.0&quot;,
    &quot;@types/node&quot;: &quot;^17.0.8&quot;,
    &quot;aws-cdk-lib&quot;: &quot;^2.8.0&quot;,
    &quot;constructs&quot;: &quot;^10.0.36&quot;,
    &quot;esbuild&quot;: &quot;^0.14.11&quot;,
    &quot;jest&quot;: &quot;^27.4.7&quot;,
    &quot;ts-jest&quot;: &quot;^27.0.2&quot;,
    &quot;ts-node&quot;: &quot;^10.4.0&quot;,
    &quot;typescript&quot;: &quot;^4.5.4&quot;
  },
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One thing to note in this specific project is that the AppSync dependency is currently in alpha. Personally, I would recommend exercising extreme caution before using it in a business critical application.&lt;/p&gt;
&lt;h2&gt;Step 3: Update import statements in the code&lt;/h2&gt;
&lt;p&gt;Since we&apos;ve changed some dependencies in our &lt;code&gt;package.json&lt;/code&gt;, we should update our import statements to match.&lt;/p&gt;
&lt;p&gt;Here&apos;s an example of what that looks like&lt;/p&gt;
&lt;p&gt;Before&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import {
  CfnOutput,
  Construct,
  RemovalPolicy,
  Stack,
  StackProps,
} from &quot;@aws-cdk/core&quot;
import { FieldLogLevel, GraphqlApi, Schema } from &quot;@aws-cdk/aws-appsync&quot;
import { AttributeType, BillingMode, Table } from &quot;@aws-cdk/aws-dynamodb&quot;
import { Architecture, Runtime } from &quot;@aws-cdk/aws-lambda&quot;
import { NodejsFunction } from &quot;@aws-cdk/aws-lambda-nodejs&quot;
import { RetentionDays } from &quot;@aws-cdk/aws-logs&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Construct } from &quot;constructs&quot;
import { Stack, StackProps } from &quot;aws-cdk-lib&quot;
import { CfnOutput, RemovalPolicy } from &quot;aws-cdk-lib&quot;
import { AttributeType, BillingMode, Table } from &quot;aws-cdk-lib/aws-dynamodb&quot;
import { Architecture, Runtime } from &quot;aws-cdk-lib/aws-lambda&quot;
import { NodejsFunction } from &quot;aws-cdk-lib/aws-lambda-nodejs&quot;
import { RetentionDays } from &quot;aws-cdk-lib/aws-logs&quot;
import { FieldLogLevel, GraphqlApi, Schema } from &quot;@aws-cdk/aws-appsync-alpha&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 4: Bootstrap your environment for v2&lt;/h2&gt;
&lt;p&gt;If you&apos;ve previously been working with v1, you&apos;ll need to re-bootstrap your environment so you can deploy using CDK v2.&lt;/p&gt;
&lt;p&gt;To do this run the following in your console after you&apos;ve installed CDK v2 on your machine.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cdk bootstrap aws://&amp;lt;your-account-number&amp;gt;/&amp;lt;your-region&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;From my end, the upgrade process was seamless. Keep in mind that this was just a small personal project not using many constructs, so your experience might possibly differ. Overall, the team at AWS did a great job writing documentation on this topic.&lt;/p&gt;
&lt;p&gt;Despite the announcement of CDK v2 moving out of developer preview, I did notice that many constructs for v2 are still in alpha...so that seemed somewhat misleading. I&apos;m excited about CDK v2, but I plan to wait a bit before upgrading business critical applications to ensure that constructs will work reliably with the new version. I am pretty excited about all of the improvements under the hood and look forward to using it more extensively in the future.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>My Top Three AWS re:Invent 2021 Announcements</title><link>https://danielleheberling.xyz/blog/reinvent-2021/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/reinvent-2021/</guid><description>My Top Three AWS re:Invent 2021 Announcements</description><pubDate>Thu, 09 Dec 2021 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;Disclaimer that this post contains my individual opinions and yours might differ. Let&apos;s get started!&lt;/p&gt;
&lt;h2&gt;1. &lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2021/11/aws-lambda-partial-batch-response-sqs-event-source/&quot;&gt;SQS Partial Batch Retries in Lambda&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/cookies.jpg&quot; alt=&quot;Cookies&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@farber?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Jonathan Farber&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/s/photos/batch?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;What this means&lt;/h3&gt;
&lt;p&gt;This feature was announced during pre:Invent, but I think it still counts. Before, if SQS messages were sent as a batch into Lambda, you had the choice to return a response of a full failure or full success for the batch. Now we have an extra setting that allows queue retry behavior to be at the record level instead of the batch level. If one or more messages within that batch fail, you have the option to only send the failed messages back to the queue to attempt a retry.&lt;/p&gt;
&lt;h3&gt;Why it&apos;s exciting&lt;/h3&gt;
&lt;p&gt;This allows me to delete some code that I wrote to ensure each message is idempotent and also allows me to reduce the number of API calls made to check if a message was processed before.&lt;/p&gt;
&lt;h2&gt;2. &lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2021/12/aws-cloud-development-kit-cdk-generally-available/&quot;&gt;AWS Cloud Development Kit (CDK) v2 is Generally Available&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/tools.jpg&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@imattsmart?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;iMattSmart&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/s/photos/hammer?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;What this means&lt;/h3&gt;
&lt;p&gt;CDK v2 has been in developer preview for a while and it brings in some exciting features.&lt;/p&gt;
&lt;p&gt;Notable changes include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One dependency library for all AWS services&lt;/li&gt;
&lt;li&gt;Stable APIs are enforced via &lt;a href=&quot;https://semver.org/&quot;&gt;semver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Introduction of &lt;a href=&quot;https://aws.amazon.com/blogs/developer/increasing-development-speed-with-cdk-watch/&quot;&gt;&lt;code&gt;cdk watch&lt;/code&gt;&lt;/a&gt; which auto-deploys code for you as you edit locally and save the changes. (Note that AWS does not recommend the use of this feature in production environments.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Why it&apos;s exciting&lt;/h3&gt;
&lt;p&gt;AWS took customer feedback and addressed some of the common painpoints with the original release of CDK. To me, these changes show maturity in the tool and increases my confidence in using it for production workloads.&lt;/p&gt;
&lt;h2&gt;3. &lt;a href=&quot;https://aws.amazon.com/blogs/aws/sustainability-pillar-well-architected-framework/&quot;&gt;Sustainability Pillar for AWS Well-Architected Framework&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/windmill.jpg&quot; alt=&quot;Windmill&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@karsten_wuerth?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Karsten Würth (➡️ @karsten.wuerth)&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/s/photos/environment?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;What this means&lt;/h3&gt;
&lt;p&gt;AWS released a whitepaper that outlines best practices and recommendations around how to run workloads in the cloud with a focus on sustainability.&lt;/p&gt;
&lt;h3&gt;Why it&apos;s exciting&lt;/h3&gt;
&lt;p&gt;Just because things are running in someone else&apos;s data center, doesn&apos;t mean that it isn&apos;t using up natural resources. With the recent unprecedented weather events, it&apos;s important to get started thinking about this now. The fact that AWS made this an official pillar shows that they also care about this.&lt;/p&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;It was difficult to choose just three, because there were many great announcements during this conference. Overall, the announcements felt more incremental than innovative which I totally support. It&apos;s much better to improve on an existing service than to continually launch new services that aren&apos;t fully ready for production.&lt;/p&gt;
&lt;p&gt;I encourage you to check out all of the announcements &lt;a href=&quot;https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2021/&quot;&gt;here&lt;/a&gt;. What were some of your favorite announcements and why?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Now go build&quot; - Werner Vogels&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>On Home and Remote Work</title><link>https://danielleheberling.xyz/blog/remote-work-home/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/remote-work-home/</guid><description>On Home and Remote Work</description><pubDate>Tue, 23 Nov 2021 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/bridge-med.jpg&quot; alt=&quot;Crossroads&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@cutnshoot?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Jonathan Rivera&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/s/photos/western-pennsylvania?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I spent the first 22 years of my life in a part of the United States that many would call &quot;flyover country.&quot; To most people this is an insignificant small town that peaked in the 1950s. As my father likes to say &quot;This town was so much better when the mafia ran everything.&quot;&lt;/p&gt;
&lt;p&gt;To me this town is a lot more... to me it is&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where I was born and grew up&lt;/li&gt;
&lt;li&gt;Where my family is&lt;/li&gt;
&lt;li&gt;Where I first learned what community felt like&lt;/li&gt;
&lt;li&gt;Where I learned that I loved performing music&lt;/li&gt;
&lt;li&gt;Where I became the head drum major of my high school marching band&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;...in short, this town was home to me...&lt;/p&gt;
&lt;p&gt;After high school graduation, I attended a state school in the area to earn a Bachelor Degree in Music Education. Taking a look around me, I noticed that teachers in local schools stayed in their positions for a long time. Upon graduation from university, I hit a crossroads where I had to choose between:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Substitute teaching for 10+ years until a teacher left their position&lt;/li&gt;
&lt;li&gt;Moving somewhere else, getting some experience as a teacher, and moving back&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/assets/crossroads-med.jpg&quot; alt=&quot;Crossroads&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by &amp;lt;a href=&quot;https://unsplash.com/@ivalex?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Ivan Aleksic&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/s/photos/crossroads?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I chose option #2 in the end. Through many twists and turns, I am no longer a music teacher, nor have I moved back. Nowhere that I&apos;ve lived since the initial move has felt like home.&lt;/p&gt;
&lt;p&gt;Now we are in the midst of the Covid-19 global pandemic where many workers are forced to work remotely, some for the first time. Many companies have stated that they&apos;re planning on keeping employees fully remote for the forseeable future.&lt;/p&gt;
&lt;p&gt;I take a look at my young neice whose current bedroom is my childhood bedroom. The future of remote work gives me hope. Hope that my neice won&apos;t be forced into the same decision crossroad that I was. If she wants to move away, she still can...but with the increase of remote work it is less likely to be a decision that could impact how much money she can make or her career opportunities.&lt;/p&gt;
&lt;p&gt;I don&apos;t regret the people and life experiences that I&apos;ve had as a result of my choice. I do still wonder how much I&apos;ve missed and ponder the possibility of an eventual return.&lt;/p&gt;
&lt;p&gt;Afterall when we&apos;re gone, it doesn&apos;t matter what our rank was at our workplaces. It&apos;s the love we shared with the people around us. In the meantime, my neighbors will have to deal with my accent that slips out on ocassion.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Event Driven Background Processes</title><link>https://danielleheberling.xyz/blog/event-driven-background-processes/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/event-driven-background-processes/</guid><description>Event Driven Background Processes</description><pubDate>Sun, 12 Sep 2021 07:12:03 GMT</pubDate><content:encoded>&lt;p&gt;One theme when talking about serverless computing that frequently comes up is that &quot;serverless is event driven.&quot; Personally I&apos;ve found this to be true and am going to share one of my favorite event driven architectures built with AWS in this post.&lt;/p&gt;
&lt;h2&gt;Business Scenario&lt;/h2&gt;
&lt;p&gt;For this post, we&apos;re going to use a simplified example application.&lt;/p&gt;
&lt;p&gt;In our example application, whenever a user signs up to use our application, we want to perform the following three actions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Count the event in our &amp;lt;a href=&quot;https://heap.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;3rd party analytics service&amp;lt;/a&amp;gt;&lt;/li&gt;
&lt;li&gt;Put them on an email mailing list in our &amp;lt;a href=&quot;https://mailchimp.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Mailchimp account&amp;lt;/a&amp;gt;&lt;/li&gt;
&lt;li&gt;Send a notification to a &amp;lt;a href=&quot;https://slack.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Slack&amp;lt;/a&amp;gt; channel in our corporate slack&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;There&apos;s many ways someone could approach this issue, but my favorite way is to use a &quot;fan out&quot; or &quot;event fork&quot; approach. Here&apos;s how it works.&lt;/p&gt;
&lt;p&gt;In our example use case, when the person signs up to use the application, we send a message to &amp;lt;a href=&quot;https://aws.amazon.com/sns/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Amazon SNS&amp;lt;/a&amp;gt;. That SNS topic then receives that message and sends it to three &amp;lt;a href=&quot;https://aws.amazon.com/sqs/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;queues&amp;lt;/a&amp;gt;, each having a &amp;lt;a href=&quot;https://aws.amazon.com/lambda/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;function&amp;lt;/a&amp;gt; on the other side. Each function contains code that does the heavy lifting for us.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/event-driven.png&quot; alt=&quot;Infra Diagram&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Message Filtering&lt;/h2&gt;
&lt;p&gt;In a real life application, you&apos;ll most likely have many many events that you want to track and perform actions as a result.&lt;/p&gt;
&lt;p&gt;Let&apos;s revisit our example and add a new event we want to track - login. For login, we only want to count the event in our 3rd party analytics service and ignore adding them to a mailing list (they&apos;re already signed up) and ignore receiving a slack notification (that would be a lot of noise in slack).&lt;/p&gt;
&lt;p&gt;We can accomplish this by utilizing &amp;lt;a href=&quot;https://docs.aws.amazon.com/sns/latest/dg/sns-message-filtering.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;SNS message filtering&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;h2&gt;Benefits to this Approach&lt;/h2&gt;
&lt;p&gt;Like any architecture decision, there are always pros and cons. The important thing is to weigh the two together and use what works for you and your team. Here&apos;s some benefits I&apos;ve noticed and why I enjoy using this approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The events are decoupled from the initial action. For example, we wouldn&apos;t want someone to not be able to sign up just because there was an error counting the signup in our analytics.&lt;/li&gt;
&lt;li&gt;These background reactions to events don&apos;t need to be instant since we don&apos;t need to provide the user feedback...it&apos;s just for our backend office uses, so it is fine to process these events seperate from the general application flow.&lt;/li&gt;
&lt;li&gt;Let&apos;s say you have a CLI tool and a web application...both of those can send messages to this SNS topic.&lt;/li&gt;
&lt;li&gt;If you decide you no longer want to use Mailchimp as your email list provider, you can delete the corresponding queue/function combo and add a new queue/function to process your replacement mailing list service.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Additional Things to Keep in Mind&lt;/h2&gt;
&lt;p&gt;I didn&apos;t discuss this in detail here, but it&apos;s a good practice to have a &quot;dead letter queue&quot; (DLQ) on each queue setup, so you can retry failed events. You can read more about how to do this in this &amp;lt;a href=&quot;https://www.danielleheberling.xyz/blog/dlq-messages/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;blog post&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;In our example architecture, we focused on SNS -&amp;gt; SQS -&amp;gt; Lambda; however, there are many other ways that you can achieve a &quot;fan out&quot; architecture. For example: &amp;lt;a href=&quot;https://aws.amazon.com/eventbridge/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Amazon EventBridge&amp;lt;/a&amp;gt; or &amp;lt;a href=&quot;https://aws.amazon.com/step-functions/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS Step Functions&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;Check out this &amp;lt;a href=&quot;https://github.com/deeheber/event-fork&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;GitHub repo&amp;lt;/a&amp;gt; for a starter template using &amp;lt;a href=&quot;https://aws.amazon.com/serverless/sam/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS SAM&amp;lt;/a&amp;gt; if you&apos;d like to see how to implement this architecture.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Routing on the Edge</title><link>https://danielleheberling.xyz/blog/routing-on-the-edge/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/routing-on-the-edge/</guid><description>Routing on the Edge</description><pubDate>Thu, 09 Sep 2021 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/jake-ingle-s-t1oJXKYI4-unsplash.jpg&quot; alt=&quot;Feet dangling from roof&quot; /&gt;&lt;/p&gt;
&lt;p&gt;At &lt;a href=&quot;https://www.koan.co/?utm_campaign=edgerouter&amp;amp;utm_medium=blog&amp;amp;utm_source=medium&quot;&gt;Koan&lt;/a&gt;, our application’s frontend is a &lt;a href=&quot;https://reactjs.org/&quot;&gt;React&lt;/a&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/SPA&quot;&gt;Single Page Application&lt;/a&gt; running in two distinct environments (Staging and Production).&lt;/p&gt;
&lt;p&gt;In addition to viewing the Staging and Production versions of our frontend, we also need to serve up a version of the frontend based off of a git commit in our Staging environment. Doing this gives Koan developers a “live preview” URL to review what the frontend looks like after committing changes but before they’re merged.&lt;/p&gt;
&lt;h3&gt;Our Solution&lt;/h3&gt;
&lt;p&gt;Our solution has the following high level steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Code changes are merged into our &lt;code&gt;main&lt;/code&gt; branch. This action kicks off our &lt;a href=&quot;https://circleci.com/&quot;&gt;CI system&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The CI builds the code and places the build artifacts (static HTML/JavaScript/CSS files) into S3 buckets&lt;/li&gt;
&lt;li&gt;A CloudFront CDN is in front of one of those S3 buckets&lt;/li&gt;
&lt;li&gt;Our staging app domain is pointed at this CloudFront CDN&lt;/li&gt;
&lt;li&gt;On all origin requests to the staging app domain → a Lambda@Edge function serves a build-specific &lt;code&gt;index.html&lt;/code&gt; with static references to the rest of the build&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/assets/edge-router.png&quot; alt=&quot;Architecture diagram&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;More Details on Build Artifacts&lt;/h3&gt;
&lt;p&gt;Our CI process delivers build artifacts into S3 at &lt;code&gt;/commit/[commit sha]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When a developer wants to “live preview” their recent commit, they need to add &lt;code&gt;/commit/&amp;lt;their commit SHA&amp;gt;&lt;/code&gt; to the end of our staging app domain.&lt;/p&gt;
&lt;p&gt;Each &lt;code&gt;index.html&lt;/code&gt; file in this S3 bucket references static assets (CSS/JS files) hosted on a separate &lt;code&gt;frontend-builds subdomain&lt;/code&gt;. This domain pointed at a second CloudFront CDN with a second S3 bucket as its origin. Serving these as CDN-friendly, immutable assets saves significant compute (money) for resources that don&apos;t need Lambda@Edge.&lt;/p&gt;
&lt;h3&gt;Inside the Lambda &quot;router&quot; function&lt;/h3&gt;
&lt;p&gt;Whenever that developer requests a specific version of the app, the request hits CloudFront as an &lt;code&gt;origin-request&lt;/code&gt;. Our Lambda@Edge function receives a &lt;a href=&quot;https://docs.amazonaws.cn/en_us/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#example-origin-request&quot;&gt;message event&lt;/a&gt; from CloudFront and then proceeds to do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Gets the git commit hash from the pathname in the request. If there isn’t a commit hash in the URL, then we assume we want the latest version.&lt;/li&gt;
&lt;li&gt;Gets the requested index file&lt;/li&gt;
&lt;li&gt;Returns the index file as the body for our response&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Let&apos;s see some code&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Gets the git commit hash from the pathname in the request&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Whenever someone makes an HTTP request to the CDN, the CDN then sends an event object to our Lambda@Edge function. The shape looks something like &lt;a href=&quot;https://docs.amazonaws.cn/en_us/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#example-origin-request&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We then pull the &lt;code&gt;pathname&lt;/code&gt; off of that event object:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const url = require(&quot;url&quot;)

exports.handler = (event, ctx, cb) =&amp;gt; {
  const { request } = event.Records[0].cf
  const { uri } = request
  const urlString = url.parse(uri)
  const { pathname } = urlString

  // ...more code here
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have our &lt;code&gt;pathname&lt;/code&gt; (including the optional &lt;code&gt;commit/&amp;lt;commit sha&amp;gt;&lt;/code&gt; fragment), we can extract our git commit hash by calling a &lt;code&gt;getHash&lt;/code&gt; helper function.&lt;/p&gt;
&lt;p&gt;If there isn’t a hash present in the &lt;code&gt;pathname&lt;/code&gt; this means that we just want to serve up the latest version of the app, so we&apos;ll return &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const getHash = (pathname) =&amp;gt; {
  const components = pathname.split(&quot;/&quot;).filter(Boolean)
  if (components[0] !== &quot;commit&quot;) {
    return null
  }
  return components[1]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gets the requested index file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now that we have our git commit hash (or the &lt;code&gt;null&lt;/code&gt; default) from the &lt;code&gt;pathname&lt;/code&gt;, let&apos;s pass that commit hash into another helper function to get the desired index file from our S3 bucket.&lt;/p&gt;
&lt;p&gt;The variables that start with &lt;code&gt;process.env&lt;/code&gt; are NodeJS&apos;s way of referencing environment variables on the Lambda function. We set these variables when the function was provisioned.&lt;/p&gt;
&lt;p&gt;If the S3 object (index.html file) is missing, we handle that in the &lt;code&gt;catch&lt;/code&gt; and log the error.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const AWS = require(&quot;aws-sdk&quot;)

const s3 = new AWS.S3({
  apiVersion: &quot;2006-03-01&quot;,
  region: &quot;us-west-2&quot;,
})

const getIndexFile = (hash) =&amp;gt; {
  let key = `${process.env.ENV}.html`
  if (hash) {
    key = `commit/${hash}/index.html`
  }
  const params = {
    Bucket: process.env.BUCKET,
    Key: key,
  }
  return s3
    .getObject(params)
    .promise()
    .then((result) =&amp;gt; {
      if (!result || !result.Body || !Buffer.isBuffer(result.Body)) {
        console.error(&quot;null data&quot;)
        return null
      }
      return result.Body.toString(&quot;utf8&quot;)
    })
    .catch((err) =&amp;gt; {
      console.error(err)
      return null
    })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A possible next step to improve this might be using Lambda@Edge memory. Since the index file is immutable, we should only need to retrieve it from S3 once (or if Edge memory is dumped). https://aws.amazon.com/blogs/networking-and-content-delivery/leveraging-external-data-in-lambdaedge/&lt;/p&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Returns the index file as the body for our response&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;All together the function&apos;s code will look something like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const AWS = require(&quot;aws-sdk&quot;)
const url = require(&quot;url&quot;)

const s3 = new AWS.S3({
  apiVersion: &quot;2006-03-01&quot;,
  region: &quot;us-west-2&quot;,
})

const RESPONSE_HEADERS = {
  // Add desired headers here
}

const getIndexFile = (hash) =&amp;gt; {
  let key = `${process.env.ENV}.html`
  if (hash) {
    key = `commit/${hash}/index.html`
  }
  const params = {
    Bucket: process.env.BUCKET,
    Key: key,
  }
  return s3
    .getObject(params)
    .promise()
    .then((result) =&amp;gt; {
      if (!result || !result.Body || !Buffer.isBuffer(result.Body)) {
        console.error(&quot;null data&quot;)
        return null
      }
      return result.Body.toString(&quot;utf8&quot;)
    })
    .catch((err) =&amp;gt; {
      console.error(err)
      return null
    })
}

const getHash = (pathname) =&amp;gt; {
  const components = pathname.split(&quot;/&quot;).filter(Boolean)
  if (components[0] !== &quot;commit&quot;) {
    return null
  }
  return components[1]
}

// The main handler function code
exports.handler = (event, ctx, cb) =&amp;gt; {
  const { request } = event.Records[0].cf
  const { uri } = request
  const urlString = url.parse(uri)
  const { pathname } = urlString

  const hash = getHash(pathname)

  return getIndexFile(hash)
    .then((body) =&amp;gt; {
      if (!body) {
        console.error(`could not find file: ${uri}`)
        cb(null, {
          status: &quot;404&quot;,
          statusDescription: &quot;not found&quot;,
        })
        return
      }
      cb(null, {
        status: &quot;200&quot;,
        statusDescription: &quot;OK&quot;,
        headers: RESPONSE_HEADERS,
        body,
      })
    })
    .catch((err) =&amp;gt; {
      console.error(err, `error with request uri: ${uri}`)
      cb(err)
    })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Closing&lt;/h3&gt;
&lt;p&gt;While there are opportunities for improvement, this setup works well for our team, and we thought that sharing this approach might give you and your team some ideas to iterate on.&lt;/p&gt;
&lt;p&gt;More recently, AWS released &lt;a href=&quot;https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/&quot;&gt;CloudFront Functions&lt;/a&gt;. Stay tuned as we evaluate if that’s a good solution for us to use instead of our existing Lambda@Edge functions. It’s highly possible we could re-architect this to completely bypass the S3 GET and/or further utilize the edge caching.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://dev.to/danielkaczmarczyk&quot;&gt;Daniel Kaczmarczyk&lt;/a&gt; and &lt;a href=&quot;https://dev.to/rjz&quot;&gt;RJ Zaworski&lt;/a&gt; for reviewing drafts of this article.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This post was originally published on the &lt;a href=&quot;https://medium.com/developing-koan/routing-on-the-edge-913eb00da742&quot;&gt;Koan dev blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>The Secret to Getting More Done</title><link>https://danielleheberling.xyz/blog/take-a-walk/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/take-a-walk/</guid><description>The Secret to Getting More Done</description><pubDate>Wed, 14 Jul 2021 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/coffee-cookies.jpg&quot; alt=&quot;Coffee and Cookies&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It was a cold and rainy day as I sat alone in my home office debugging Webpack config errors. No matter what I tried, the errors would not go away. My natural inclination was to &quot;just get through most of them&quot; before eating lunch. But as I fixed errors, more emerged. Do you know what eventually got me through these hang ups?&lt;/p&gt;
&lt;h2&gt;Taking a break.&lt;/h2&gt;
&lt;p&gt;As my hunger grew stronger, I decided to give in and went to lunch. Being able to step away and temporarily detach my mind from the task at hand was just what I needed to approach this problem with a fresh approach full of new things to try.&lt;/p&gt;
&lt;p&gt;Time and time again, the act of taking a break has helped immensely both for getting meaningful work done and for my overall mental state.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/man-sandwich.jpg&quot; alt=&quot;Man eating a sandwich&quot; /&gt;
Photo by &amp;lt;a href=&quot;https://unsplash.com/@sanderdalhuisen?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Sander Dalhuisen&amp;lt;/a&amp;gt; on &amp;lt;a href=&quot;https://unsplash.com/s/photos/lunch?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;My top three activities while taking a break throughout the workday are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take a walk outside if weather isn&apos;t too bad.&lt;/li&gt;
&lt;li&gt;Change my surroundings. This could mean working at nearby coffee shop or moving from a desk to a couch. The context switching required to get up and move also helps to refocus.&lt;/li&gt;
&lt;li&gt;Read a book. Bonus points if it is a physical copy or on an e-reader. It&apos;s important to not stare at the same screen all day.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&quot;https://fortune.com/2021/07/06/kickstarter-four-day-work-week-2022/&quot;&gt;Some companies&lt;/a&gt; are planning to take this idea a step further and are piloting a four day work week. Personally, I&apos;m really interested to see how the rise of remote/hybrid workplaces as a result of the COVID-19 pandemic affects workers&apos; break frequency. Curious to see if it goes up or down. In the meantime, I&apos;m happy I get to work on a &lt;a href=&quot;https://www.koan.co/company/about&quot;&gt;team&lt;/a&gt; that facilitates working with purpose, built on a culture that supports transparency, autonomy and inclusivity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Four-day weeks and hybrid workplaces don&apos;t mean less work—just a more honest accounting of what already goes on.&lt;/strong&gt; The standard work week in the USA is currently 40 hours, but no one is actually productive that entire time. I set up a poll on Twitter to get some data points on this and here&apos;s the results.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/twitterPoll.png&quot; alt=&quot;twitterPoll&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Remembering to take breaks has helped me to get more done while working fewer hours, and I can support my teammates better by bringing my best self to my work. Maybe it can help you too. What are some of your favorite activities to do when taking a break during the work day?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This post was originally published on the &lt;a href=&quot;https://dev.to/koan/the-secret-to-getting-more-done-5hjh&quot;&gt;Koan dev blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Build a GraphQL API with TypeScript, AWS AppSync, and CDK</title><link>https://danielleheberling.xyz/blog/appsync-cdk/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/appsync-cdk/</guid><description>Build a GraphQL API with TypeScript, AWS AppSync, and CDK</description><pubDate>Sun, 13 Jun 2021 22:12:03 GMT</pubDate><content:encoded>&lt;h2&gt;What We&apos;re Building&lt;/h2&gt;
&lt;p&gt;Previously, I had used &amp;lt;a href=&quot;https://aws.amazon.com/serverless/sam/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS SAM&amp;lt;/a&amp;gt; to build a &amp;lt;a href=&quot;https://github.com/deeheber/note-service&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;CRUD app for notes&amp;lt;/a&amp;gt;. Recently, I wanted to learn more about the &amp;lt;a href=&quot;https://aws.amazon.com/cdk/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS Cloud Development Kit (CDK)&amp;lt;/a&amp;gt;, &amp;lt;a href=&quot;https://graphql.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;GraphQL&amp;lt;/a&amp;gt;, and &amp;lt;a href=&quot;https://aws.amazon.com/appsync/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS AppSync&amp;lt;/a&amp;gt;, so I decided to refactor my original project.&lt;/p&gt;
&lt;p&gt;Disclaimer that at the time of writing the &amp;lt;a href=&quot;https://aws.amazon.com/blogs/developer/announcing-aws-cloud-development-kit-v2-developer-preview/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;CDK v2 is in developer preview&amp;lt;/a&amp;gt;. This blog post will use CDK v1.&lt;/p&gt;
&lt;p&gt;If you want to skip ahead, take a look the finished project &amp;lt;a href=&quot;https://github.com/deeheber/note-service-next-generation/tree/blog-post&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;If this is your first time using the CDK, bootstrap your AWS account. The &lt;code&gt;Prerequisites&lt;/code&gt; section in &amp;lt;a href=&quot;https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;this guide&amp;lt;/a&amp;gt; has the steps.&lt;/p&gt;
&lt;p&gt;Start by installing the CDK npm package and running the &amp;lt;a href=&quot;https://docs.aws.amazon.com/cdk/latest/guide/cli.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;cdk init&amp;lt;/a&amp;gt; command. This is helpful because it gives you a directory structure along with a very minimal stack to get started.&lt;/p&gt;
&lt;p&gt;On top of the initial scaffolding in &lt;code&gt;/lib/note-service-stack.ts&lt;/code&gt; let&apos;s add some extra imports.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import {
  CfnOutput,
  Construct,
  RemovalPolicy,
  Stack,
  StackProps,
} from &quot;@aws-cdk/core&quot;
import { FieldLogLevel, GraphqlApi, Schema } from &quot;@aws-cdk/aws-appsync&quot;
import { AttributeType, BillingMode, Table } from &quot;@aws-cdk/aws-dynamodb&quot;
import { Code, Function, Runtime } from &quot;@aws-cdk/aws-lambda&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The notes will be persisted in DynamoDB, so let&apos;s also add the following CDK code to create a notes-table.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const notesTable = new Table(this, &quot;NotesTable&quot;, {
  billingMode: BillingMode.PAY_PER_REQUEST,
  partitionKey: { name: &quot;id&quot;, type: AttributeType.STRING },
  removalPolicy: RemovalPolicy.DESTROY,
  tableName: &quot;notes-table&quot;,
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Create the API&lt;/h2&gt;
&lt;p&gt;The next step is to set up our api like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const api = new GraphqlApi(this, &quot;NotesApi&quot;, {
  name: &quot;notes-api&quot;,
  logConfig: {
    fieldLogLevel: FieldLogLevel.ERROR,
  },
  schema: Schema.fromAsset(&quot;src/graphql/schema.graphql&quot;),
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The schema property points to a file in the repository. You can also provide this inline if your prefer. The schema needs to be well formed in order to have a successful deploy of the stack.&lt;/p&gt;
&lt;h2&gt;Between the API and DataStore&lt;/h2&gt;
&lt;p&gt;The basic flow for GraphQL is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;schema =&amp;gt; resolver =&amp;gt; data source
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The schema is your API contract. It says here&apos;s my query and mutation definitions in this API and he&apos;s what I expect to receive back from this specific query or mutation. Each field in that schema definition can be connected to a resolver that connects to the datasource.&lt;/p&gt;
&lt;p&gt;For example our Query to get a note by id looks like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  getNote(id: ID!): Note
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It takes an &lt;code&gt;id&lt;/code&gt; parameter and expectes a &lt;code&gt;Note&lt;/code&gt; to be returned. A &lt;code&gt;Note&lt;/code&gt; is defined in the schema to look like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Note {
  id: ID!
  content: String!
  author: String!
  createdAt: String!
  updatedAt: String
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Traditionally on AWS one would use &amp;lt;a href=&quot;https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-programming-guide.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Velocity Template Language (VTL)&amp;lt;/a&amp;gt; to resolve GraphQL schema fields to a datasource; however, last year AWS released the concept of a &amp;lt;a href=&quot;https://aws.amazon.com/blogs/mobile/appsync-direct-lambda/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;direct Lambda resolver&amp;lt;/a&amp;gt;. There&apos;s definitely reasons to continue using VTL over direct Lambda resolvers that I won&apos;t get into here, but I personally was pretty excited about this development since it meant that I could directly resolve to a Lambda used as a data source without needing to write any VTL.&lt;/p&gt;
&lt;h2&gt;Create Resolver and Data Source&lt;/h2&gt;
&lt;p&gt;For demonstration purposes let&apos;s focus on the &lt;code&gt;getNote&lt;/code&gt; Query. This is a query that a client will make to this API that will send a note id and receive the note back.&lt;/p&gt;
&lt;p&gt;Keep in mind that you&apos;ll also need to do similar things for all other queries/mutations in your API (in this case it&apos;s &lt;code&gt;listNotes&lt;/code&gt; query, &lt;code&gt;createNote&lt;/code&gt; mutation, &lt;code&gt;deleteNote&lt;/code&gt; mutation, &lt;code&gt;updateNote&lt;/code&gt; mutation).&lt;/p&gt;
&lt;p&gt;Let&apos;s start by adding some CDK code to create the Lambda function&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const getLambda = new Function(this, &quot;GetLambda&quot;, {
  functionName: &quot;get-lambda&quot;,
  runtime: Runtime.NODEJS_14_X,
  handler: &quot;get.handler&quot;,
  code: Code.fromAsset(&quot;src/get&quot;),
  memorySize: 3008,
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;code&lt;/code&gt; property points to the source code for this function in &lt;code&gt;src/get&lt;/code&gt;. This will contain the code necessary to retrieve our note from DynamoDB.&lt;/p&gt;
&lt;p&gt;We&apos;ll then want to add this Lambda as a data source to the api we previously created.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const getDs = api.addLambdaDataSource(&quot;getDatasource&quot;, getLambda)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next let&apos;s create a resolver to resolve the &lt;code&gt;getNote&lt;/code&gt; Query field in our schema to our Lambda data source.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;getDs.createResolver({
  typeName: &quot;Query&quot;,
  fieldName: &quot;getNote&quot;,
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final step is to ensure our Lamba has proper permissions to perform operations on DynamoDB, so let&apos;s send it the table name as an environment variable and grant it read permissions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;getLambda.addEnvironment(&quot;TABLE_NAME&quot;, notesTable.tableName)
notesTable.grantReadData(getLambda)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Finished Project&lt;/h2&gt;
&lt;p&gt;I personally have really enjoyed using GraphQL and the AWS CDK in my projects and hope you do too. Check out the full example &amp;lt;a href=&quot;https://github.com/deeheber/note-service-next-generation/tree/blog-post&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&amp;lt;/a&amp;gt;. Contributions are welcome.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>You Are More Than Your Tech Choices</title><link>https://danielleheberling.xyz/blog/you-are-more/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/you-are-more/</guid><description>You Are More Than Your Tech Choices</description><pubDate>Sat, 22 May 2021 21:12:03 GMT</pubDate><content:encoded>&lt;p&gt;If you know me, you&apos;ve probably noticed that my technical specialities lately have been JavaScript and AWS Serverless.&lt;/p&gt;
&lt;p&gt;As a software developer with a few years of experience, recruiters often message me trying to convince me to apply for a job opening. In one specific instance, I was actively job seeking and came across a very interesting recruiter message encouraging me to apply for a Python developer position.&lt;/p&gt;
&lt;p&gt;...sure I&apos;ve read Python before and know a tiny bit, but my imposter syndrome immediately kicked in and I wondered if I&apos;d be good enough. I then started reflecting on who I am as a developer and if I&apos;d miss being primarily a JavaScript developer.&lt;/p&gt;
&lt;h2&gt;Looking Around&lt;/h2&gt;
&lt;p&gt;In my experience both online and offline when you get a group of software developers together, people have opinions. Strong opinions that sometimes transform into a way that people self identify as a developer.&lt;/p&gt;
&lt;p&gt;Here&apos;s some examples of said opinions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Monolith vs Microservices&lt;/li&gt;
&lt;li&gt;Containers vs Serverless&lt;/li&gt;
&lt;li&gt;Language X vs Language Y&lt;/li&gt;
&lt;li&gt;SQL vs NoSQL&lt;/li&gt;
&lt;li&gt;Don&apos;t use language X because it&apos;s old and bad&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;...ok I over emphasized a bit on the last one, but you get my point.&lt;/p&gt;
&lt;p&gt;Some of these online and offline personalities even get extreme to the point of identifying as a &quot;JavaScript developer&quot;, &quot;the GraphQL guy&quot;, etc etc. and then only speaking on that topic.&lt;/p&gt;
&lt;h2&gt;Self Reflection&lt;/h2&gt;
&lt;p&gt;My initial hesitation of applying for that Python job got me thinking &quot;how much of my identity as a developer is tied to the technological choices and tools that I use?&quot;&lt;/p&gt;
&lt;p&gt;...turns out it was quite a lot.&lt;/p&gt;
&lt;p&gt;There&apos;s so many choices for tooling out there that it&apos;s easy to forget the &quot;who&quot;, &quot;what&quot;, and &quot;why&quot; by jumping immediately to the &quot;how&quot; when starting on a coding project.&lt;/p&gt;
&lt;p&gt;Non-software engineer customers don&apos;t care if you used the latest and greatest framework or programming language to build that app, they care about the app working when they need it.&lt;/p&gt;
&lt;p&gt;So this got me thinking more about what really matters to me as a developer.&lt;/p&gt;
&lt;h2&gt;What Really Matters (to me)&lt;/h2&gt;
&lt;p&gt;We&apos;re more than the tools that we choose to use as software developers. Technology is always changing, so those latest and greatest tools you&apos;re using today might not be the greatest in a few years. Instead of focusing on the tools, we should be focusing on why we&apos;re here.&lt;/p&gt;
&lt;p&gt;One thing that will always be around are 1) coworkers and 2) customers, so I&apos;ve shifted my professional focus to center around these two things.&lt;/p&gt;
&lt;p&gt;When selecting tools to build something, I&apos;ve changed my thinking towards trying to use the right tool for the right job. To me &quot;the right tool for the right job&quot; boils down to alignment with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;team values&lt;/li&gt;
&lt;li&gt;team goals&lt;/li&gt;
&lt;li&gt;the current team&apos;s strengths/weaknesses&lt;/li&gt;
&lt;li&gt;being able to properly balance building out new features quickly and being able to maintain code that is already there&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We all will naturally have specialities and interests and that&apos;s ok! But instead of identifying as a &quot;JavaScript developer&quot; I think from now on I choose to identify myself as &quot;someone who enjoys team collaboration and solving real world problems for customers.&quot;&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Upgrading to the AWS SDK for JavaScript v3</title><link>https://danielleheberling.xyz/blog/new-aws-js-sdk/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/new-aws-js-sdk/</guid><description>Upgrading to the AWS SDK for JavaScript v3</description><pubDate>Thu, 04 Feb 2021 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/three-dice.jpg&quot; alt=&quot;Three Dice&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In December 2020, AWS announced &amp;lt;a href=&quot;https://aws.amazon.com/blogs/developer/modular-aws-sdk-for-javascript-is-now-generally-available/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;general availability&amp;lt;/a&amp;gt; of version 3 of their SDK for JavaScript. I decided to take some time to experiment and see what&apos;s changed.&lt;/p&gt;
&lt;h2&gt;What&apos;s New?&lt;/h2&gt;
&lt;p&gt;There are lots of new features in this release. Here&apos;s the new features that I&apos;ve seen highlighted the most.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Modular Packages&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In prior versions, we&apos;d just &lt;code&gt;npm install&lt;/code&gt; the entire &lt;code&gt;aws-sdk&lt;/code&gt; package and everything was in there. Now the sdk is split among multiple npm packages. Just install the packages that you need to use for your application. This is a big win for being able to get those code bundle sizes down.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Middleware Stack&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Middleware Stack gives developers more control over the lifecycle of the requests sent via the aws-sdk. My internal mental reaction is similar to how I felt after hearing the &amp;lt;a href=&quot;https://aws.amazon.com/blogs/compute/introducing-aws-lambda-extensions-in-preview/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS Lambda Extensions announcement&amp;lt;/a&amp;gt;. It seems interesting, but I&apos;m not quite sure what I&apos;d do with it. That doesn&apos;t mean this isn&apos;t an exciting feature to someone else.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;First Class TypeScript Support&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;TypeScript is growing in popularity, so it seems fitting that AWS would continue the trend. As someone who recently started using TypeScript, this is pretty exciting.&lt;/p&gt;
&lt;h2&gt;Let&apos;s See Some Code!&lt;/h2&gt;
&lt;p&gt;Because I was curious about how everything might look in v3, I experimented by converting a minimally featured CRUD api to use the new version. Check out the &amp;lt;a href=&quot;https://github.com/deeheber/note-service/blob/master/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;README&amp;lt;/a&amp;gt; to see the high level architecutre of the app. And &amp;lt;a href=&quot;https://github.com/deeheber/note-service/pull/4/files&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&apos;s the git diff&amp;lt;/a&amp;gt; when converting from the sdk v2 to v3.&lt;/p&gt;
&lt;p&gt;Since DynamoDB is my database in this app, I went looking for the v3 equivalent of the &amp;lt;a href=&quot;https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;DynamoDB document client&amp;lt;/a&amp;gt; that strips out the DynamoDB types and makes things more human readible. I found some mixed messaging on the official AWS Github repo for the JavaScript SDK and my overall takeway is that originally an AWS employee said they&apos;d port it over, now it looks like that probably won&apos;t happen. My personal opinion is that they could&apos;ve done a better job communicating &amp;lt;a href=&quot;https://github.com/aws/aws-sdk-js-v3/issues/1223&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;in this issue&amp;lt;/a&amp;gt; filed on Github.&lt;/p&gt;
&lt;p&gt;Anyway long story short, I found I was able to get the same result by marshalling and unmarshalling JavaScript objects/DynamoDB Records in the code. Info on that can be found in the docs for the &lt;code&gt;util-dynamodb&lt;/code&gt; package &amp;lt;a href=&quot;https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/modules/_aws_sdk_util_dynamodb.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&amp;lt;/a&amp;gt;. Another example can also be found in my git diff mentioned above.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Post update March 7, 2021
Looks like there&apos;s now a &amp;lt;a href=&quot;https://github.com/aws/aws-sdk-js-v3/pull/2097&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;DynamoDB Document Client&amp;lt;/a&amp;gt; 😀.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;What&apos;s Next?&lt;/h2&gt;
&lt;p&gt;Out of caution, I plan to wait for things to settle a bit more before doing upgrades on major business critical applications.&lt;/p&gt;
&lt;p&gt;This has been a fun experiment to see what&apos;s changed, what stayed the same, and what I can look forward to in the future. Overall, I felt like AWS did a great job with this rewrite.&lt;/p&gt;
&lt;p&gt;What are your thoughts on the update? Have any fun use case ideas with the new middleware stack feature? Let me know!&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>5 Things I Learned from Advent of Code</title><link>https://danielleheberling.xyz/blog/5-things-i-learned-from-advent-of-code/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/5-things-i-learned-from-advent-of-code/</guid><description>5 Things I Learned from Advent of Code</description><pubDate>Wed, 30 Dec 2020 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/advent-of-code-2020.jpeg&quot; alt=&quot;Advent of Code&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Every year since 2015, &amp;lt;a href=&quot;https://adventofcode.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Advent of Code&amp;lt;/a&amp;gt; happens. Each day from December 1st through December 25th, there&apos;s a new 2 part puzzle to solve.&lt;/p&gt;
&lt;p&gt;On a typical year, I get to about day 4 then life gets in the way. When I finally remember there&apos;s more puzzles to do, it&apos;s almost time for the next year of puzzles. This year was pretty different in many ways, and I found myself with lots of free time. I acknowledge that not everyone might find this type of thing fun and that&apos;s ok, but this type of activity was perfect for me in my quarantine boredom. Here&apos;s my top 5 takeways from doing the 2020 edition of Advent of Code.&lt;/p&gt;
&lt;h4&gt;1. Think through how you would solve the puzzle in real life before writing code.&lt;/h4&gt;
&lt;p&gt;I found that sitting down with a piece of paper manually going through the puzzle sample input and seeing what had to be done to match the desired soltuion to be incredibly helpful. It&apos;s very tempting to jump straight to code, but I discovered when taking that route I&apos;d often misunderstand a part of the instructions and go down the wrong path. Really thinking through the solution by writing down the steps in plain English and then in code proved to be the most successful method for me.&lt;/p&gt;
&lt;h4&gt;2. Puzzle input can be different between the sample and the full input.&lt;/h4&gt;
&lt;p&gt;Each day the puzzle provides you with sample input (sometimes a few sample inputs) with the answer to help you to get to the solution. There were a few days that left me scratching my head as to why the smaller sample input worked while the full input didn&apos;t. For the most part the sample input was pretty close to the full input, but these tiny differences could make or break my final solution. If you have outstanding questions in regards to the requirements, take a quick peek at the full input to see how it compares to the sample input and be sure you&apos;re taking everything into account. One day I spent hours assuming the input was a square when in reality it was a rectangle.&lt;/p&gt;
&lt;h4&gt;3. Ask for help.&lt;/h4&gt;
&lt;p&gt;The overall theme seems to be that the puzzles get harder as the days progress. The great part about these puzzles in my opinion is that they&apos;re focused on solving a problem in a programming language agnostic way. There&apos;s also a large community of folks who are putting their solutions up on Github, doing live streams on Twitch, or posting videos on YouTube. If you get stuck, there&apos;s no shame in taking a look at those to get ideas on how to continue or talking to someone else you know who&apos;s also doing Advent of Code regardless of what programming language they&apos;re using.&lt;/p&gt;
&lt;h4&gt;4. There&apos;s often multiple ways to solve the same problem.&lt;/h4&gt;
&lt;p&gt;I often found myself solving a puzzle and then looking at Github to see how others solved it. I learned a lot doing this too, since sometimes others solved the puzzle in a way that I would have never imagined.&lt;/p&gt;
&lt;h4&gt;5. Sometimes you need to know a random math theorem to solve the problem.&lt;/h4&gt;
&lt;p&gt;Day 13 part 2 was giving me some serious trouble. After hours of trying to get it to run, I finally gave up and took a look at someone else&apos;s solution. Turned out I needed to know some random math theorem in order to get this working. Not all of us are mathmeticians, so if you run into this don&apos;t get down on yourself. It&apos;s a great opportunity to learn something new.&lt;/p&gt;
&lt;h4&gt;Closing&lt;/h4&gt;
&lt;p&gt;In the year 2020, I set out to learn more about traditional computer science data structures and algorithims. Advent of code was a great way to take what I learned throughout the year while having some fun solving silly puzzles on the internet. It can be a tad stressful since there&apos;s a new puzzle each day, but don&apos;t let that get you down. It&apos;s ok to take a break and go back to those puzzles later. As of the time writing this, I have nearly completed all challenges and have already learned a lot. Check out my progress &amp;lt;a href=&quot;https://github.com/deeheber/advent-of-code/tree/master/2020&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here on Github&amp;lt;/a&amp;gt;. If you enjoy programming puzzles and want to learn something new while having fun, I encourage you to check out Advent of Code.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Using GitHub Actions to Build a Self Updating README</title><link>https://danielleheberling.xyz/blog/github-actions/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/github-actions/</guid><description>Using GitHub Actions to Build a Self Updating README</description><pubDate>Sun, 25 Oct 2020 22:12:03 GMT</pubDate><content:encoded>&lt;h2&gt;The Project Idea&lt;/h2&gt;
&lt;p&gt;A few months ago, Github enabled the ability to add a README section to your profile in addition to the pinned repositories.&lt;/p&gt;
&lt;p&gt;Personally, I find this pretty exciting. I&apos;m sure many other software engineers can relate that we have some throw away projects and things that we built years ago on our profiles that don&apos;t fully reflect the big picture of our abilities or what we&apos;re currently into building. This gives us an extra place where we can highlight whatever we find important that we want to convey to people looking at our Github profile.&lt;/p&gt;
&lt;p&gt;I decided to embark on the the journey of getting mine set up and wanted the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Work information (past and present)&lt;/li&gt;
&lt;li&gt;Interests within the Software Engineering realm i.e. what languages, frameworks, etc I enjoy&lt;/li&gt;
&lt;li&gt;What I&apos;m currently excited about learning&lt;/li&gt;
&lt;li&gt;Links to my website and twitter&lt;/li&gt;
&lt;li&gt;Showcase my three most recent blog blog&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started Building&lt;/h2&gt;
&lt;p&gt;Github makes this pretty easy to get set up...with that said, you can make this as hard or as easy as you choose. It is definately easy to add complication to make things challenging.&lt;/p&gt;
&lt;p&gt;The base idea is that you create a repository named the same as your username, add a &lt;code&gt;README.md&lt;/code&gt;, and anything you put in that &lt;code&gt;README.md&lt;/code&gt; file will be showcased on the main page of your profile for others to see. I got that part done pretty fast and added some emojis to make it fun.&lt;/p&gt;
&lt;h2&gt;Going Beyond Markdown&lt;/h2&gt;
&lt;p&gt;I decided I didn&apos;t want to manually update my &lt;code&gt;README&lt;/code&gt; file everytime I published a new post. I decided to automate it in order to ensure my three most recently published blog were showcased. Becuase I wanted a relatively lightweight solution that would be quick to implement, I decided to try out Github actions.&lt;/p&gt;
&lt;p&gt;So the idea was to setup a job that runs once a day that does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clones my repository code&lt;/li&gt;
&lt;li&gt;Sets up Node (my preferred language for this task)&lt;/li&gt;
&lt;li&gt;Runs a script that
&lt;ul&gt;
&lt;li&gt;Pulls down my three most recent post titles and links from my blog&apos;s rss feed&lt;/li&gt;
&lt;li&gt;Replaces the links in the current README file with the new information&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If there are changes, push up the changes&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Setting up the Job&lt;/h2&gt;
&lt;p&gt;In my repo, I clicked actions and set up a new workflow. This consisted of adding a &lt;code&gt;.yml&lt;/code&gt; file to &lt;code&gt;.github/workflow&lt;/code&gt; in my &lt;code&gt;deeheber&lt;/code&gt; repo. Github had tons of examples for me to take a look through and the syntax was quicker to pick up than say something like AWS CloudFormation.&lt;/p&gt;
&lt;p&gt;In the end that file was pretty readible and looked like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: Build README

on:
  workflow_dispatch:
  schedule:
    - cron: &quot;30 15 * * *&quot;

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Check out repo
        uses: actions/checkout@v2
      - name: Use Node
        uses: actions/setup-node@v1
        with:
          node-version: &quot;12.x&quot;
      - name: Install node dependencies
        run: npm install
      - name: Check for RSS feed updates
        run: npm run scrape
      - name: Commit and push if changed
        run: |-
          git diff
          git config --global user.email &quot;actions@users.noreply.github.com&quot;
          git config --global user.name &quot;README-bot&quot;
          git add -A
          git commit -m &quot;Updated content&quot; || exit 0
          git push
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even if you aren&apos;t familiar with the syntax, I think you can for the most part tell what each step is doing. This gets positive marks in my book.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;workflow_dispatch&lt;/code&gt; event allowed me to go into the Github UI under actions and hit a button to manually run this job to test that things were working...this was super helpful in my development workflow.&lt;/p&gt;
&lt;p&gt;Also since this action was setup under the README Github repo, I didn&apos;t need to worry about providing Github credentials to the job. It just worked ™️.&lt;/p&gt;
&lt;h2&gt;Reflections&lt;/h2&gt;
&lt;p&gt;I am really impressed with how easy this was to get started and how quickly I was able to get something together and working. This also got me thinking that even though this is a super simple use case, that Github actions might be helpful for more complex cases and might reach for them in the future.&lt;/p&gt;
&lt;p&gt;Here&apos;s how mine turned out&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/github-readme.png&quot; alt=&quot;Github README&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/deeheber/deeheber&quot;&gt;Here&apos;s my code&lt;/a&gt; if you want to take a look and try to build something similar yourself.&lt;/p&gt;
&lt;p&gt;I&apos;d encourage you to check this out. Even if you aren&apos;t in the DevOps yaml writing land like I am...I think Github has done a fantastic job with documentation to make this approachable for newcomers.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Dangers of Console-Driven Development</title><link>https://danielleheberling.xyz/blog/dangers-of-console-driven-development/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/dangers-of-console-driven-development/</guid><description>Dangers of Console-Driven Development</description><pubDate>Thu, 15 Oct 2020 22:12:03 GMT</pubDate><content:encoded>&lt;h2&gt;What is Console Driven Development (CDD)?&lt;/h2&gt;
&lt;p&gt;AWS offers the ability to login to a web UI dashboard. In this dashboard, you can add, edit, and deploy various cloud resources. When I was first getting started with AWS, this is where I began for two reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It seemed the easiest way to get started&lt;/li&gt;
&lt;li&gt;Many tutorials out there included setting up cloud resources via the console&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Story Time - AKA Real Life CDD&lt;/h2&gt;
&lt;p&gt;My very first full-time job as a Software Engineer was on a small enough team that all of our infrastructure was setup using the AWS console in a single AWS account. When I arrived everything was already established for the most part, so I just needed to create an EC2 instance to use as my dev sandbox for my dev version of the application. Easy enough until it was not easy.&lt;/p&gt;
&lt;p&gt;I quickly learned that parts of the application went beyond my dev sandbox and used various AWS services that I had never heard of before nor knew how to set up. When a customer wrote in with a question or problem, it was often in these parts of the application. Not only that, I was also unclear how to test these parts of the application unless it was directly in our production environment.&lt;/p&gt;
&lt;p&gt;Since the team had a lot of siloed knowledge in this area and we wanted the ability for everyone to have visibility into this part of the application, we hired a Cloud Engineer who began the task of transforming all of our existing infrastructure into deployable AWS CloudFormation templates. Even though at the time I didn’t have full grasp on what the templates meant, I was able to take a look at them, compare them with the AWS documentation, and make a reasonable conclusion about what that part of the application was doing.&lt;/p&gt;
&lt;h2&gt;Infrastructure-as-Code For Everyone&lt;/h2&gt;
&lt;p&gt;While getting started in AWS or any cloud provider, it can be easy to just jump straight to clicking through the console to create your resources. Short term that could work; however, as the application grows and/or you add new team members it can be difficult to remember exactly how you clicked through the console and configured resources. This is where the idea of infrastructure-as-code (IaC) comes into help.&lt;/p&gt;
&lt;p&gt;IaC is helpful for many reasons, including but not limited to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Have a written record of your infrastructure settings and how to connect things.&lt;/em&gt; No need to recall how and where in the console you set something up. Bonus if you have this in git, since it makes it easier to rollback to previous versions should the unexpected happen.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Transportable infrastructure.&lt;/em&gt; Having the IaC template allows us to take what we have in one cloud provider account and replicate it in another cloud provider account. Having separate dev, staging, prod etc. accounts helps reduce the risk of taking something down in prod when you just want to test a code change in a sandbox environment.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Onboarding new engineers is easier.&lt;/em&gt; All they have to do to get their dev environment setup is to clone the repo and deploy it into their sandboxed cloud provider account.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How Stackery is helpful...&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Auto generates the IaC template by dragging and dropping resources on our Design Canvas. If you do know CloudFormation or want to go further than our default values, you have direct access to edit the template&lt;/li&gt;
&lt;li&gt;Visualize your AWS stack via our visualization tool&lt;/li&gt;
&lt;li&gt;Deploys your code via the UI dashboard or our CLI&lt;/li&gt;
&lt;li&gt;Cloudlocal debugging of Lambda functions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Getting started in serverless and IaC can feel very daunting at first. It can be tempting to just click through the AWS console to create your cloud resources like many of the “getting started” tutorials recommend; however, I encourage you to sign up for a free trial of Stackery and give us a try. Your teammates, customers, and future self will be happy you made that choice.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This post was originally published on https://www.stackery.io/&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Computer Science for Everyone - Queues</title><link>https://danielleheberling.xyz/blog/computer-science-for-everyone-queues/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/computer-science-for-everyone-queues/</guid><description>Computer Science for Everyone - Queues</description><pubDate>Tue, 08 Sep 2020 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/queue.jpg&quot; alt=&quot;Bubbles&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by Halacious on Unsplash&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Motivations&lt;/h2&gt;
&lt;p&gt;TL;DR. I come from a non-traditional background and never knew basic computer science concepts before getting into the industry. As I interviewed for software engineering roles, I found this to be a blocker at some companies. So, I&apos;ve decided to see what all the hype was about and do some self study to improve myself and want to share what I learned with the hopes that it may help others too.&lt;/p&gt;
&lt;p&gt;This is part 2 of a ??? part series. If you&apos;re interested check out what we covered last time &amp;lt;a href=&quot;https://www.danielleheberling.xyz/blog/cs-bubble-sort/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;h2&gt;Let&apos;s get to the second topic - Queues&lt;/h2&gt;
&lt;h2&gt;What is a queue?&lt;/h2&gt;
&lt;p&gt;Queues are everywhere in our lives. In Noth America, we more commonly refer to them as &quot;lines&quot;. You go to the coffee shop, a lot of time you wait in line. You go out to buy groceries, you wait in line to pay for your groceries. You&apos;re driving on the turnpike, you wait in line inside your car to pay the toll.&lt;/p&gt;
&lt;p&gt;The idea of the line or queue also exists in the digital world. You click to print something on your computer, that print job enters a queue where it waits in line to be printed. The common theme on this type of data structure is the priority order in which jobs get handled. The first item to enter the queue is the first item to leave the queue. Much like in real life how we frown upon line jumpers that cut in line, queues also do not like this. In more official terms this is known as &quot;FIFO&quot; or &quot;first in first out&quot;.&lt;/p&gt;
&lt;h2&gt;Queue operations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Enqueue&lt;/em&gt; - Add an item to the end queue&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Dequeue&lt;/em&gt; - Take the item at the front out of the queue&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Peek&lt;/em&gt; - Take a look at the first element in the queue without removing it&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What does this look like in code?&lt;/h2&gt;
&lt;p&gt;There&apos;s many ways to go about simulating a queue. I&apos;m going to use Javascript since tons of the CS resources I&apos;ve found tend to lean on Java for code snippets.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Create the queue
const queue = [];

// Add items to the queue (enqueue)
queue.push(&apos;a&apos;);
queue.push(&apos;b&apos;);
queue.push(&apos;c&apos;);

// Remove items from the queue (dequeue)
while (queue.length &amp;gt; 0) {
  const itemToProcess = queue.shift();

  ...process the item taken off of the queue here
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Going further&lt;/h2&gt;
&lt;p&gt;On the surface queues seem pretty simple especially if you&apos;re comfortable with arrays. Oftentimes queues are used as a building block for something larger and have the potential to get complicated pretty quickly, depending on what you&apos;re doing.&lt;/p&gt;
&lt;p&gt;Queues are often used for breadth first (also known as level order) tree traversal. If you don&apos;t know what that means, that&apos;s ok. I&apos;ll most likely cover that in a later post.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Lambda Layer Update</title><link>https://danielleheberling.xyz/blog/lamba-layer-update/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/lamba-layer-update/</guid><description>Lambda Layer Update</description><pubDate>Fri, 03 Jul 2020 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/layer-cake.jpg&quot; alt=&quot;Layer Cake&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;Earlier this year, I did a &amp;lt;a href=&quot;https://www.danielleheberling.xyz/blog/lambda-layer-example/&quot; target=&quot;_blank&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;walk through demo&amp;lt;/a&amp;gt; on how to use lambda layers with NodeJS. We build a helper function that takes in a string and returns the string backwards.&lt;/p&gt;
&lt;p&gt;&amp;lt;a href=&quot;https://aws.amazon.com/blogs/compute/working-with-aws-lambda-and-lambda-layers-in-aws-sam/&quot; target=&quot;_blank&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Now that AWS SAM and the SAM CLI has support for building layers&amp;lt;/a&amp;gt;, it&apos;s now easier to get this setup. The way in my prior post still works, but personally I recommend utilizing this method going forward.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;In this demo, I am going to use &amp;lt;a href=&quot;https://www.stackery.io/&quot; target=&quot;_blank&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Stackery&amp;lt;/a&amp;gt;. Be sure to have the following installed in order to guarantee this will work.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The lastest version of the SAM CLI&lt;/li&gt;
&lt;li&gt;The lastest version of the Stackery CLI&lt;/li&gt;
&lt;li&gt;Docker desktop (just needed for local invoke)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Add Lambda Layer Resource&lt;/h2&gt;
&lt;p&gt;We start by adding a layer resource to the canvas in Stackery and connecting it to the function(s) that we&apos;d like to use the layer. For readability, I renamed my Layer Resource Logicial Id to &lt;code&gt;PalindromeLayer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/palindrome-layer-function.png&quot; alt=&quot;Layer to function&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Make sure that your layer resource is referenced properly and that any old layers you&apos;re no longer using are removed. You can find this in the Function settings or in your template.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/layer-reference.png&quot; alt=&quot;Layer reference&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Move Layer Source Code&lt;/h2&gt;
&lt;p&gt;Since the AWS SAM CLI can now build our layer for us, let&apos;s move the source code. Using my example, I moved my &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;palindrome.js&lt;/code&gt; file into &lt;code&gt;src/PalindromeLayer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/file-structure.png&quot; alt=&quot;File structure&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I then made sure my &lt;code&gt;ContentUri&lt;/code&gt; correctly referenced this new location.
&lt;img src=&quot;/assets/content-uri.png&quot; alt=&quot;Content URI&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Deploy the Layer and Locally Invoke&lt;/h2&gt;
&lt;p&gt;Once everything is deployed, I can now iterate locally on my function even if it has a layer. Since the Docker container used to build/include the layer in my function execution now needs to do the building, we need to add &lt;code&gt;--build&lt;/code&gt; to the command.&lt;/p&gt;
&lt;p&gt;Here&apos;s the command that I ran to test my function locally, keep in mind it might be a tad different than yours.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;stackery local invoke -e development -f Function --build
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Additional&lt;/h2&gt;
&lt;p&gt;You can still deploy a Layer in another stack and reference it by ARN. Some organizations probably prefer this method, especially if that layer is being used by multiple functions in different stacks. To do this, set your &lt;code&gt;Build Method&lt;/code&gt; to &lt;code&gt;none&lt;/code&gt; in the layer settings and add the layer ARN in the function(s) settings that you&apos;d like to use the layer.&lt;/p&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;Personally I like this method better because it eliminate the manual process of zipping and uploading my Lambda Layer and then manually updating the layer version in my SAM template.&lt;/p&gt;
&lt;p&gt;Check out the full code example in &amp;lt;a href=&quot;https://github.com/deeheber/lambda-layer-example/tree/layer-resource&quot; target=&quot;_blank&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;this repo&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;All of the official documentation from AWS about Lambda layers can be found &amp;lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html&quot; target=&quot;_blank&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&amp;lt;/a&amp;gt;.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Building My Own Jamstack</title><link>https://danielleheberling.xyz/blog/building-my-own-jamstack/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/building-my-own-jamstack/</guid><description>Building My Own Jamstack</description><pubDate>Fri, 26 Jun 2020 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;The trend of the frontend and backend coming closer together is continuing and even has a name now...the &amp;lt;a href=&quot;https://jamstack.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Jam stack&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://danielleheberling.xyz/blog/deploy-frontend/&quot;&gt;Last year&lt;/a&gt;, I discussed my excitment about the backend and frontend slowly merging closer together and did a walk through on how to deploy a JavaScript based frontend to an S3 bucket with website hosting enabled.&lt;/p&gt;
&lt;p&gt;Building and deploying websites still continues to be a very common use case with folks using AWS, but I personally wanted a more &quot;push button&quot; solution that would also allow me to connect my Stackery backend resources easily. So I&apos;ve decided to iterate on what we built the last time to make it better.&lt;/p&gt;
&lt;h3&gt;What We Built Last Time&lt;/h3&gt;
&lt;p&gt;&amp;lt;a href=&quot;https://www.danielleheberling.xyz/blog/text-to-speech/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Previously&amp;lt;/a&amp;gt;, I wrote an application that translates written text to speech. I was getting tired of having to recall the different API endpoints to hit and wanted a nice UI to better manage these files...so I got to work on building a frontend.&lt;/p&gt;
&lt;p&gt;Then I got tired of manually building and uploading those files to S3 for website hosting, so I wrote a Lambda function to do this for me. Here&apos;s what the final architecture looked like&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/finishedStack.png&quot; alt=&quot;Finished stack&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That is all fine and still works, but keeping up with latest Serverless trends I recognize that writing code in a Lambda function can cause potential unwanted technical debt. What if there was some service that I could just input specific things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;source code location&lt;/li&gt;
&lt;li&gt;build command&lt;/li&gt;
&lt;li&gt;built files location&lt;/li&gt;
&lt;li&gt;location (an S3 Bucket) to upload the built files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lucky for us, there&apos;s AWS CodeBuild!&lt;/p&gt;
&lt;p&gt;So I embarked on the journey of replacing my &lt;code&gt;PopulateFrontend&lt;/code&gt; Lambda function with a CodeBuild Project.&lt;/p&gt;
&lt;h3&gt;The Easy Push Button Way of Doing this&lt;/h3&gt;
&lt;p&gt;Stackery released the &amp;lt;a href=&quot;https://docs.stackery.io/docs/api/nodes/Website/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Website Resource&amp;lt;/a&amp;gt;. Check it out...it&apos;s just a matter of dragging and dropping the resource into your stack and providing a few settings (Source Directory, Build Command, and Publish Directory).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/website-resource.png&quot; alt=&quot;Website Canvas&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We have a more detailed tutorial &amp;lt;a href=&quot;https://docs.stackery.io/docs/tutorials/react-spa-tutorial/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;h3&gt;Connecting my Frontend to a Backend API&lt;/h3&gt;
&lt;p&gt;Once your S3 Bucket and Website are on the canvas, add an API too (both HTTP API and original API Gateway work).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/websiteToApi.png&quot; alt=&quot;Website to API&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After you connect the API to the &lt;code&gt;References&lt;/code&gt; facet under the &lt;code&gt;Website&lt;/code&gt;, you&apos;ll then see that the &lt;code&gt;API_URL&lt;/code&gt; environment variable is available to the CodeBuild Project.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/api-url.png&quot; alt=&quot;API URL setting&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I personally decided to write a minimal shell script that writes the &lt;code&gt;API_URL&lt;/code&gt; environment varaible to a file that my frontend code later references. Here&apos;s a snippet.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/sh

# Inject API into config file
echo &quot;export default {
  backendAPI: &apos;$API_URL&apos;
};&quot; &amp;gt; src/config.js

echo &quot;Config file written to src/config.js&quot;

# Build the site
npm run build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I added an npm script to my &lt;code&gt;package.json&lt;/code&gt; that executes this script, titled it &quot;production&quot;, and updated my &lt;code&gt;Website&lt;/code&gt; &lt;code&gt;Build Command&lt;/code&gt; to be &lt;code&gt;npm run production&lt;/code&gt;. All this script does is run the snippet above.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/package-json.png&quot; alt=&quot;package json&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/build-command.png&quot; alt=&quot;build command setting&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Speeding up the Delivery with a CDN (Optional)&lt;/h3&gt;
&lt;p&gt;If you want to speed up the delevery of your website, I&apos;d suggest adding a CDN in front of your S3 bucket using Amazon CloudFront. Connect your S3 Bucket to the CDN Origin like this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/cdn.png&quot; alt=&quot;cdn&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;The CodeBuild Job&lt;/h3&gt;
&lt;p&gt;The rest of this post is going to be a technical deep dive under the hood for those who are interested.&lt;/p&gt;
&lt;p&gt;The CodeBuild job does the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Installs dependencies (in my case NodeJS and NPM)&lt;/li&gt;
&lt;li&gt;Copies my source code into the build job container&lt;/li&gt;
&lt;li&gt;Executes my build command&lt;/li&gt;
&lt;li&gt;Copies the built files into an S3 Bucket&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All of the necessary variables are set via the Stackery Website resource settings, but you can also directly alter the template if you prefer.&lt;/p&gt;
&lt;p&gt;Here&apos;s what the CodeBuild job template code looks like for my text to speech converter app:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Website:
  Type: AWS::CodeBuild::Project
  Metadata:
    StackeryType: website
  DependsOn: WebsiteRole
  Properties:
    Name: !Sub ${AWS::StackName}-Website
    Artifacts:
      Type: NO_ARTIFACTS
    Environment:
      ComputeType: BUILD_GENERAL1_SMALL
      Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
      Type: LINUX_CONTAINER
      EnvironmentVariables:
        - Name: API_URL
          Value: !Sub https://${HttpApi}.execute-api.${AWS::Region}.amazonaws.com
    ServiceRole: !GetAtt WebsiteRole.Arn
    Source:
      Type: NO_SOURCE
      BuildSpec: !Sub
        - |-
          version: 0.2
          phases:
            install:
              runtime-versions:
                nodejs: latest
                python: latest
                ruby: latest
              commands:
                - |
                  _SOURCE_LOCATION=${SourceLocation}
                  if [ s3 != &quot;${!_SOURCE_LOCATION%%:*}&quot; ]; then
                    git clone ${SourceLocation} repo
                    cd repo
                    git checkout ${SourceVersion}
                  else
                    aws s3 cp ${SourceLocation} repo.tgz
                    tar --strip-components 1 -xvvzf repo.tgz
                  fi
                - cd ${SourceDirectory}
            pre_build:
              commands:
                - |
                  if [ ! -f yarn.lock -a -f package.json ]; then
                    npm install --production
                  elif [ -f yarn.lock -a -f package.json ]; then
                    yarn install --production
                  elif [ -f requirements.txt ]; then
                    pip install -r requirements.txt
                  elif [ -f Gemfile ]; then
                    bundle install
                  fi
            build:
              commands:
                - ${BuildCommand}
            post_build:
              commands:
                - |
                  _SOURCE_LOCATION=${SourceLocation}
                  if [ s3 != &quot;${!_SOURCE_LOCATION%%:*}&quot; ]; then
                    cd &quot;${!CODEBUILD_SRC_DIR}/repo&quot;
                  else
                    cd ${!CODEBUILD_SRC_DIR}
                  fi
                - aws s3 sync &apos;${PublishDirectory}&apos; &apos;s3://${DestinationBucketName}&apos; --acl public-read --cache-control &apos;max-age=0, must-revalidate, public&apos; --no-progress --delete
        - PublishDirectory: src/frontend/build
          BuildCommand: npm run production
          SourceDirectory: src/frontend
          DestinationBucketName: !Ref FrontEnd
    Tags:
      - Key: Stackery Project Type
        Value: Website Builder
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Orchestrating the CodeBuild Job&lt;/h3&gt;
&lt;p&gt;Now that the CodeBuild job is setup, we know how we&apos;re going to build the site and publish it to an S3 Bucket. The remaining question is how can we trigger the CodeBuild job to start when the CloudFormation stack is deployed?&lt;/p&gt;
&lt;p&gt;My answer to that question was to use a &amp;lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Custom Resource&amp;lt;/a&amp;gt;. The template for it looks like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WebsiteBuildTrigger:
  Type: Custom::StackeryWebsiteBuildTrigger
  DependsOn: WebsiteEvents
  Properties:
    ServiceToken: !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stackery-agent-commander
    Type: website
    ProjectName: !Ref Website
    SourceVersion: !Ref SourceVersion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the &lt;code&gt;Type&lt;/code&gt; starts with &lt;code&gt;Custom::&lt;/code&gt;...this is how CloudFormation knows it&apos;s a custom resource.&lt;/p&gt;
&lt;p&gt;Any time one of the &lt;code&gt;Properties&lt;/code&gt; changes (in this case the &lt;code&gt;SourceVersion&lt;/code&gt;), CloudFormation will trigger Lambda function I specify under &lt;code&gt;ServiceToken&lt;/code&gt;. In this case, it&apos;s a Stackery controlled Lambda function in a different CloudFormation stack that has two main jobs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Trigger the start of the CodeBuild job&lt;/li&gt;
&lt;li&gt;Report back to CloudFormation when the CodeBuild job succeeds or fails, so CloudFormation knows how to continue deploying the stack&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Step 1 is handled directly in the Lambda function via &amp;lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/reference/codebuild/start-build.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;this command&amp;lt;/a&amp;gt;, but how can we enable the CodeBuild job to report back a success or failure to CloudFormation?&lt;/p&gt;
&lt;p&gt;My answer is &lt;code&gt;CloudWatch Events&lt;/code&gt;. This is wired up in the template under the resource named &lt;code&gt;WebsiteEvents&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WebsiteEvents:
  Type: AWS::Events::Rule
  DependsOn: Website
  Properties:
    EventPattern:
      source:
        - aws.codebuild
      detail-type:
        - CodeBuild Build State Change
      detail:
        build-status:
          - SUCCEEDED
          - FAILED
          - FAULT
          - STOPPPED
          - TIMED_OUT
        project-name:
          - !Ref Website
    Targets:
      - Arn: !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stackery-agent-commander
        Id: StackeryAgentCommander
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the CodeBuild Project for our Website has a build status of SUCCEEDED, FAILED, FAULT, STOPPED, or TIMED_OUT, the configured CloudWatch Event reports this information back to my custom resource function. My custom resource function then sends the appropriate information to CloudFormation. Once CloudFormation gets a pass/fail response, it knows how to conintue with deploying the stack.&lt;/p&gt;
&lt;h3&gt;Closing&lt;/h3&gt;
&lt;p&gt;Check out the entire code repository referenced &amp;lt;a href=&quot;https://github.com/deeheber/text-to-speech-converter/tree/blog-post-3&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;There are definitely many different approaches to build and deploy a JavaScript frontend on AWS. I encourage you to investigate everything that&apos;s out there and see what works best for you.&lt;/p&gt;
&lt;p&gt;I&apos;m biased, but I recommend checking out Stackery. Not only has the &amp;lt;a href=&quot;https://docs.stackery.io/docs/api/nodes/Website/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Website resource&amp;lt;/a&amp;gt; been added recently, we&apos;ve also updated our Developer plan to have more features still at the amazing price of free.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Computer Science for Everyone - Bubble Sort</title><link>https://danielleheberling.xyz/blog/computer-science-for-everyone-bubble-sort/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/computer-science-for-everyone-bubble-sort/</guid><description>Computer Science for Everyone - Bubble Sort</description><pubDate>Fri, 10 Apr 2020 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/bubbles.jpg&quot; alt=&quot;Bubbles&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by Pieter on Unsplash&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Motivations&lt;/h2&gt;
&lt;p&gt;As an individual that is a professional software engineer and does not have a computer science degree, data structures and alogorithms used to be frightening words for me to hear. Oftentimes my inner voice translated those words into &quot;here&apos;s some concepts that are often not used regularly, yet come up in software engineering interviews all the time. This gives us an excuse to not hire you solely based off of the fact that you do not have a computer science degree.&quot;&lt;/p&gt;
&lt;p&gt;After being in the industry for a while, I&apos;ve found my inner voice to be partially factual. The technical interviewing process is broken. As an individual who&apos;s had to interview others and had to write some coding challenges...I can totally understand why interviewers sometimes gravitate toward asking these types of questions beyond the aforementioned gatekeeping. It&apos;s nearly impossible to find a perfect set of coding questions to test one&apos;s knowledge...sometimes it&apos;s just easier to ask these types of questions to see how someone thinks through problems.&lt;/p&gt;
&lt;p&gt;While I don&apos;t have a solution to how we can fix the broken coding interview process, I decided to focus on what I can control. I felt a sense of imposter syndrome, because I could not answer those types of questions in interviews...it was downright embarassing. Once an interviewer asked me to write a linked list and at the time I didn&apos;t even know what a linked list was. For those reasons, I decided to learn a bit about these concepts and hope to share over a series of blog what I&apos;ve learned in hopes that it may help others.&lt;/p&gt;
&lt;h2&gt;Let&apos;s get onto the first topic&lt;/h2&gt;
&lt;h3&gt;Bubble Sort and how it works&lt;/h3&gt;
&lt;p&gt;We have a list of items and want to put them in order. For example purposes and to keep things simple, let&apos;s just use a list of numbers.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;8, 6, 7, 5, 3, 0 ,9&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s the high level steps on how to sort this via the bubble sort method:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start going through the list&lt;/li&gt;
&lt;li&gt;Compare two items at a time&lt;/li&gt;
&lt;li&gt;If the two items are out of order swap them&lt;/li&gt;
&lt;li&gt;Once going through the entire list, if we swapped items --&amp;gt; repeat point #2 and #3&lt;/li&gt;
&lt;li&gt;End with a list in order once no items are swapped&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With our example list we&apos;ll start by comparing the first two numbers&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;8&lt;/code&gt; and &lt;code&gt;6&lt;/code&gt; are out of order --&amp;gt; swap their order&lt;/li&gt;
&lt;li&gt;The list is now &lt;code&gt;6, 8, 7, 5, 3, 0, 9&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Move to the next two items and compare &lt;code&gt;8&lt;/code&gt; and &lt;code&gt;7&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Out of order so swap their place&lt;/li&gt;
&lt;li&gt;The list is now &lt;code&gt;6, 7, 8, 5, 3, 0, 9&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Continue until you&apos;ve gone through the entire list&lt;/li&gt;
&lt;li&gt;If you swapped at least once, repeat the steps through the entire list&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Code Snippet&lt;/h2&gt;
&lt;p&gt;Since a lot of computer science example material seems to be Java-centric, here&apos;s what a bubble sort looks like in JavaScript for a little change of scenery. Keep in mind there are other ways to write this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function bubbleSort(nums) {
  let wasSwapped = false
  do {
    wasSwapped = false
    for (let i = 0; i &amp;lt;= nums.length - 1; i++) {
      const firstNum = nums[i]
      const secondNum = nums[i + 1]

      if (secondNum &amp;lt; firstNum) {
        nums[i] = secondNum
        nums[i + 1] = firstNum
        wasSwapped = true
      }
    }
  } while (wasSwapped)
  return nums
}

// Test case
console.log(bubbleSort([8, 6, 7, 5, 3, 0, 9]))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Time and Space Complexity&lt;/h2&gt;
&lt;p&gt;When talking about computer science-y things time and space complexity appears to be important, so I&apos;d be remiss to not mention this.&lt;/p&gt;
&lt;p&gt;In regards to &quot;Big O&quot; this has an average time complexity of On^2. Compared to other sorting methods it is rarely the most efficient, since one needs to go through the entire list at least once (often multiple times) to get a sorted list. It is not recommended to use in production, but since the though process generally aligns well with most people&apos;s natural thought process it is a good starter sorting algorithm used for teaching purposes.&lt;/p&gt;
&lt;p&gt;It has a good space complexity of O(1) or &quot;constant time&quot;.&lt;/p&gt;
&lt;h2&gt;Go forth and sort the bubbles&lt;/h2&gt;
&lt;p&gt;I hope to be back with some similar blog explaining other computer science concepts in the future.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Create Your Own Lambda Layer with NodeJS</title><link>https://danielleheberling.xyz/blog/lambda-layer-example/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/lambda-layer-example/</guid><description>Create Your Own Lambda Layer with NodeJS</description><pubDate>Wed, 22 Jan 2020 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/layer-header.jpg&quot; alt=&quot;Mountains&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by Ivana Cajina on Unsplash&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;While speaking with folks who are new to serverless, I’m often asked “how can I reuse my code across multiple Lambda functions?” One way to do this is by writing your own custom Lambda Layer. In this post, we’re going to build a simple layer using nodeJS, deploy it, and use the layer in a function.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: there is an updated method for using Lambda layers outlined &lt;a href=&quot;https://www.danielleheberling.xyz/blog/lambda-layer-update/&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;What We’re Building&lt;/h2&gt;
&lt;p&gt;We’re going to write a lambda layer that contains a helper function. This function takes in a word or phrase and reverses the string. It’s not overly helpful in prod, but a simple example can be useful in seeing how things are setup.&lt;/p&gt;
&lt;h2&gt;Write Lambda Layer Source Code&lt;/h2&gt;
&lt;p&gt;Create a new directory named palindrome. The file hierarchy will look like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;palindrome
|_ nodejs
    |_ package.json
    |_ palindrome.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The package.json will look like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;palindrome&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The reverse.js file looks like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module.exports = function (phrase) {
  return phrase.split(&quot;&quot;).reverse().join(&quot;&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that &lt;code&gt;palindrome&lt;/code&gt; and &lt;code&gt;nodejs&lt;/code&gt; are folders. The nodejs folder naming and placement is very important because it affects how the layer delivers the code to functions.&lt;/p&gt;
&lt;h2&gt;Deploy the Layer&lt;/h2&gt;
&lt;p&gt;First we’ll need to zip up the layer in order to upload it to the AWS Lambda console.&lt;/p&gt;
&lt;p&gt;You’ll want to &lt;code&gt;cd&lt;/code&gt; into your &lt;code&gt;palindrome&lt;/code&gt; folder via the terminal and run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Zip -r palindrome.zip ./*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should now see a .zip file added in the nodejs directory. This is the file we will upload via the AWS Lambda console.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;palindrome
|_ nodejs
    |_ package.json
    |_ palindrome.js
|_ palindrome.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Login to the AWS console and head to Lambda &amp;gt; Layers &amp;gt; click to create a new layer&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/add-layer.png&quot; alt=&quot;Add Layer&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can then find the layer ARN here...copy/paste this for use later.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/layerarn.png&quot; alt=&quot;Layer Arn&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you prefer to use the AWS CLI instead of the console, you can also do that by following &amp;lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html#configuration-layers-manage&quot; target=&quot;_blank&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;these directions&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-layerversion.html&quot; target=&quot;_blank&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Cloudformation&amp;lt;/a&amp;gt; also offers the ability to set this up; however, be careful about creating the layer in the same stack that has dependencies on the layer. It’s my personal opinion that keeping dependencies in a separate repo/stack might be a better practice to imitate how npm and other package registries work.&lt;/p&gt;
&lt;h2&gt;Create a Function that Uses that Layer&lt;/h2&gt;
&lt;p&gt;I personally like to use Stackery in order to add my resources since it adds all the necessary permissions and connection for me via a drag and drop interface, so these instructions will show how to do that. Keep in mind if you wish to set it up using another method, be sure to remember to add the &amp;lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html#configuration-layers-permissions&quot; target=&quot;_blank&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;correct permissions&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;Head over to the canvas and double click your function to open up this dialog. You’ll then want to paste your layer ARN here:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/stackerysettings.png&quot; alt=&quot;Stackery settings&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Note: you need to do this everytime you update the layer version.&lt;/p&gt;
&lt;p&gt;Not overly necessary for this example, but if you have multiple environments that rely on different layers you can &amp;lt;a href=&quot;https://docs.stackery.io/docs/using-stackery/environments/#setting-configuration-store-values&quot; target=&quot;_blank&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;setup a parameter&amp;lt;/a&amp;gt; to reference this layer that will be dependent on which environment you’re deployed into.&lt;/p&gt;
&lt;p&gt;Also keep in mind that as of my writing this, AWS has a limit of 5 layers per function.&lt;/p&gt;
&lt;h2&gt;Add code to the function&lt;/h2&gt;
&lt;p&gt;The code in your index.js should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const palindrome = require(&quot;/opt/nodejs/palindrome&quot;)

exports.handler = async (event, context) =&amp;gt; {
  // Log the event argument for debugging and for use in local development.
  console.log(JSON.stringify(event, undefined, 2))

  const words = &quot;This string should be reversed&quot;
  const response = palindrome(words)

  return {
    statusCode: 200,
    headers: {},
    response: JSON.stringify(response),
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important thing to note here is the first line where we require from &lt;code&gt;/opt/nodejs&lt;/code&gt;, otherwise you can use your library like you normally would when developing outside of Lambda.&lt;/p&gt;
&lt;p&gt;Once deployed, go ahead and invoke the function. You should see the original string reversed in the function response.&lt;/p&gt;
&lt;p&gt;Check out the full code example in &amp;lt;a href=&quot;https://github.com/deeheber/lambda-layer-example/tree/original-blog-post&quot; target=&quot;_blank&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;this repo&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;All of the official documentation from AWS about Lambda layers can be found &amp;lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html&quot; target=&quot;_blank&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&amp;lt;/a&amp;gt;.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Serverless *Can* be for Everyone - Let&apos;s Build it that Way</title><link>https://danielleheberling.xyz/blog/serverless-can-be-for-all/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/serverless-can-be-for-all/</guid><description>Serverless *Can* be for Everyone - Let&apos;s Build it that Way</description><pubDate>Mon, 23 Dec 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/boothpic.jpg&quot; alt=&quot;AWS re:Invent Booth Picture&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Serverless can bring opportunities by making DevOps more accessible to folks new to the industry. It can also bring opportunities to folks already in the industry that want to spend more time focusing on adding business value. But many technologists, seasoned or otherwise, hear a lot about serverless but don’t always know how to get started. As an early adopter of serverless, I am very interested in closing that gap by encouraging widespread adoption across all types of users and demographic groups. For me, this goal is a personal one.
Serverless IS for everyone, but the onus is on those already involved in the movement to make opportunities and resources more available — that means me! I want to share a bit about my story and goals with the hopes that it might a) inspire you to see how serverless can do the same for you and b) open up a conversation about how people like me can help others interested in serverless.&lt;/p&gt;
&lt;h2&gt;How I Started&lt;/h2&gt;
&lt;p&gt;For those of you who don’t know me, a little introduction.&lt;/p&gt;
&lt;p&gt;I began my adult career as a music teacher and then transitioned to tech support. Tech support was a wonderful experience because each day was a new challenge. Customers would write in with questions, bugs, and feature requests that would often require me to research their inquiry in order to fully address it.&lt;/p&gt;
&lt;p&gt;After a few years of working in tech support, I noticed a pattern.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A customer would write in&lt;/li&gt;
&lt;li&gt;I’d try my best to address their question/resolve their issue&lt;/li&gt;
&lt;li&gt;Oftentimes, I’d be limited in how much I can help them out due to my role’s credentials and my experience-level&lt;/li&gt;
&lt;li&gt;I’d escalate the issue to a software engineer that had the access and expertise to directly fix their issue and/or add their desired feature in the code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Because I wanted to provide a higher level of support to customers, I decided I wanted to be that person who made the code change to directly address the customer’s inquiry vs just handing it off to another individual. I saw a need within my own day-to-day work and a clear way to address it.&lt;/p&gt;
&lt;p&gt;For this reason, I quit my day job and attended a code school to become a software engineer.&lt;/p&gt;
&lt;h2&gt;My Engineering Journey Begins&lt;/h2&gt;
&lt;p&gt;In code school, we spent a lot of time learning JavaScript. One thing that was lacking and became apparent whenever it came time to do our final group project was how to deploy the applications we were building. To be fair, the program was very fast-paced and it was impossible to cover everything, so in the meantime, my team used &amp;lt;a href=&quot;https://www.heroku.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Heroku&amp;lt;/a&amp;gt; to deploy our final project. That worked well, but as an individual, I often found myself pondering how this would look with a production-level enterprise application. What’s the point of developing an application if you don’t know how to share it with the rest of the world?&lt;/p&gt;
&lt;p&gt;Fast forward to my first job post-code school graduation. The team used Docker containers very heavily to deliver our application to the end-users. Since there were so many microservices and moving parts, we also used &amp;lt;a href=&quot;https://aws.amazon.com/ecs/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;ECS&amp;lt;/a&amp;gt; to handle the management of the container clusters.&lt;/p&gt;
&lt;p&gt;On an individual level, I loved my job and really enjoyed writing code to solve problems. One thing that was necessary for my job was writing Dockerfiles and managing the various ECS clusters that were deployed on AWS. I wasn’t actually very interested in this part of the position but, since we were a small team, we had a weekly rotation where a different engineer every week was assigned deployment duty. Sometimes during deployment duty, I’d poke around AWS and see various managed services like SQS, SNS, Lambda etc. I had no idea then that my interest in wanting to know what these did would affect me down the line.&lt;/p&gt;
&lt;p&gt;One day, my boss asked me to review a pull request in which he was experimenting with using a serverless architecture to create a chatbot. The idea was to experiment with a smaller tool used internally to see if this type of architecture might work for customer-facing production applications. I immediately had many questions and became hooked on this type of application building.&lt;/p&gt;
&lt;h2&gt;Changes in Development Workflow and Minset Post-Serverless&lt;/h2&gt;
&lt;p&gt;Fast forward a few months. At this point, I was no longer focused on provisioning and managing Docker containers. Instead, I was utilizing managed services (in this case AWS) to do this work for me. Now that I had the extra mental bandwidth, I could spend time focusing on writing business logic. A mentor once told me that as a software engineer we’re paid to solve problems not to write code — and since I had begun to digest the concept of serverless, I had a better grasp on what that exactly meant.&lt;/p&gt;
&lt;p&gt;Here’s a few highlights of things that noticed from a technical standpoint that needed to shift:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Viewing logs in the cloud provider can be very helpful in troubleshooting code not working as expected.&lt;/li&gt;
&lt;li&gt;Local testing is possible with tooling, but it doesn’t completely replace deploying your application and testing it out in the cloud.&lt;/li&gt;
&lt;li&gt;Service limits are now tied directly to the managed services being used. Usually, these are very high limits. If you max out on a service limit, most cloud providers can raise the limit for you if you ask.&lt;/li&gt;
&lt;li&gt;Event-driven models and async workflows are more common.&lt;/li&gt;
&lt;li&gt;Deployment strategy / Environments / Infrastructure as code is more important than ever.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/assets/meeting.jpg&quot; alt=&quot;Engineering Meeting&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;What&apos;s Next for Serverless&lt;/h2&gt;
&lt;p&gt;Many people mark the start of serverless with the launch of AWS Lambda in late 2014. If you think about it, it really wasn’t that long ago. Serverless as a concept is still in its early stages, so is the community surrounding it.&lt;/p&gt;
&lt;p&gt;I do a lot of time thinking about the serverless community, the degree to which it’s evolved in this short amount of time, and the direction I believe it should go in. It might sound like an oversimplification, but I believe the most important thing that we need to intentionally focus on in regards to evolving the serverless ecosystem is inclusion. We &lt;strong&gt;must&lt;/strong&gt; do everything possible to attract folks from all backgrounds in order to make the community stronger and to build better applications that more accurately reflect the diverse user base we serve. It’s also crucial to continue asking what that looks like: &lt;em&gt;how&lt;/em&gt; do we speak to various individuals and groups? It’s not a static answer.&lt;/p&gt;
&lt;p&gt;For now, I’ve come up with an answer in my own personal work and life: speaking to as many different groups in as many different locations as possible. Earlier in December, I spoke alongside Stackery’s Ecosystem’s Director, Farrah Campbell at &amp;lt;a href=&quot;https://www.stackery.io/blog/reinvent-danielle-talk/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS re:Invent in Las Vegas&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/reInvent.jpg&quot; alt=&quot;reInvent Talk&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This was an amazing and humbling opportunity that I’m still stoked about! But it’s no secret that not everybody is able to attend a huge and costly event like re:Invent for both logistical and financial reasons. So what other communities can I speak to and with?&lt;/p&gt;
&lt;h2&gt;Serverless Days Nashville 🤠&lt;/h2&gt;
&lt;p&gt;Next up is Tennessee for &amp;lt;a href=&quot;https://serverlessnashville.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Serverless Days Nashville&amp;lt;/a&amp;gt;. The topic? Encouraging people to take the first (or umpteenth) step in their serverless journey by using Stackery. If you can/will be in the area in February, I’d love to see you there. Want to connect about serverless and Stackery (or share any sweet tips about Nashville)? &amp;lt;a href=&quot;https://twitter.com/deeheber&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Reach out to me on Twitter&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This post was originally published on https://www.stackery.io/&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Real Time Interactions with Websockets</title><link>https://danielleheberling.xyz/blog/websockets/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/websockets/</guid><description>Real Time Interactions with Websockets</description><pubDate>Tue, 19 Nov 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/phone.jpg&quot; alt=&quot;Phone&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by Quino Al on Unsplash&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;I was working on a web application that allows users to upload plain text files and translate that text into another language (with the caveat that the inbound and outbound languages are supported by &amp;lt;a href=&quot;https://docs.aws.amazon.com/translate/latest/dg/what-is.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS Translate&amp;lt;/a&amp;gt;). The frontend talks to the backend via AWS API Gateway REST endpoints.&lt;/p&gt;
&lt;p&gt;Everything was going well, until I started uploading longer text files to translate. Because of the larger file size, the backend took a while longer to process and I found my HTTP connection response was timing out before the translation was complete. Sure refreshing the webpage until the translation was complete is a solution that would work...but I wanted something a little more seamless.&lt;/p&gt;
&lt;p&gt;Enter websockets. Websockets allow for two way communication between clients (in this case my website in the browser) and a backend (in this case my AWS API, Lambda Functions and S3 Buckets). A popular use case for websockets is real time chat applications. Someone writes and sends a message and the message is received by everyone in that particular chat room.&lt;/p&gt;
&lt;p&gt;In my particular use case, I wanted to send a request to start the translation from my client (the website) to my backend. Whenever the translation is ready, send a response to the client from the backend (it is ready when the translated file is put in an S3 Bucket).&lt;/p&gt;
&lt;h2&gt;Setting Up the Websocket API&lt;/h2&gt;
&lt;p&gt;Luckily Stackery offers the Websocket API Gateway &amp;lt;a href=&quot;https://docs.stackery.io/docs/api/nodes/WebSocketApi/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;as a drag/drop resource&amp;lt;/a&amp;gt; in the editor. So I started by doing this. The important thing for me to add was my routes. AWS has three reserved routes &lt;code&gt;$connect&lt;/code&gt;, &lt;code&gt;$disconnect&lt;/code&gt;, and &lt;code&gt;$default&lt;/code&gt;. It’s up to you if you want to include them in your project or not. &lt;code&gt;$connect&lt;/code&gt; is triggered when a client connects to the websocket API, &lt;code&gt;$disconnect&lt;/code&gt; is triggered when a client disconnects, and &lt;code&gt;$default&lt;/code&gt; is the route that requests fall back on if a request comes in that doesn’t match any of the routes. You can also add your own custom routes in addition to these three default ones.&lt;/p&gt;
&lt;p&gt;The default route key selection mechanism is &lt;code&gt;$request.body.action&lt;/code&gt;, so I decided to stick with that. This means that in the body of your requests, you need to include an “action” key that has a value of one of your routes in order to ensure your request gets sent to the desired route in your API. You can also include any other important key/value pairs that your system is expecting.  For example, I want to send some data to a route named &quot;test&quot;, the body of my request would look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{ “action”: test, “data”: “data I am sending” }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Backend walkthrough&lt;/h2&gt;
&lt;p&gt;On the other end of each API Gateway route, I connected a Lambda Function. I set up my &lt;code&gt;$connect&lt;/code&gt; route to store all client connection ids in a DynamoDB table and my &lt;code&gt;$disconnect&lt;/code&gt; route to remove the connection id from the DynamoDB table when a client disconnects. This is a nice way of keeping track of who is all connected, especially if I want to broadcast changes to all or some of my clients.&lt;/p&gt;
&lt;p&gt;Next I got to work on my &lt;code&gt;BucketPutNotification&lt;/code&gt; Lambda Function. I first wired it up to be triggered on &lt;code&gt;s3:ObjectCreated:*&lt;/code&gt; and &lt;code&gt;s3:ObjectRemoved:*&lt;/code&gt; events.&lt;/p&gt;
&lt;p&gt;This Lambda Function works like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get information about item(s) placed or removed from the S3 Bucket from the event object passed into the function.&lt;/li&gt;
&lt;li&gt;Get all connection ids from the DynamoDB table.&lt;/li&gt;
&lt;li&gt;Send the S3 Bucket information to every client.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In order to send my notification from my Lambda Function to my client(s), I used the &lt;code&gt;postToConnection&lt;/code&gt; method off of the &amp;lt;a href=&quot;https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ApiGatewayManagementApi.html#postToConnection-property&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;ApiGatewayManagementApi JavaScript class&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;Keep in mind that your Lambda Function must have &lt;code&gt;execute-api:ManageConnections&lt;/code&gt; IAM permission in order to be able to do these types of sdk calls. It will also need to know your API connections endpoint as well as your client connection ids. One hiccup I had was neglecting to stringify the data I was sending. According to the documentation, the data being sent must be a string or buffer. I can tell you firsthand if you’re trying to send a JavaScript object, it won’t work.&lt;/p&gt;
&lt;p&gt;&amp;lt;a href=&quot;https://www.npmjs.com/package/wscat&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Wscat&amp;lt;/a&amp;gt; was a tool suggested throughout the AWS docs in order to setup a client to test your websockets. I used this for testing purposes, but my end goal was not a CLI type of interface.&lt;/p&gt;
&lt;p&gt;Here&apos;s what my backend ended up looking like from an architectural standpoint.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/websocketarchitecture.png&quot; alt=&quot;Architecture&quot; /&gt;
(click to expand image)&lt;/p&gt;
&lt;h2&gt;Client (Frontend) Walkthrough&lt;/h2&gt;
&lt;p&gt;For this particular project my desired client was a website built using reactJS, so here’s how I went about getting that setup.&lt;/p&gt;
&lt;p&gt;I first grabbed my Websocket URL from the Stackery dashboard (can also get it from the AWS console) and used the browser &amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Websocket API to connect&amp;lt;/a&amp;gt; via &lt;code&gt;new WebSocket&lt;/code&gt; on &lt;code&gt;ComponentDidMount&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once connected, my application listened for specific events &lt;code&gt;onopen&lt;/code&gt;, &lt;code&gt;onmessage&lt;/code&gt;, &lt;code&gt;onclose&lt;/code&gt;, &lt;code&gt;onerror&lt;/code&gt; in order to determine what type of action to take. These events (as well as others) are outlined in the &amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebSocket&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Websocket API spec&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;If I want my frontend client to send a message to the backend, I can use the &lt;code&gt;send&lt;/code&gt; method off of my connected WebSocket object. Then I’d include a JSON object that looks something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{ “action”: “echomessage”, “data”: “data I am sending” }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;We’ve only scratched the surface, but hopefully this post can give you some insight on how to setup websockets to talk to a web ui client via API Gateway websockets. If you’d like to see a working example of the infrastructure/code, I discussed in this post take a look at &amp;lt;a href=&quot;https://github.com/deeheber/websockets&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;this GitHub repo&amp;lt;/a&amp;gt;.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>AWS Amplify Review</title><link>https://danielleheberling.xyz/blog/aws-amplify-review/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/aws-amplify-review/</guid><description>AWS Amplify Review</description><pubDate>Wed, 06 Nov 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/glasses.jpg&quot; alt=&quot;Glasses&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by Kevin Ku on Unsplash&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Recently, I’ve been playing around with &amp;lt;a href=&quot;https://aws.amazon.com/amplify/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS Amplify&amp;lt;/a&amp;gt;. Wanted to share some thoughts in case anyone is interested in exploring this ecosystem. Disclaimer that these are my opinions and experiences. You may have a different one. This is ok.&lt;/p&gt;
&lt;h4&gt;Why Did I Do this?&lt;/h4&gt;
&lt;p&gt;If you talked to me three years ago, I would’ve labeled myself as a software engineer who specializes in frontend. I embraced this label until I was introduced to the world of serverless. All of a sudden, I realized that I could write infrastructure as code to use managed services to create more powerful systems. Systems that would auto scale to accommodate various workloads while not having to reinvent the wheel. No longer did I need to be incredibly bored writing Dockerfiles and orchestrating clusters of containers.&lt;/p&gt;
&lt;p&gt;Disclaimer that serverless is much much more than auto scale and utilizing managed services, but this blog post isn’t on that topic. I highly recommend &amp;lt;a href=&quot;https://read.acloud.guru/serverless-is-a-state-of-mind-717ef2088b42&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;this blog post&amp;lt;/a&amp;gt; if you want to get into that topic. Also disclaimer about Docker/containers etc. I have a strong belief that you should use the right tool for the job and sometimes that tool happens to be containers. Just from my personal experience, I haven’t gotten that much joy out of writing Dockerfiles etc.&lt;/p&gt;
&lt;p&gt;This world of serverless also brought along a heavy focus on  using and learning about various AWS services. It’s been a fun ride, but I do still find myself enjoying jumping into the frontend of an application every now and then, so the aws-amplify NPM  package was a great utility library to use when connecting my frontend to various AWS services. With all of the new features of AWS Amplify Console, I decided to take a deeper look to see what this ecosystem is like.&lt;/p&gt;
&lt;h4&gt;What I Built&lt;/h4&gt;
&lt;p&gt;Since I learn best by doing, I decided to build the classic TODO application with AWS Amplify. This would utilize CRUD (create, read, update, delete) endpoints talking to my &amp;lt;a href=&quot;https://reactjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;ReactJS&amp;lt;/a&amp;gt; frontend. As a bonus, I decided to add authentication. Authentication and Cognito are two things that have always been pretty difficult to implement. This seems to be true with some of my conversations with pretty senior Software Engineers too.&lt;/p&gt;
&lt;h4&gt;GraphQL API&lt;/h4&gt;
&lt;p&gt;When first setting up the API, I noticed an option for &amp;lt;a href=&quot;https://graphql.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;GraphQL&amp;lt;/a&amp;gt;...I figured why not let’s go down this journey even further. After a few prompts from the Amplify CLI and an &lt;code&gt;amplify push&lt;/code&gt;...I had a CRUD GraphQL API setup complete with schema generation, mutations, queries, subscriptions, resolvers, and a DynamoDB table as my data source. If you would’ve asked me about some of those words a few months ago, I wouldn’t have been able to tell you what they mean...but AWS Amplify made it super easy to setup, deploy, and learn more about those things. I also enjoyed playing around in the Appsync console creating test mutations and queries to experiment more with GraphQL.&lt;/p&gt;
&lt;h4&gt;Setting up Authentication with Cognito User Pools&lt;/h4&gt;
&lt;p&gt;After getting a CRUD api setup for my TODO app, it was time to add authentication. This way someone would need to login to see the TODO items, and they could only create, read, update, and delete their own TODO items.&lt;/p&gt;
&lt;p&gt;I first ran &lt;code&gt;amplify add auth&lt;/code&gt; in the console and followed the prompts to get setup. There are options where you can authenticate with social media logins, but I decided to use Cognito User Pools. You can also customize the sign in workflow.&lt;/p&gt;
&lt;p&gt;After another &lt;code&gt;amplify push&lt;/code&gt;, I now needed to update my GraphQL schema to only allow the owner of the TODO item to CRUD it. Luckily AWS Amplify has an &lt;code&gt;@auth&lt;/code&gt; directive that does just this.&lt;/p&gt;
&lt;p&gt;Here’s what my schema looked like before:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Todo @model {
  id: ID!
  name: String!
  complete: Boolean
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And after&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Todo @model @auth(rules: [{allow: owner}]) {
  id: ID!
  name: String!
  complete: Boolean
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also make this more complicated if you want. There are also the options to only allow specific groups, only allow certain types of operations (i.e. read, update etc), and add field level auth rules too. Probably more that I didn&apos;t end up using.&lt;/p&gt;
&lt;p&gt;I also added a login screen by using the aws-amplify &lt;code&gt;withAuthenticator&lt;/code&gt; higher order component. I chose not to customize it, but it is flexible to allow for customization (https://aws-amplify.github.io/docs/js/authentication#customize-withauthenticator). Overall it took me minutes to setup what once took me hours (and sometimes days) to do.&lt;/p&gt;
&lt;h4&gt;Frontend Deploy&lt;/h4&gt;
&lt;p&gt;To host the frontend on AWS, the choice most folks go with is uploading their frontend code to an S3 Bucket and sometimes adding a CloudFront CDN in front of the S3 Bucket. This seems like it should be simple to automate, but it really isn&apos;t quite yet. With Amplify it’s easy. All you need to do is &lt;code&gt;amplify add hosting&lt;/code&gt; and &lt;code&gt;amplify publish&lt;/code&gt; then you’re done! Much nicer than writing a &amp;lt;a href=&quot;https://www.danielleheberling.xyz/blog/deploy-frontend/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;weird Lambda Function&amp;lt;/a&amp;gt; to do this.&lt;/p&gt;
&lt;h4&gt;Summary&lt;/h4&gt;
&lt;p&gt;Amplify is a great choice if you want to get started with GraphQL/Appsync. It’s also good for frontend focused applications that needs to utilize a backend built using AWS. Overall, I’d recommend it to folks who are new to AWS or seasoned folks who want to get a project up and running quickly. There are also great CI/CD tools you can use with the &amp;lt;a href=&quot;https://aws.amazon.com/amplify/console/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Amplify Console&amp;lt;/a&amp;gt; which I didn’t touch on in this post. Major bonus that the examples in the AWS Amplify docs are using React hooks.&lt;/p&gt;
&lt;p&gt;A downside that I noticed was there didn’t seem to be a way to eject from the framework. This could be problematic if down the line I decided I wanted to use &amp;lt;a href=&quot;https://aws.amazon.com/serverless/sam/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS SAM&amp;lt;/a&amp;gt; or the &amp;lt;a href=&quot;https://serverless.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Serverless framework&amp;lt;/a&amp;gt; instead. Also as someone who likes to understand how things work in the event of error messages...it seemed like AWS  Amplify performed a lot of “magic” under the hood and I could not discern how the framework was doing things. It would be amazing if the framework did all the stuff it does and then gives an option to output a CloudFormation template or something could be used to port to another framework.&lt;/p&gt;
&lt;p&gt;Anyway give it a shot and let me know what you think. You won’t regret it. If you want to see my completed todo app, check it out here: https://github.com/deeheber/amplify-todo.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>The Secret Lives of Failed SQS Messages</title><link>https://danielleheberling.xyz/blog/the-secret-lives-of-failed-sqs-messages/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/the-secret-lives-of-failed-sqs-messages/</guid><description>The Secret Lives of Failed SQS Messages</description><pubDate>Wed, 18 Sep 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/mailbox.jpg&quot; alt=&quot;Mailbox&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo by Samuel Zeller on Unsplash&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A common pattern in serverless architecture is to have a queue before a function. This is great because you can create a second queue for all of the messages that failed in the function execution (or, if we want to put it in terms that don’t sound like we’re aggressively shaming them, we can classify them as having “encountered an error at some point”). This second queue is known as a “dead letter queue” or DLQ for short. If messages arrive in the DLQ, you can analyze what might have caused the error by reviewing any relevant logs, making changes to your stack, and running a redrive function to retry those messages. DLQs are useful for debugging your serverless application because they let you isolate problem-messages to determine why their processing isn’t working.&lt;/p&gt;
&lt;p&gt;A redrive function is how we get those failed messages out of the DLQ and back into the original pipeline to retry the operation. I wanted to set up this type of architecture in a new stack using Stackery and decided to take a look around the internet for inspiration on different patterns and ideas on how to go about it. I found lots of information about dead letter queues, but no examples that demo-ed a redrive function to retry those failed messages. This post is meant to serve as a nice example for folks like me who want to do this on AWS and haven’t found many examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update December 1, 2021&lt;/strong&gt; - AWS has added the ability to redrive your SQS messages in the console. See &lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2021/12/amazon-sqs-dead-letter-queue-management-experience-queues/&quot;&gt;here&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update June 9, 2023&lt;/strong&gt; - AWS has added a new set of APIs to the AWS CLI for DLQ redrive. See &lt;a href=&quot;https://aws.amazon.com/blogs/aws/a-new-set-of-apis-for-amazon-sqs-dead-letter-queue-redrive/&quot;&gt;here&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h2&gt;The Setup&lt;/h2&gt;
&lt;h3&gt;Happy Path&lt;/h3&gt;
&lt;p&gt;Let’s start out with our happy path flow. I’ve set up a stack that consists of an SNS topic &amp;gt; SQS queue &amp;gt; Lambda function. In Stackery it looks something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/happyPath.png&quot; alt=&quot;Happy Path&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Retry Path&lt;/h3&gt;
&lt;p&gt;In the event that something goes wrong during the function execution, I set up a second queue; this will serve as my DLQ for failed messages to be delivered. I then added a redrive function to allow me to retry those failed messages through the original pipeline. Here’s what that set up looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/retryPath.png&quot; alt=&quot;Retry Path&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I added a RedrivePolicy to the original SQS queue to point those failed messages to my DLQ by adding some &amp;lt;a href=&quot;https://aws.amazon.com/cloudformation/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Cloudformation&amp;lt;/a&amp;gt; in my template.yaml:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Queue:
  Type: AWS::SQS::Queue
  Properties:
    QueueName: !Sub ${AWS::StackName}-Queue
    RedrivePolicy:
      deadLetterTargetArn: !GetAtt DeadLetterQueue.Arn
      maxReceiveCount: 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, the queue’s message retention period is 4 days. I wanted to give myself some extra time to review the messages in the DLQ, so I upped the MessageRetentionPeriod on my DLQ to the max allowed 14 days like this in my template.yaml. This is the sci-fi beauty of my profession: creating &lt;em&gt;more time&lt;/em&gt; is possible! As many before me have discussed on the floor of a dorm room, time is a malleable concept— and at least in software engineering, it has a practical purpose like gaining more days to review DLQ messages. Note that the values are in seconds:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DeadLetterQueue:
  Type: AWS::SQS::Queue
  Properties:
    QueueName: !Sub ${AWS::StackName}-DeadLetterQueue
    MessageRetentionPeriod: 1209600
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Redrive Function Content&lt;/h2&gt;
&lt;p&gt;Now that the base architecture is all set up, let’s take a look inside the redrive function. This is how I decided to set mine up, but keep in mind that there are many different ways you can do this:&lt;/p&gt;
&lt;p&gt;Here’s the high-level steps of what our retry function needs to do:
Receive messages from the DLQ
If no messages are received exit the function
If there are received messages, loop through each message &amp;gt; send it to the original queue
Delete the message from the DLQ on a successful send
Repeat&lt;/p&gt;
&lt;p&gt;Here’s what the code looks like written in nodeJS:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const aws = require(&quot;aws-sdk&quot;)
const sqs = new aws.SQS()

exports.handler = async (event) =&amp;gt; {
  // Log the event argument for debugging and for use in local development.
  console.log(JSON.stringify(event, undefined, 2))

  while (true) {
    try {
      // Use long polling to avoid empty message responses
      const receiveParams = {
        QueueUrl: process.env.DLQ_URL,
        MaxNumberOfMessages: 10,
        WaitTimeSeconds: 1,
      }

      // Get messages from the DLQ
      // Continue looping until no more messages are left
      const DLQMessages = await sqs.receiveMessage(receiveParams).promise()

      if (!DLQMessages.Messages || DLQMessages.Messages.length === 0) {
        console.log(`NO MESSAGES FOUND IN ${process.env.DLQ_URL}`)
        // Exit the loop since there aren&apos;t any messages left
        break
      }

      console.log(`RECEIVED ${DLQMessages.Messages.length} MESSAGES`)

      for (const message of DLQMessages.Messages) {
        // Send message to original queue
        const outboundMessage = {
          MessageBody: message.Body,
          QueueUrl: process.env.QUEUE_URL,
        }

        console.log(`SENDING: ${JSON.stringify(outboundMessage, null, 2)}`)
        await sqs.sendMessage(outboundMessage).promise()
        console.log(&quot;SEND MESSAGE SUCCEEDED&quot;)

        // Delete message from DLQ
        const deleteParams = {
          QueueUrl: process.env.DLQ_URL,
          ReceiptHandle: message.ReceiptHandle,
        }

        console.log(`DELETING: ${JSON.stringify(deleteParams, null, 2)}`)
        await sqs.deleteMessage(deleteParams).promise()
        console.log(&quot;DELETE MESSAGE SUCCEEDED&quot;)
      }
    } catch (err) {
      console.log(`AN ERROR OCCURRED: ${err.message}`)
      console.log(JSON.stringify(err, null, 2))
      throw err
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;What to keep in mind when setting up a redrive function.&lt;/h3&gt;
&lt;h4&gt;Receive message step&lt;/h4&gt;
&lt;p&gt;The default MaxNumberOfMessages to return is 1, so you’ll most likely want to up that number depending on your use case. I just went to the max allowed which is 10.
If you hypothetically have 10 messages in your DLQ and do receiveMessage on that queue with a MaxNumberOfMessages set to 10, you might possibly not receive all of those messages in a single receiveMessage batch. It’s important to have looping to ensure that you receive all of the messages when they are available.
Enabling long polling can help reduce the cost of SQS by reducing the number of empty responses. I decided to go this route by adding WaitTimeSeconds to my receiveMessage params.&lt;/p&gt;
&lt;h4&gt;Delete message step&lt;/h4&gt;
&lt;p&gt;Ensure the send message was successful before deleting it. I decided to wrap the entire function in a try, catch...so if there’s an error the function will stop processing.&lt;/p&gt;
&lt;p&gt;As you can see, it does require a bit of additional setup, but creating dead letter queues and a redrive function to re-run failed messages can be extremely helpful in your application’s life cycle. You can go to https://github.com/deeheber/redrive to see an example of a simple stack that uses a DLQ and a redrive function in action.&lt;/p&gt;
&lt;p&gt;For a solid and succinct description of the benefits of a DLQ (alongside lots of other info about them,) check out the AWS documentation &amp;lt;a href=&quot;https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html#sqs-dead-letter-queues-benefits&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This post was originally published on https://www.stackery.io/&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Automated React Frontend Deploys</title><link>https://danielleheberling.xyz/blog/automated-react-frontend-deploys/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/automated-react-frontend-deploys/</guid><description>Automated React Frontend Deploys</description><pubDate>Thu, 29 Aug 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;As the frontend and backend are slowly merging closer together, more and more folks are writing full stack applications. While Serverless mostly is referring to the underlying infrastructure that tends to live a bit closer to the backend side, you can also use a serverless architecture to build and deploy the frontend portion of your application.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Technology is always changing. I found what I feel is a &quot;better&quot; way to build/deploy a website to AWS. Check it out &lt;a href=&quot;https://danielleheberling.xyz/blog/building-my-own-jamstack/&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;What We&apos;re Building&lt;/h2&gt;
&lt;p&gt;&amp;lt;a href=&quot;https://www.danielleheberling.xyz/blog/text-to-speech/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Previously&amp;lt;/a&amp;gt;, I wrote an application that translates written text to speech. I was getting tired of having to recall the different API endpoints to hit and wanted a nice UI to better manage these files...so I got to work on building a frontend.&lt;/p&gt;
&lt;p&gt;I decided to keep it simple/minimal in style as this is for personal use...so here’s how it ended up looking at the end.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/finishedFrontend.png&quot; alt=&quot;Finished frontend&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Initial Setup&lt;/h2&gt;
&lt;p&gt;I started by adding an s3 bucket titled “FrontEnd” and enabled website hosting. In between my FrontEnd s3 bucket and my API gateway resource, I added the PopulateFrontend Lambda function to bridge the two resources together.&lt;/p&gt;
&lt;p&gt;We’ll go more into detail on this later, but this Lambda function’s purpose is to build deploy the frontend part of the application everytime there is a deploy of the stack.&lt;/p&gt;
&lt;p&gt;Also since we’re now going to be using a browser to make requests to the backend API, &amp;lt;a href=&quot;https://github.com/deeheber/text-to-speech-converter/blob/blog-post-2/template.yaml#L37&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;I enabled cors on my API gateway endpoints&amp;lt;/a&amp;gt; and &amp;lt;a href=&quot;https://github.com/deeheber/text-to-speech-converter/blob/blog-post-2/src/ConvertToAudio/index.js#L65&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;ensured my backend Lambdas allowed cors in their headers on each return&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;Here’s how the visualization of the completed stack looks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/fullstackApp.png&quot; alt=&quot;Finished stack&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Populate Frontend - Custom Resource&lt;/h2&gt;
&lt;p&gt;There are a few ways you could go about automating the build/deploy of your frontend. After thinking through different options, I finally decided to use a &amp;lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;CloudFormation Custom Resource&amp;lt;/a&amp;gt;. The YAML for my custom resource in the &lt;code&gt;template.yaml&lt;/code&gt; file looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PopulateFrontendDeployTrigger:
  Type: Custom::FunctionDeployTrigger
  Properties:
    ServiceToken: !GetAtt PopulateFrontend.Arn
    DeploymentTimestamp: !Ref DeploymentTimestamp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So when the Type property starts with Custom::, CloudFormation then looks at the ServiceToken field and sends a request to the resource specified on every deploy. In this case, the requests will go to my PopulateFrontend function. Anything after Custom:: can be whatever you&apos;d like. Personally, I&apos;d suggest naming it something that makes sense as to what it does...but that&apos;s totally up to you.&lt;/p&gt;
&lt;p&gt;When using custom resources, it is very important to send back success and failure messages from the resource that was requested by CloudFormation. If you do not do this, then often times CloudFormation will hang for hours in a state that is difficult to exit. There are a few ways to go about doing this, but I decided to use the &amp;lt;a href=&quot;https://www.npmjs.com/package/cfn-custom-resource&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;cfn-custom-resource npm package&amp;lt;/a&amp;gt;. Here is where I’m using this library to send my success or error message response back to CloudFormation: https://github.com/deeheber/text-to-speech-converter/blob/blog-post-2/src/PopulateFrontend/index.js#L25-L30.&lt;/p&gt;
&lt;h2&gt;Populate Frontend - Contents&lt;/h2&gt;
&lt;p&gt;Now that you know about custom resources and how they work, let’s dig a little deeper into what the PopulateFrontend function is doing and how.&lt;/p&gt;
&lt;p&gt;So on every deploy this function is triggered via the request sent to the function ARN from our custom CloudFormation resource. When triggered it does the following to build and deploy our frontend.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Copies the frontend files to the Lambda &lt;code&gt;/tmp&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;Grabs the API url from the function’s environment variable&lt;/li&gt;
&lt;li&gt;Writes the API url to a config file (this is needed for the frontend to be able to talk to the backend)&lt;/li&gt;
&lt;li&gt;Runs &lt;code&gt;npm install&lt;/code&gt; and &lt;code&gt;npm build&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Npm build puts all of the built files in a folder called &lt;code&gt;/build&lt;/code&gt;, so we then send all of those built files to our FrontEnd s3 bucket. Since the s3 bucket is enabled to static website hosting, it should “just work.”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&apos;s the final code for that function if you&apos;d like to take a look: https://github.com/deeheber/text-to-speech-converter/blob/blog-post-2/src/PopulateFrontend/index.js&lt;/p&gt;
&lt;h2&gt;More on the Config File&lt;/h2&gt;
&lt;p&gt;I could’ve easily used another http request library such as &amp;lt;a href=&quot;https://www.npmjs.com/package/axios&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Axios&amp;lt;/a&amp;gt;, but since AWS Amplify has a &amp;lt;a href=&quot;https://www.npmjs.com/package/aws-amplify&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;nice library&amp;lt;/a&amp;gt; that works well with sending http requests to API Gateway, I decided to go that route. Side plug...it also works well with GraphQL/AWS AppSync if http is not your preference.&lt;/p&gt;
&lt;p&gt;In order to make calls to my API Gateway http endpoints via this framework, I need to feed it the &lt;code&gt;API_URL&lt;/code&gt; in a config file, so I utilized the PopulateFrontend function to grab this url and write it into a config file on every deploy. It might be a tad bit overkill, but if I’m deploying to different environments I won’t need to think twice about this configuration...it’ll already be done for me.&lt;/p&gt;
&lt;p&gt;As a note, if you want to work on the react frontend locally, you’ll need to add a config.js file in src/PopulateFrontend/frontend-content/src that looks something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default {
  backendAPI: &quot;ENTER YOUR API GATEWAY URL HERE&quot;,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;Hopefully this post gave you a nice example of how to implement an automated build/deploy of your frontend code with every stack deploy. You can see all of the code I referenced this this post here: https://github.com/deeheber/text-to-speech-converter/tree/blog-post-2.&lt;/p&gt;
&lt;p&gt;If you want something more customized or complex, I’d recommend checking out AWS’s &amp;lt;a href=&quot;https://aws.amazon.com/codebuild/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;code-build&amp;lt;/a&amp;gt; and/or &amp;lt;a href=&quot;https://aws.amazon.com/codepipeline/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;code-pipeline&amp;lt;/a&amp;gt; services. If time permits, perhaps I’ll do another blog post or blog on those services.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Rekognition - Facing My Artificial Intelligence Fears</title><link>https://danielleheberling.xyz/blog/face-compare/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/face-compare/</guid><description>Rekognition - Facing My Artificial Intelligence Fears</description><pubDate>Wed, 03 Jul 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/rekognition-me.png&quot; alt=&quot;Me in rekognition&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Artificial intelligence (henceforth referred to as AI for the rest of this post) is becoming more and more prominent in our lives whether we realize it or not. This fact both excites and terrifies me as an individual. It is exciting because AI has the major potential to make all of our lives easier. It is terrifying because as a minority on multiple levels, I fear that my unique view and challenges in the world might not be reflected in those learning models if folks who look and act like me are not actively involved in the creation of those models. Furthermore, I’m also afraid for folks who don’t have as much privilege as I do.&lt;/p&gt;
&lt;p&gt;So like the serverless developer that I am, I decided to scratch the surface by playing around a bit with the AWS service &amp;lt;a href=&quot;https://aws.amazon.com/rekognition/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Rekognition&amp;lt;/a&amp;gt;. I wanted to see what I could do with it and also to test it’s accuracy.&lt;/p&gt;
&lt;h2&gt;Detect Labels&lt;/h2&gt;
&lt;p&gt;I began my journey with the detect labels API. To me this was super interesting, because all that I had to do was feed the API an image and the managed AWS service would do all of the heavy lifting and return a list of labels along with the level of confidence in that label. For example it might return something that would suggest “I’m 90% confident there is a dog” in this image.&lt;/p&gt;
&lt;p&gt;Stackery and the AWS SDK made this possible for me to build. I set up an S3 Images bucket where uploading an image would trigger my ImageProcessor Lambda function. Then within the ImageProcessor Lambda function, I used the AWS SDK to make calls to Rekognition. For simplicity sake, I decided to just send the results to my Cloudwatch logs. Here’s what that architecture looked like.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/detectLabels.png&quot; alt=&quot;Detect Labels Stack&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you&apos;d like to see my code for this stack, you can check it out &amp;lt;a href=&quot;https://github.com/deeheber/face-match/tree/detectLabels&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;h2&gt;Compare Faces&lt;/h2&gt;
&lt;p&gt;Overall, the images that I uploaded were surprisingly accurate in the results returned, so I decided to go a step further. How about I create an application that when I upload an image of a person, it does a comparison through a collection of images of myself to see if there are any face matches? When I spoke about this idea with a coworker, he suggested I use this as a way to unlock a diary. Hardware on the diary takes a picture of me and then if there is a face match, the diary unlocks. Unsure if I’ll do that exact thing with this, but it was a pretty funny idea.&lt;/p&gt;
&lt;p&gt;First, I needed to setup a collection that contained pictures of myself. These pictures are the ones that I will be comparing to, so it’s important to get a nice mix of pictures that are reflective of how I would look on an average day. So I opened up my terminal, installed the &amp;lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;aws cli&amp;lt;/a&amp;gt;, and ran:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aws rekognition create-collection --collection-id Danielle&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I then set up a CollectionImages S3 bucket in which a put or delete event will trigger my ManageCollection Lambda function.&lt;/p&gt;
&lt;p&gt;When I upload an image to the CollectionImages S3 bucket (put), the ManageCollection Lambda function adds the face to the collection via &amp;lt;a href=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/API_IndexFaces.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;index faces&amp;lt;/a&amp;gt;. Index faces will index all faces in an image, so for this case I only uploaded images that were me alone and set MaxFaces to 1. Upon successful index, I then stored the FaceId and bucket information in a DynamoDB table for later use.&lt;/p&gt;
&lt;p&gt;When I delete an image from the CollectionImages S3 bucket, the ManageCollection Lambda function retrieves the FaceId that I stored in DynamoDB when uploading the image and uses that information to call &amp;lt;a href=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/API_DeleteFaces.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;delete faces&amp;lt;/a&amp;gt;. DeleteFaces removes that face from the collection. I then want to keep my data tidy, so I remove the information related to that deleted image from my DynamoDB table.&lt;/p&gt;
&lt;p&gt;Now that my collection is all set, I have pictures to compare to...now let’s get to doing the face comparisons. To do this, I set up an Images S3 bucket. When I upload an image to that bucket, the put will trigger my CompareFaces Lambda function. The function then uses the &amp;lt;a href=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/API_SearchFacesByImage.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;searchFacesByImage&amp;lt;/a&amp;gt; method in the rekognition api to compare the face in the image I just uploaded to the images in my collection. This endpoint returns a lot of stuff, but I’m mostly interested in the list of face matches along with the confidence (in percentage). To keep things simple, I set it up to just log out the results to the CloudWatch logs for my CompareFaces Lambda function.&lt;/p&gt;
&lt;p&gt;Here’s what that architecture looks like in Stackery:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/compareFaces.png&quot; alt=&quot;Compare Faces Stack&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can also take a peek at my source code &amp;lt;a href=&quot;https://github.com/deeheber/face-match/tree/compareFaces&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;here&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;h2&gt;Circular Dependency Errors&lt;/h2&gt;
&lt;p&gt;If you take a look at my template.yaml in the detectLabels branch, you’ll see something really bad. &amp;lt;a href=&quot;https://github.com/deeheber/face-match/blob/detectLabels/template.yaml#L23&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Here&apos;s&amp;lt;/a&amp;gt; where I&apos;m talking about.&lt;/p&gt;
&lt;p&gt;I essentially gave my ImageProcessor function GetObject access to all S3 buckets in my entire AWS account. The best practice is to give the minimal permissions necessary…so I should’ve just given that function GetObject access to my Images S3 bucket.&lt;/p&gt;
&lt;p&gt;The problem that I ran into when I did that was a circular dependency error when attempting to deploy this via Stackery...this specific error comes from CloudFormation. I had a function that responded to an event on the Images S3 bucket, but it also needed access to GetObject from that same S3 bucket. I had never run into this issue before and like the smart developer I am, I did a web search for that error and found this &amp;lt;a href=&quot;https://aws.amazon.com/blogs/infrastructure-and-automation/handling-circular-dependency-errors-in-aws-cloudformation/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;wonderful post&amp;lt;/a&amp;gt; that solved my issue.&lt;/p&gt;
&lt;p&gt;For demo purposes, I did not update that in the detectLabels branch, but you can see that I fixed that &amp;lt;a href=&quot;https://github.com/deeheber/face-match/blob/compareFaces/template.yaml#L49&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;on this line&amp;lt;/a&amp;gt; in the compareFaces branch. Since I already knew what my S3 bucket’s name will be, I place that in there instead of referencing the CloudFormation resource directly. So now, that function no longer has GetObject access to every single S3 bucket in my AWS account...it just has GetObject permission on that specific bucket without a circular dependency error.&lt;/p&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;Overall my testing has shown that AWS Rekognition is a lot more accurate than I had imagined. Despite this, I do think that it is important to ensure that new technology is representative of everyone especially if we plan to use AI to make decisions/judgements on things that have the potential to leave a large impact on an individual&apos;s life. This initial hands off experiment has gotten me more interested in exploring AI further and possibly learning how to create my own AI models in the future.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Building a Better Product through Customer Service</title><link>https://danielleheberling.xyz/blog/building-a-better-product-through-customer-service/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/building-a-better-product-through-customer-service/</guid><description>Building a Better Product through Customer Service</description><pubDate>Tue, 21 May 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/teamwork.jpg&quot; alt=&quot;Teamwork&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As an individual coming from a customer support background that knows the value of being on the front lines, I am ecstatic that Stackery focuses on the customer across all teams. Moreover, I’m excited by the impact of having the front line perspective ingrained in the product we build. After all, the customers are the reason why we work so hard at what we do. In this post, I’m going to highlight some of the major benefits that I’ve noticed as a software engineer working in a periodic customer service rotation capacity. Here at Stackery, we call this the “Support Hero” role.&lt;/p&gt;
&lt;h2&gt;1. Better time management&lt;/h2&gt;
&lt;p&gt;The flow of work is very different when working on engineering tasks vs customer service tasks. The first thing that I noticed when working the customer support rotation was that I had to make sure I was in the office and available to receive customer inquiries from 9am-5pm every day that week. If I had to step out for a meeting or break, I had to keep an eye on our support channels and/or ask a teammate to cover for me while away.
During my weeks of software engineering, I have a bit more freedom and flexibility when it comes to starting and ending my work day and can pretty much get up and take a break without needing to think about coverage.
Beyond making sure I’m available if customers have questions, I also find myself needing to be flexible in order to balance having multiple conversations with different folks, oftentimes simultaneously. Since all inquiries are unique, they each require a different level of attention, care, and focus.&lt;/p&gt;
&lt;h2&gt;2. Clearer communication with others&lt;/h2&gt;
&lt;p&gt;A lot of the times companies have their own internal jargon of what they call things. This internal jargon often doesn’t map directly to what external customers are calling things. It’s easy to get into a habit of using a specific set of terms and phrases so much that you just assume everyone speaks the same way. When chatting with customers, I find that Stackery is not the exception to this and when I started my rotation, I often found myself asking for more clarification when a customer submitted an inquiry— I had to be certain we were on the same page.
The more I speak with customers, the better I get at being able to intuitively read what they’re looking to do. This is a better way because the conversation can be more natural instead of a back-and-forth sequence of asking questions.
A lot of times, I find answering the customer’s question to be quicker than that initial discovery of what they’re looking to do. It’s important to not jump ahead and skip that very important step at the beginning of the conversation.&lt;/p&gt;
&lt;h2&gt;3. A different perspective&lt;/h2&gt;
&lt;p&gt;I think many people in the modern workforce have worked at a company where the software engineers sit off in their own world and don’t get to hear the direct feedback from customers about the product they’re building. When creating a product and staring at the user interface for 40+ hours a week, it is easy to gain some heavy biases around how folks use the product and about what customers want.
Through working in customer support, I’ve discovered a lot of bugs and things that seem intuitive to me, but in reality, are confusing to the bulk of the population. Being able to hear these concerns directly from a customer can help me attach a face and story to how this affects their daily work and gives me a higher urgency to ensure that reported issues are handled in a timely manner.&lt;/p&gt;
&lt;h2&gt;4. Increased product knowledge&lt;/h2&gt;
&lt;p&gt;An enterprise sized application is huge and has many different moving parts. It is impossible to fully master all of it. This is especially true if you’re a software engineer working on a product. In my personal experience, I’ve found myself to be an expert on the parts of the application that I had a key part in developing while I have significantly less knowledge about the other parts.
Working in customer service, customers will ask questions related to every part of the application. I love this because it gives me more incentive to explore the other parts of the application that I might otherwise be unfamiliar with.&lt;/p&gt;
&lt;h2&gt;5. A better product. Period.&lt;/h2&gt;
&lt;p&gt;I want to give one example of how our customer support can improve the product. One of Stackery’s super powers is bundling and managing the configuration and environment variables so that all the settings you need to run in “staging” or “production” are in one place. We on the team knew that this feature helped users take the same set of functions and resources and deploy them in different environments.
Our users, however, were more confused by these tools than empowered. We used that feedback to build something great.&lt;/p&gt;
&lt;p&gt;Every week the “Support Hero” would hear from users confused by what these “environment” settings even did. We took their responses and spent some time redesigning the interface, and along with other feedback we designed a new “deploy” interface that clearly showed the environments you’d created, and let you choose to deploy your app to seperate environments in sequence.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/deploy-menu.png&quot; alt=&quot;Deploy Meenu&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;our updated interface made the tool we’d already built a lot easier for our users to understand and use&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This feedback, and the improvements, wouldn’t have happened if we didn’t have engineers doing support. The users weren’t asking for more environment support, it wasn’t a feature they were really aware of, so they weren’t going to tell Sales they wanted something they hadn’t heard of. And if all we’d looked at was user behavior, we probably would have assumed “people just don’t want to manage the same app in separate environments.” It was only by dealing with users directly, realizing that a tool we already had could help users if the User Interface (UI) was better.&lt;/p&gt;
&lt;p&gt;With this process in place, and the product improvements driven by support hero experiences, we’ve seen our weekly active Stackery users triple. I beleive that a number of factors have influenced this including our the launch of &amp;lt;a href=&quot;https://www.stackery.io/blog/how-to-serverless-locally/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Stackery local invoke&amp;lt;/a&amp;gt; but it also seems clear to me that the use of Stackery is growing because we are able to help trial users and inquiring minds more efficiently.&lt;/p&gt;
&lt;p&gt;Acheive great things
This type of process may not be appropriate for all organizations, but I thought I’d share a little bit about how our “Support Hero” philosophy has strengthened the Stackery product. I also believe that this has strengthened the Stackery software engineers on an individual level. I see my coworkers inspired to reflect on how our company is engaging with customers. It to makes the Stackery product and service better.&lt;/p&gt;
&lt;p&gt;Want to benefit from a direct line of communication with Stackery’s serverless engineers? There are a variety of ways you can ask us questions, but one of the most fun is to join our team’s weekly livestream on Wednesdays at 10 AM PDT. Sometimes there are topics and guest hosts and sometimes we leave it as an open demo. Either way, it’s a great chance to get to know Team Stackery and ask us anything!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This post was originally published on https://www.stackery.io/&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Building a Text to Speech Converter with AWS Polly</title><link>https://danielleheberling.xyz/blog/text-to-speech/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/text-to-speech/</guid><description>Building a Text to Speech Converter with AWS Polly</description><pubDate>Mon, 06 May 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;Recently, I spent some time building my own serverless text to speech converter and thought it might be helpful to share a bit about what I’ve learned during the process.&lt;/p&gt;
&lt;p&gt;The Inspiration
I have been studying to take some AWS certification exams. One of the most helpful resources for me in preparing for those exams has been the &amp;lt;a href=&quot;https://acloud.guru/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;A Cloud Guru&amp;lt;/a&amp;gt; courses. One of the lessons was a lab in which you build your own text to speech converter in Python for the purposes of being able to review the notes that you’ve taken during the course via audio.&lt;/p&gt;
&lt;p&gt;After following along with the lab, I wondered what that application would look like if it were built using &amp;lt;a href=&quot;https://www.stackery.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Stackery&amp;lt;/a&amp;gt; and NodeJS...so I decided to try it out on my own. Because I take a lot of public transit, I figured this application would be useful to take my notes in text form and convert into mp3 files that can be listened to while on the go.&lt;/p&gt;
&lt;h2&gt;The Architecture&lt;/h2&gt;
&lt;p&gt;I began by thinking through all of the high level steps and added/connected the resources in Stackery. There were a few iterations and many different ways to go about building this, but here’s the architecture I eventually settled on.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/text-to-speech-stack.png&quot; alt=&quot;Text to speech stack&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We have two API gateway endpoints: one to upload the text and convert it to an audio file and a second one to retrieve the metadata (including the s3 url) for each audio file.&lt;/p&gt;
&lt;p&gt;The POST endpoint does the following:
An API Gateway endpoint invokes the CreateNewFile Lambda function
The CreateNewFile Lambda function adds metadata to a DynamoDB table and then hands the metadata info to the ConvertToAudio Lambda function
The ConvertToAudio Lambda function separates the input text into batches of 2500-ish characters each (this is due to Polly size limits more detail can be found in the Future Improvements section of this post). Then for each batch it calls Polly to convert the text to audio and places the resulting audio stream in the &lt;code&gt;/tmp&lt;/code&gt; directory for temporary storage. Once all batches are processed, the contents of /tmp are uploaded to s3 and the s3 url is added to the dynamoDB table.&lt;/p&gt;
&lt;h2&gt;Resource Permissions&lt;/h2&gt;
&lt;p&gt;Whenever you connect a resource (in this case Lambda functions) to a DynamoDB table or to an s3 bucket in Stackery visual mode, Stackery will grant that resource CRUD access to the target resource (in this case a DynamoDB table or an s3 bucket) since that is what most folks need. I enjoyed that setup for development purposes, but once the application was nearly complete, I only wanted to give my Lambda functions permissions to the actions they needed for security purposes.&lt;/p&gt;
&lt;p&gt;So for example, my GetFile function allowed DynamoDB CRUD, but I just wanted it to have Scan and GetItem permissions to the table. Luckily with Stackery, I could use the template view to customize this beyond the default values. &amp;lt;a href=&quot;https://github.com/deeheber/text-to-speech-converter/blob/blog-post/template.yaml#L59&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&amp;gt;Here’s&amp;lt;/a&amp;gt; what the end result looked like.&lt;/p&gt;
&lt;p&gt;Another thing to note is if you want to give a function access to an entire bucket make sure you tack on &lt;code&gt;/*&lt;/code&gt; to the end of the resource’s arn. It’s relatively simple to tack that onto the end of the resource’s arn via &amp;lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&amp;gt;CloudFormation’s join&amp;lt;/a&amp;gt;. &amp;lt;a href=&quot;https://github.com/deeheber/text-to-speech-converter/blob/blog-post/template.yaml#L104&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&amp;gt;Here’s&amp;lt;/a&amp;gt; an example of what that looks like.&lt;/p&gt;
&lt;p&gt;Finally security wise, since Stackery doesn’t have a built in Polly resource (because Polly is not supported by CloudFormation), I also had to make sure I added permissions from my Lambda function to Polly like &amp;lt;a href=&quot;https://github.com/deeheber/text-to-speech-converter/blob/master/template.yaml#L119&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&amp;gt;this&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;h2&gt;Future Improvements&lt;/h2&gt;
&lt;p&gt;I view every one of my projects as a living breathing entity that is always growing and never fully complete, so here are some improvements I’d like to make as this application evolves. Most improvements are centered around how to better optimize the application in the case where I’d want to convert a massive amount of text (like an entire book) into an audio file.&lt;/p&gt;
&lt;p&gt;Polly’s synthesizeSpeech API has a &amp;lt;a href=&quot;https://docs.aws.amazon.com/polly/latest/dg/limits.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&amp;gt;limit&amp;lt;/a&amp;gt;. For now, I’ve built the app in a way where the input text gets chunked into blobs of about 2,500-ish characters then the program loops through each blob in order to call synthesizeSpeech for each blob to accommodate this limit. I may want to look into using the &amp;lt;a href=&quot;https://docs.aws.amazon.com/polly/latest/dg/asynchronous.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&amp;gt;SpeechSynthesis task API&amp;lt;/a&amp;gt; instead.&lt;/p&gt;
&lt;p&gt;It doesn’t affect smaller jobs or periodic blobs of notes, but if I want to scale this to accept an entire book of text there is a DynamoDB &amp;lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&amp;gt;max item size limit&amp;lt;/a&amp;gt; that might wreak havoc in the way the system is currently set up. I currently have it set to write all of the data including the entire text to be converted to audio to the DynamoDB table, I may want to truncate the amount of text that gets written to the metadata.&lt;/p&gt;
&lt;p&gt;Again much like the Dynamo limit, there are also some &amp;lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/limits.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&amp;gt;Lambda limits&amp;lt;/a&amp;gt; that I may bump up against if I use this program to process an entire book of text. Notably, the storage size of the &lt;code&gt;/tmp&lt;/code&gt; folder and the invocation payload limit since we’re using a Lambda to invoke another Lambda by &amp;lt;a href=&quot;https://github.com/deeheber/text-to-speech-converter/blob/blog-post/src/CreateNewFile/index.js#L45&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&amp;gt;sending over the needed data via the payload&amp;lt;/a&amp;gt;. I may want to re-architect this in a way where we’re utilizing a queue to send that info over from one function to the other or possibly even combine the two functions into one. Not overly sure of a nice solution to the &lt;code&gt;/tmp&lt;/code&gt; storage size issue at this moment, but I’ll get back to you on that.&lt;/p&gt;
&lt;p&gt;Unrelated to scaling to allow for larger file processing, I’d like to build a frontend for this application along with a login/auth. For simplicity sake, I temporarily added the &lt;code&gt;public-read&lt;/code&gt; ACL to the audio files that are uploaded to the FileStore s3 bucket. That would be a serious issue, if we wanted to ensure those files weren’t accessible to the public. For studying purposes, I don’t really mind, so this setup works for this particular use case.
Closing
This was a really fun and useful project, not only with getting the hands on experience exploring an AWS service that is new to me...but also by building something meaningful that I can use to continue to improve myself via studying for my AWS certification exams while on the go.&lt;/p&gt;
&lt;p&gt;The full repository can be found &amp;lt;a href=&quot;https://github.com/deeheber/text-to-speech-converter&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&amp;gt;here&amp;lt;/a&amp;gt;. Hope it helps inspire you to go out and build something that is both fun and meaningful.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Website Refresh With Gatsby</title><link>https://danielleheberling.xyz/blog/website-refresh/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/website-refresh/</guid><description>Website Refresh With Gatsby</description><pubDate>Wed, 13 Mar 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/computer.jpg&quot; alt=&quot;Computer&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Sometimes you just want a change. In this post, I am going to write about my experience rebuilding my website using Gatsby. All of the things that I say in this are from my own experience and it might not match yours. It is my hope that this might help you decide if Gatsby is the right choice for you or if you&apos;re already using Gatsby maybe get some ideas for your site.&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;As a former employee of Squarespace, I get two free comped sites for life which is pretty awesome, considering that those hosting fees do add up quickly. With that said, my old website on Squarespace was great for what it was...but as I learned how to code, I wanted to customize it more and more beyond what any template offered.&lt;/p&gt;
&lt;p&gt;As someone who used to support the Squarespace developer platform, I&apos;m very aware that I could build my own template...but that has its own black holes. A lot of times the core platform CMS would update and things within custom templates built on the Squarespace developer platform would randomly break...I did not want to deal with this.&lt;/p&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;p&gt;I wanted something that was minimalist, accessible, looks great on various screen sizes, customizable, and easy to add more blog. I also wanted full access and ownership of all of the code. This way, I could have the freedom to easily move my entire site elsewhere if I so chose to.&lt;/p&gt;
&lt;h2&gt;Enter Gatsby&lt;/h2&gt;
&lt;p&gt;There is a lot of hype around &amp;lt;a href=&quot;https://www.gatsbyjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Gatsby&amp;lt;/a&amp;gt; in the JavaScript community particularly, folks who use React. I didn&apos;t want to immediately jump on the bandwagon until it was a bit more mature. The more and more positive things that I saw being said about Gatsby on Twitter and viewing awesome Gatsby sites, I knew I had to at least give it a try...I&apos;m very glad I did.&lt;/p&gt;
&lt;h2&gt;Gatsby Features I Think Are Cool&lt;/h2&gt;
&lt;p&gt;If you&apos;re new to React and/or just don&apos;t want to build everything completely from scratch there are many &amp;lt;a href=&quot;https://www.gatsbyjs.org/starters/?v=2&quot; target=&quot;_blank&quot; rel=&quot;noopeener noreferrer&quot;&amp;gt;starter templates&amp;lt;/a&amp;gt; to choose from. These are created by Gatsby employees and members of the community. I think you can also start from scratch though if you want, but am not overly sure.&lt;/p&gt;
&lt;p&gt;Gatsby also has great &amp;lt;a href=&quot;https://www.gatsbyjs.org/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopeener noreferrer&quot;&amp;gt;documentation &amp;lt;/a&amp;gt; and an active &amp;lt;a href=&quot;https://github.com/gatsbyjs/gatsby/issues&quot; target=&quot;_blank&quot; rel=&quot;noopeener noreferrer&quot;&amp;gt;GitHub repository&amp;lt;/a&amp;gt; where you can file an issue to ask for help on something or file a bug.&lt;/p&gt;
&lt;p&gt;I am personally not a huge fan of React router, so I was delighted when I learned that &amp;lt;a href=&quot;https://reach.tech/router&quot; target=&quot;_blank&quot; rel=&quot;noopeener noreferrer&quot;&amp;gt;Reach Router&amp;lt;/a&amp;gt; comes with Gatsby. Major bonus is that it is highly accessible, since Reach Router was created with accessible navigation at its core.&lt;/p&gt;
&lt;p&gt;Gatsby has a great plugin eecosystem. Some of these plugins are maintained by Gatsby while others are maintained by various open source developers. Here are a few that I like...though there are many more to explore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;a href=&quot;https://www.gatsbyjs.org/packages/gatsby-plugin-react-helmet/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;React Helmet &amp;lt;/a&amp;gt;makes it a breeze to enhance the SEO of your website.&lt;/li&gt;
&lt;li&gt;&amp;lt;a href=&quot;https://www.gatsbyjs.org/packages/gatsby-plugin-typography/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Typography &amp;lt;/a&amp;gt;allows easy use of adding a prebuilt typography theme.&lt;/li&gt;
&lt;li&gt;&amp;lt;a href=&quot;https://www.gatsbyjs.org/packages/gatsby-plugin-offline/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Offline &amp;lt;/a&amp;gt;adds a service worker to your site to allow it work offline and when there are bad network connections.&lt;/li&gt;
&lt;li&gt;&amp;lt;a href=&quot;https://www.gatsbyjs.org/packages/gatsby-plugin-feed/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Feed &amp;lt;/a&amp;gt;creats an RSS feed or feeds for your site.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can deploy a Gatsby site nearly anywhere you want. There are plugins to make deploying to an S3 bucket or on Netflify super easy. &amp;lt;a href=&quot;https://www.gatsbyjs.org/docs/deploying-and-hosting/&quot; target=&quot;_blank&quot; rel=&quot;noopeener noreferrer&quot;&amp;gt;The docs&amp;lt;/a&amp;gt; have a list of ideas for hosting if you aren&apos;t sure with instructions on how to deploy to those different platforms.&lt;/p&gt;
&lt;h2&gt;Challenges I ran into&lt;/h2&gt;
&lt;p&gt;This one is mostly a first world problem, but the starter template that I decided to go with used some coding conventions that don&apos;t match my style, so I ended up spending some time doing things like getting rid of dangling commas, adding semicolons etc.&lt;/p&gt;
&lt;p&gt;I think this may possibly have been due to the service worker, but I found that my site was very aggressibely cached. Lots of times when I made a change, it would not show on localhost until I did a hard refresh.&lt;/p&gt;
&lt;p&gt;Not directly Gatsby related, but I attempted to use &amp;lt;a href=&quot;https://www.netlify.com/docs/form-handling/&quot; target=&quot;_blank&quot; rel=&quot;noopeener noreferrer&quot;&amp;gt;Netlify forms&amp;lt;/a&amp;gt;, followed the docs, and still could not get the form the reliably submit. I filed an issue with Gatsby. They were amazing and replied super fast. We eventually decided the issue most likely was something on Netfliy&apos;s side. After more troubleshooting and talking with Netlify&apos;s support, I decided to give up on that since I wanted to ensure my form would reliably submit as I did not want to miss any messages and there are many other options out there to create a form.&lt;/p&gt;
&lt;h2&gt;The Future&lt;/h2&gt;
&lt;p&gt;I view pretty much every project of mine as a living, breathing entity that is never complete. I&apos;m keeping track of ideas of things I&apos;d like to implement in the future on &amp;lt;a href=&quot;https://github.com/deeheber/danielle-heberling-dot-xyz/issues&quot; target=&quot;_blank&quot; rel=&quot;noopeener noreferrer&quot;&amp;gt;GitHub&amp;lt;/a&amp;gt;. Feel free to contribute if you&apos;d like.&lt;/p&gt;
&lt;p&gt;In addition to my issues listed on GitHub, I also think I&apos;d like to deploy the site to an S3 bucket (it is currently hosted on Netlify) to make integration with other AWS services easier. For now, I think thinking I&apos;d like to put that bucket behind a CloudFront distribution that can talk to a Lambda which processes the form and then sends me a customized email with the form submission info via SES and utilizes Cloud Monkey. Ok now I&apos;m just making up fictional AWS services, I&apos;ll stop.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Working with Gatsby has been a joy. The community is so kind and helpful, and the docs are very detailed/searchable. Anytime I had a question 99% of the time, I could find it through a quick search since someone else had already asked the same question I was.&lt;/p&gt;
&lt;p&gt;Gatsby is a great tool that can be used for various things such as a personal blog or a documentation site. In my opinion, it really shines with the various CMS options that you can connect to a Gatsby frontend. This is very helpful if you have people who don&apos;t know how to code or don&apos;t want to code updating or adding new blog blog or other content. They can add their content while the developer can work on customizing how the website or web app looks. I give Gatsby a 10/10...would use again.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Chaos Engineering Ideas For Serverless</title><link>https://danielleheberling.xyz/blog/chaos-engineering-ideas-for-serverless/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/chaos-engineering-ideas-for-serverless/</guid><description>Chaos Engineering Ideas For Serverless</description><pubDate>Thu, 24 Jan 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/chaos.jpg&quot; alt=&quot;Chaos&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The &amp;lt;a href=&quot;http://principlesofchaos.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Principles&amp;lt;/a&amp;gt; define chaos engineering as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The discipline of experimenting on a distributed system in order to build confidence in the system’s capability to withstand turbulent conditions in production.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The high-level steps for implementing chaos experiments involve: defining your application’s steady state, hypothesizing the steady state in both the control and experimental groups, injecting realistic failures, observing the results, and making changes to your code base/infrastructure as necessary based off of the results.&lt;/p&gt;
&lt;p&gt;Chaos experiments are not meant to replace unit and integration tests. They’re intended to work with those existing tests in order to assure the system is reliable. A great real-world analogy is that chaos experiments are like vaccines: A vaccine contains a small amount of the live virus that gets injected into the body in order to prompt the body to build up immunity to prevent illness. With chaos experiments, we’re injecting things like latency and errors into our application to see if the application handles them gracefully. If it does not, then we can adjust accordingly in order to prevent incidents from happening.&lt;/p&gt;
&lt;p&gt;Sometimes chaos engineering gets a bad reputation as “breaking things for fun.” I believe the problem is that there’s too much emphasis on breaking things while the focus should be on why the experiments are being run. In order to minimize your blast radius, it’s recommended to begin with some experiments in non-production environments during the workday while everyone is around to monitor the system. On the people side of things, make sure you communicate with your entire team what you’re doing, so they aren’t caught by surprise. Once you have experience and confidence running experiments, you can then move onto &amp;lt;a href=&quot;https://www.stackery.io/product/deploy/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;running them in production&amp;lt;/a&amp;gt;. The end goal is to run experiments in production since it is difficult to have an environment that matches production exactly.&lt;/p&gt;
&lt;p&gt;Traditionally chaos engineering at a high level is running experiments that often involve shutting off servers, but if you are in a serverless environment with managed servers this can pose a new challenge. Serverless environments typically have smaller units of deployment, but more of them. This means for someone who wants to run chaos experiments, there are more boundaries to harden around in your applications.&lt;/p&gt;
&lt;p&gt;If you’re thinking about running some chaos experiments of your own in a serverless environment, some ideas of things to look out for are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Performance/latency (most common)&lt;/li&gt;
&lt;li&gt;Improperly tuned timeouts&lt;/li&gt;
&lt;li&gt;Missing error handling&lt;/li&gt;
&lt;li&gt;Missing fallbacks&lt;/li&gt;
&lt;li&gt;Missing regional failover (if using multiple regions)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For serverless, the most common experiments involve latency injection or error injection into functions.&lt;/p&gt;
&lt;p&gt;Some examples of errors you could inject are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Errors common in your application&lt;/li&gt;
&lt;li&gt;HTTP 5xx&lt;/li&gt;
&lt;li&gt;Amazon DynamoDB throughput exceeded&lt;/li&gt;
&lt;li&gt;Throttled AWS lambda invocations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A pretty neat trend I’ve seen is folks writing their own libraries to inject latency and/or errors into a Lambda function using the new Lambda layers feature. &amp;lt;a href=&quot;https://docs.stackery.io/docs/api/nodes/Function/#layers&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Stackery makes it easy&amp;lt;/a&amp;gt; to add layers to your function. Another idea is to implement chaos experiments as part of your CI/CD pipeline. If you don’t want to write your own library there are a lot of open source projects and also some companies that offer “chaos-as-a-service.”&lt;/p&gt;
&lt;p&gt;If you’d like to go into more detail on implementation, I’d suggest checking out &amp;lt;a href=&quot;https://medium.com/@adhorn/injecting-chaos-to-aws-lambda-functions-using-lambda-layers-2963f996e0ba&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;this article&amp;lt;/a&amp;gt; to see some code. This &amp;lt;a href=&quot;https://github.com/dastergon/awesome-chaos-engineering&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;GitHub repo&amp;lt;/a&amp;gt; also has some great resources on the overall topic of Chaos Engineering. I hope this post gave you some ideas and inspiration on different ways you can test your serverless environment to ensure system reliability for your customers!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This post was originally published on https://www.stackery.io/&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Why I&apos;m a Software Engineer</title><link>https://danielleheberling.xyz/blog/why-software-engineer/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/why-software-engineer/</guid><description>Why I&apos;m a Software Engineer</description><pubDate>Fri, 21 Dec 2018 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/book.jpeg&quot; alt=&quot;Books&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As a software engineer from a non-traditional background, I’m often asked about how I got here and what motivated me to make a career change…this article will attempt to do a good job of explaining my path. Hope it helps you on your journey — whatever that journey might be.&lt;/p&gt;
&lt;h2&gt;The Early Years&lt;/h2&gt;
&lt;p&gt;Growing up in the early 1990’s the internet was still a pretty new idea to the general public. It would often take minutes for a simple HTML/CSS page to load and many folks didn’t have a PC in their house with access to the internet. Lots of people in my hometown would flock to the local library in order to use the internet. I was lucky enough that my father recognized that the internet was the future and invested in a home PC with dial up internet. I still remember when we first got the computer and he was just so excited to show us the internet for the first time.&lt;/p&gt;
&lt;h2&gt;The Musician Years&lt;/h2&gt;
&lt;p&gt;Fast forward a few years to university as a music education major. As a creative, I primarily surrounded myself with other people in various artistic disciplines. I was generally a more analog person as I saw technology to be robotic, calculated, and lacking the raw human emotion that went into creating good art.&lt;/p&gt;
&lt;p&gt;This was until I had to take a music technology class as a requirement for my degree. In this class, we were taught how to use technologies like Finale, Garage Band, and Band in a Box. In this class, I learned that I could utilize technology to not only create my art, but to make it better. After completing that course, I often found myself researching and buying the next Apple product/upgrading it probably beyond what I actually needed just because I could.&lt;/p&gt;
&lt;h2&gt;The Tech Support Years&lt;/h2&gt;
&lt;p&gt;Fast forward even further. As an adult, I held various jobs, but eventually gravitated toward technical support. This was because I loved learning about new technology, enjoyed empowering/educating others, and didn’t know how to code (yet).&lt;/p&gt;
&lt;p&gt;At this point in my career, I had been promoted enough where a major part of my job was acting as a bridge between engineering and customer support. We would have many cross-departmental meetings that involved activities such as going through open bug reports, discussing the overall customer impact of recent bugs/outages, and talking about common feature requests. Sadly, a lot of the time these conversations would be completely disregarded by the engineering team as if the meeting never happened.&lt;/p&gt;
&lt;p&gt;I knew that my perspective was valuable and deserved to be taken seriously. I wanted the skills and knowledge required to be able to directly act on these requests from customers in order to create an overall better experience. I also wanted to bring human empathy into engineering and to instill a culture of respect for customer support from the engineering department. This is what ultimately led me into the journey of learning how to code.&lt;/p&gt;
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;Through my learning process, I realized that even though coding is very logical with many rules, there’s also an artistic aspect to creating an application since there are always multiple ways to approach and solve the same problem.&lt;/p&gt;
&lt;p&gt;Anyway, shout out to all of those individuals/organizations who have both positively and negatively impacted my journey. It wasn’t easy or short, but I wouldn’t change any of it since I feel it gives me a unique perspective to bring to the table.&lt;/p&gt;
&lt;p&gt;Stay tuned for the next part of my journey when I share about my discovery of the magic that is serverless…&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>Using AWS Amplify To Authenticate Users</title><link>https://danielleheberling.xyz/blog/using-aws-amplify-to-authenticate-users/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/using-aws-amplify-to-authenticate-users/</guid><description>Using AWS Amplify To Authenticate Users</description><pubDate>Wed, 24 Oct 2018 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/amplify.jpg&quot; alt=&quot;Amplify&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So you want to use AWS Cognito to authenticate users and have your user pool, identity pool, and app client all set up in the AWS console. ...the next question is how can you connect this with your React based frontend? While there are a few ways to go about doing this, this post is going to give you a brief overview on how to do this via a library called &amp;lt;a href=&quot;https://aws-amplify.github.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;AWS-Amplify&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;AWS-Amplify is an open source project managed by AWS described as “a declarative JavaScript library for application development using cloud services.” I liked this particular library, because it has a client first approach and abstracts away some of &amp;lt;a href=&quot;https://docs.aws.amazon.com/cognito/latest/developerguide/using-amazon-cognito-user-identity-pools-javascript-examples.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;the setup&amp;lt;/a&amp;gt; required in the JavaScript SDK.&lt;/p&gt;
&lt;p&gt;My favorite features of Amplify are: Authentication (via &amp;lt;a href=&quot;https://aws.amazon.com/cognito/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;Cognito&amp;lt;/a&amp;gt;), API (via API Gateway), and Storage (via S3), but this library has a lot more to offer than just those features. This post will focus on how to authenticate users from a React based frontend...more specifically user signup that has an email address verification step.&lt;/p&gt;
&lt;h4&gt;The Setup&lt;/h4&gt;
&lt;p&gt;First you’ll need to setup a config file to reference your already created AWS resources (in this case the user pool, identity pool, and client id) in your /src folder. The &lt;code&gt;src/config.js&lt;/code&gt; file will look something like this :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default {
   cognito: {
    REGION: ‘YOUR_COGNITO_REGION’,
    USER_POOL_ID: ‘YOUR_USER_POOL_ID’,
    APP_CLIENT_ID: ‘YOUR_APP_CLIENT_ID’,
    IDENTITY_POOL_ID: ‘YOUR_IDENTITY_POOL_ID’
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in your &lt;code&gt;src/index.js&lt;/code&gt; file where you setup your react app, you’ll need to configure aws Amplify. It’ll look similar to this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from ‘react’;
import ReactDOM from ‘react-dom’;
import Amplify from ‘aws-amplify’;

import config from ‘./config’;
import App from ‘./App’;

Amplify.configure({
  Auth: {
    mandatorySignIn: true,
    region: config.cognito.REGION,
    userPoolId: config.cognito.USER_POOL_ID,
    identityPoolId: config.cognito.IDENTITY_POOL_ID,
    userPoolWebClientId: config.cognito.APP_CLIENT_ID
  }
});

ReactDOM.render(
  &amp;lt;Router&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/Router&amp;gt;,
  document.getElementById(‘root’)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;mandatorySignIn&lt;/code&gt; property is optional, but is a good idea if you are using other AWS resources via Amplify and want to enforce user authentication before accessing those resources.&lt;/p&gt;
&lt;p&gt;Also note that for now having a separate config file might seem a bit overkill, but once you add in multiple resources (i.e. Storage, API, Pub Sub etc.) you’ll want that extra config file to keep things easy to manage.&lt;/p&gt;
&lt;h4&gt;Implementation Overview&lt;/h4&gt;
&lt;p&gt;The signup flow will look like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The user submits what they’ll use for login credentials (in this case email and password) via a signup form and a second form to type in a confirmation code will appear.&lt;/li&gt;
&lt;li&gt;Behind the scenes the Amplify library will sign the user up in Cognito.&lt;/li&gt;
&lt;li&gt;Cognito will send a confirmation code email to the user’s signup email address to verify that the email address is real.&lt;/li&gt;
&lt;li&gt;The user will check their email &amp;gt; get the code &amp;gt; type the code into the confirmation form.&lt;/li&gt;
&lt;li&gt;On submit, Amplify will send the information to Cognito which then confirms the signup. On successful confirmation, Amplify will sign the user into the application.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Implementation Part 1&lt;/h4&gt;
&lt;p&gt;First in your signup form component, you’ll need to import Auth from the Amplify library like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;import { Auth } from ‘aws-amplify’;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;As you create your form, I’d suggest using local component state to store the form data. It’ll look like your typical form with the difference being using the Amplify methods in your handleSubmit function whenever the user submits the form. The handleSubmit function will look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; handleSubmit = async event =&amp;gt; {
    event.preventDefault();

    try {
      const newUser = await Auth.signUp({
        username: this.state.email,
        password: this.state.password
      });
      this.setState({
        newUser
      });
    } catch (event) {
      if (event.code === ‘UsernameExistsException’) {
        const tryAgain = await Auth.resendSignUp(this.state.email);
        this.setState({
          newUser: tryAgain
        });
      } else {
        alert(event.message);
      }
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On success, Amplify returns a user object after the signUp method is called, so I’ve decided to store this object in my component local state so the component knows which form to render (the signup or the confirmation).&lt;/p&gt;
&lt;p&gt;Before we continue let’s go over a quick edge case. So if our user refreshes the page when on the confirmation form and then tries to sign up again with the same email address, they’ll receive an error that the user already exists and will need to signup with a different email address. The catch block demonstrates one way of handling that possibility by resending the signup code to the user if that email is already present in Cognito. This will allow the user to continue using the same email address should they refresh the page or leave the site before entering the confirmation code.&lt;/p&gt;
&lt;h4&gt;Implementation Part 2&lt;/h4&gt;
&lt;p&gt;So now the user is looking at the confirmation form and has their confirmation code to type in. We’ll need to render the confirmation form. Similar to the signup form it’ll look like a typical form with the exception being the function that is called whenever the user submits the confirmation form. The handleSubmit function for the confirmation form will look similar to this when using Amplify:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;handleConfirmationSubmit = async (event) =&amp;gt; {
  event.preventDefault()

  try {
    await Auth.confirmSignUp(this.state.email, this.state.confirmationCode)
    await Auth.signIn(this.state.email, this.state.password)

    this.props.isAuthenticated(true)
    this.props.history.push(&quot;/&quot;)
  } catch (event) {
    alert(event.message)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So it is taking in the form data, using Amplify to confirm the user’s email address via the conformation code and signing in the user if successful. You can then verify if a user is signed in via props at the route level if you’d like. In this case, I arbitrarily named it &lt;code&gt;isAuthenticated&lt;/code&gt; and redirected the user to the root path.&lt;/p&gt;
&lt;p&gt;The complete docs for using the Auth feature of Amplify can be &amp;lt;a href=&quot;https://aws-amplify.github.io/docs/js/authentication&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;found here&amp;lt;/a&amp;gt;. We’ve only scratched the surface in this post, so go forth and explore the all of the different features that Amplify has to offer. I’ve found it has a very nice declarative syntax and is very readable for folks who are new to a codebase. For building further on your React-based serverless applications, I highly recommend Stackery for &amp;lt;a href=&quot;https://www.stackery.io/product/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;managing all of your serverless infrastructure&amp;lt;/a&amp;gt; backed up by seamless, git-based version control.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This post was originally published on https://www.stackery.io/&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><author>Danielle Heberling</author></item><item><title>How Being a Musician Has Made Me a Better Software Engineer</title><link>https://danielleheberling.xyz/blog/musician/</link><guid isPermaLink="true">https://danielleheberling.xyz/blog/musician/</guid><description>How Being a Musician Has Made Me a Better Software Engineer</description><pubDate>Mon, 06 Aug 2018 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/headphones.jpeg&quot; alt=&quot;Headphones and Computer&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Oftentimes in society, we like to categorize things in two opposing binaries. Black and white. Dog people and cat people. Creative and logical. Artistic and mathematical. What about the gray area? What if something doesn’t fit neatly into this box or that box and instead loosely fits into both boxes?&lt;/p&gt;
&lt;p&gt;I knew from a relatively young age that I wanted to be a musician. Music has always been a major influence in my life whether that be through my own medium of expression or through the inspiration that I receive from consuming music from other creators. What I didn’t know at that young age was that the skills that I learned as a musician though seemingly unrelated translate very well into other topic areas.&lt;/p&gt;
&lt;p&gt;Since I made a career switch from being a music teacher to being a software engineer, here’s an overview of how the skills I learned as a musician have made me a better software engineer.&lt;/p&gt;
&lt;h2&gt;Teamwork&lt;/h2&gt;
&lt;p&gt;Story time: when I was a music student in university there were two woodwind quintet groups. Group #1 was all of the individuals who were first chair in the entire university for their respective instrument. Group #2 (my group) were individuals that were good acquaintances and all in the same graduating class.&lt;/p&gt;
&lt;p&gt;After a concert that showcased both groups, we all received feedback that group #2 outperformed group #1. The reason…well it wasn’t because each individual in the quintet was the “best” at our respective instrument…it was because we did a great job of collaborating with each other and blending to form a cohesive ensemble.&lt;/p&gt;
&lt;p&gt;I feel this can also translate to a software team. Say for example you have a group of “rockstar” programmers…it doesn’t automatically mean that they as individuals are also great at collaborating or working on a team (disclaimer that some “rockstars” can). The best software teams that I have been on didn’t necessarily have the best individual programmers, but they were group of individuals that had a primary focus of helping each other out with the end goal of making the finished product better accompanied by a curiosity to learn something new.&lt;/p&gt;
&lt;h2&gt;Practice&lt;/h2&gt;
&lt;p&gt;As a musician, I spent about 90% of my time practicing and 10% performing. Even more time was spent making my own oboe reeds…but that’s probably a separate blog post.&lt;/p&gt;
&lt;p&gt;The same can be applied to software development. Currently as a professional software engineer, I spend the bulk of my time researching a new framework or technology, building throwaway apps, and reading documentation. The least amount of time spent is writing code that ends up getting shipped into production&lt;/p&gt;
&lt;h2&gt;Attention to detail&lt;/h2&gt;
&lt;p&gt;In most academic settings a “B” grade is good enough to pass. Different institutions define a “B” differently; however, most places define is as 80–90% correct answers. Now think of that in terms of music…if I played a song with 90% of the notes being accurate…it would sound completely terrible.&lt;/p&gt;
&lt;p&gt;The same principle applies to building software. How many times have you spent 30+ minutes trying to figure out why a linter was yelling at you…only to find you were missing a semi-colon or a closing curly brace? Near perfection is required.&lt;/p&gt;
&lt;h2&gt;Learning a new language&lt;/h2&gt;
&lt;p&gt;When you think of a spoken language, there are always smaller building blocks that come together to build something larger. For example, a letter…becomes a word…becomes a sentence…becomes a paragraph…becomes a novel.&lt;/p&gt;
&lt;p&gt;It’s similar in music. A note becomes a scale…sometimes multiple notes are played at the same time which creates a chord…multiple notes, scales, and chords come together to create a song.&lt;/p&gt;
&lt;p&gt;In software development we have multiple programming languages and most languages have common building blocks that come together to create a script or software product. Those common building blocks include variables, functions, and conditional expressions.&lt;/p&gt;
&lt;h2&gt;Context Switching&lt;/h2&gt;
&lt;p&gt;This next part is my personal opinion. As a musician I felt that the people that were the most fun to perform with were the folks who could just walk into a room and almost instantaneously fit in…both on a musicianship level and on a social skills level. I always enjoyed playing with the people that played multiple instruments and genres of music.&lt;/p&gt;
&lt;p&gt;On the other hand, my favorite software engineers I’ve worked with are very good at reading the room and can adjust how they interact to fit the team’s preferred communication/working style. It has also been helpful (but not mandatory) to work with software engineers that know multiple programming languages and have worked on many different codebases of various sizes.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you’re already a software engineer and reading this; hopefully, you feel inspired to try performing or creating music. Not only is it a lot of fun, it will make you a better software engineer.&lt;/p&gt;
&lt;p&gt;If you’re a musician, in Portland Oregon, and want to dabble with coding there are some free beginner learn how to code workshops on Meetup.&lt;/p&gt;
&lt;p&gt;What are some other parallels that I might have missed? Also feel free to share how skills learned in other disciplines have helped you become a better software engineer and why in the comments.&lt;/p&gt;
</content:encoded><author>Danielle Heberling</author></item></channel></rss>