<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alex</title>
    <description>The latest articles on DEV Community by Alex (@shaftoe).</description>
    <link>https://dev.to/shaftoe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F326304%2F3ea8b976-e1a0-45f1-a8f0-f3c07da709ed.jpeg</url>
      <title>DEV Community: Alex</title>
      <link>https://dev.to/shaftoe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shaftoe"/>
    <language>en</language>
    <item>
      <title>Setup Atom/RSS feed as event source for AWS Lambda invocation with CDK</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Fri, 08 May 2020 14:54:41 +0000</pubDate>
      <link>https://dev.to/shaftoe/setup-atom-rss-feed-as-event-source-for-aws-lambda-invocation-with-cdk-1dko</link>
      <guid>https://dev.to/shaftoe/setup-atom-rss-feed-as-event-source-for-aws-lambda-invocation-with-cdk-1dko</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Now everyone has a license to speak, it’s a question of who gets heard.&lt;/p&gt;

&lt;p&gt;― Aaron Swartz&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Recently I've found myself blogging extensively about my findings when developing simple &lt;em&gt;serverless event-driven applications&lt;/em&gt; with the help of &lt;strong&gt;AWS Cloud Development Kit (CDK)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In a &lt;a href="https://a.l3x.in/2020/02/17/serverless-publish-to-multiple-social-media.html"&gt;previous blog post&lt;/a&gt; for example I shared the outcomes of developing &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.9.0/lib/stacks/publish_to_social/__init__.py"&gt;&lt;code&gt;publish-to-social&lt;/code&gt;&lt;/a&gt;, a simple app that gives me a convenient and extensible way to update (almost all...) my social media feeds whenever there's a new blog post published and ready to be shared.&lt;/p&gt;

&lt;p&gt;In that article I observed that the &lt;strong&gt;100% automation&lt;/strong&gt; goal was not met though and left the completion for the future, hinting at one possible solution like polling the RSS (actually &lt;em&gt;Atom&lt;/em&gt;) feed of this blog using Python &lt;a href="https://pythonhosted.org/feedparser/"&gt;Feedparser library&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filling the gap
&lt;/h2&gt;

&lt;p&gt;As a start I've thrown the &lt;code&gt;feedparser&lt;/code&gt; Python module into the bunch creating a dedicated &lt;em&gt;Lambda Layer&lt;/em&gt; for that. In CDK is &lt;strong&gt;very convenient&lt;/strong&gt; to add a Lambda Function dependency via Lambda Layers: with just a bit of &lt;em&gt;configuration by convention&lt;/em&gt; (keeping all the Layers content under &lt;code&gt;lib/layers/&amp;lt;layer_name&amp;gt;&lt;/code&gt;) all it takes now is &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.9.0/lib/stacks/publish_to_social/__init__.py#L111"&gt;a single line of code&lt;/a&gt; when instantiating a Lambda Function in the CDK stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lambda_poll = get_lambda(
    # ...other_parameters,
    layers=[get_layer(self, "feedparser", id)],
    # ...
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Feedparser&lt;/code&gt; is the main actor in the new &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.9.0/lib/stacks/publish_to_social/lambda/feed_poller.py"&gt;&lt;code&gt;feed_poller&lt;/code&gt;&lt;/a&gt; Lambda function: it takes care of sending the HTTP &lt;em&gt;GET&lt;/em&gt; request to fetch the XML feed content, parse and validate it and finally provide an handy API for extracting useful information such as time sorted blog entries with title, link, timestamps and so on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimizing RSS/Atom feed polling
&lt;/h3&gt;

&lt;p&gt;Both RSS and Atom feeds specification provide bandwidth optimization features, such as &lt;a href="https://pythonhosted.org/feedparser/http-etag.html"&gt;ETag and Last-Modified HTTP headers&lt;/a&gt;, which remove the need to fetch the whole data set at every poll event in case there are no new entries.&lt;/p&gt;

&lt;p&gt;Unfortunately though the current setup of this blog (statically generated by &lt;a href="https://github.com/jekyll/jekyll/releases/tag/v4.0.0"&gt;Jekyll &lt;em&gt;v4&lt;/em&gt;&lt;/a&gt; and hosted by &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt;) does not support any of those out of the box.&lt;/p&gt;

&lt;p&gt;This blog and its feed are low traffic and currently don't require any kind of optimizations so investing effort on saving very little bandwidth would have been pure over engineering (or, more specifically, &lt;em&gt;premature optimization&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Netlify provides &lt;a href="https://docs.netlify.com/routing/headers/"&gt;custom HTTP headers support&lt;/a&gt; though so it should be possible (I'm not sure, I haven't done any research) to update the &lt;a href="https://github.com/jekyll/jekyll-feed"&gt;Jekyll Feed plugin&lt;/a&gt; to better integrate with the current setup and I don't exclude I'll have a look at this at some point just for the fun of it: it's been long since the last time I had the chance to work with Ruby and I always found it a (mostly) pleasant experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  A stateless option
&lt;/h3&gt;

&lt;p&gt;At first I thought there was the need to manage some kind of state to ensure only new posts were considered for publishing. Instead of jumping at any &lt;em&gt;stateful&lt;/em&gt; kind of solution though I thought a bit deeper about the actual requirement and decided that I was more than happy to trade some latency for... statelessness 🥰!&lt;/p&gt;

&lt;p&gt;As already discussed in the previous chapter, currently there's no support for bandwidth-optimization headers anyway (i.e. neither &lt;strong&gt;ETag&lt;/strong&gt; nor &lt;strong&gt;Last-Updated&lt;/strong&gt; data) so no need to store the &lt;em&gt;last updated&lt;/em&gt; timestamp (not for that reason at least).&lt;/p&gt;

&lt;p&gt;Beside that, RSS/Atom feeds are generally meant to be used in an &lt;em&gt;eventually consumed&lt;/em&gt; kind of fashion anyway: I definitely have no need for updating my social feeds in real-time and I'm comfortable with even one day of delay between publishing something new on the blog and notifying the various socials about the new article.&lt;/p&gt;

&lt;p&gt;These specifications help in simplifying the implementation sensibly as you can see from the current workflow summary:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;poll the blog Atom feed once per day (currently at 06:00 UTC)&lt;/li&gt;
&lt;li&gt;check for entries that have publishing timestamp matching &lt;strong&gt;any timestamp from yesterday&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;for each new entry (if any), trigger the &lt;code&gt;publish&lt;/code&gt; Lambda (with a little delay between each invocation to avoid being throttled by the destination API services)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This workflow implies there's no need to keep any state whatsoever, which helps in keeping it as &lt;em&gt;simple and stupid&lt;/em&gt; as possible.&lt;/p&gt;

&lt;p&gt;I also make sure in the CDK stack that &lt;code&gt;feed_poller&lt;/code&gt; Lambda is executed &lt;strong&gt;only once&lt;/strong&gt; (per day) even in case of errors to avoid double postings: the default AWS Lambda behavior is to retry execution two times in case of previous non-zero exit.&lt;/p&gt;

&lt;p&gt;This is not meant to be a &lt;em&gt;mission critical&lt;/em&gt; service anyway so, in the unlikely case of errors, the "Lambda errors" email report I receive daily should be more than enough to let me deal with them.&lt;/p&gt;

&lt;p&gt;For the records, the above automation workflow has been in place for the last few weeks and it did work as expected when publishing the &lt;a href="https://a.l3x.in/2020/04/27/fix-router-with-raspberry-node-puppeteer.html"&gt;previous blog post&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;Python &lt;code&gt;datetime&lt;/code&gt; module (included in the &lt;em&gt;Python Standard Library&lt;/em&gt;) comes handy when working with timestamps and time deltas, it should also deal properly with those nasty edge cases that can surface during &lt;strong&gt;leap seconds/days/years&lt;/strong&gt; (many thanks to Gregory for ridding us of Leap &lt;strong&gt;Months&lt;/strong&gt; at least 😆):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from datetime import (datetime, timedelta)

def _midnightify(date: datetime) -&amp;gt; datetime:
    """Return midnightified datetime."""
    return date.replace(hour=0, minute=0, second=0, microsecond=0)

now = datetime.utcnow()
yesterday = now - timedelta(days=1)

today_noon = _midnightify(now)
yesterday_noon = _midnightify(yesterday)

new_posts = [entry.link for entry in entries  # entries: Feedparser iterable object
             if yesterday_noon &amp;lt;= entry.published_datetime &amp;lt; today_noon]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The above snippet is just a redacted excerpt that's supposed to give you the general idea, please refer to &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.9.0/lib/stacks/publish_to_social/lambda/feed_poller.py"&gt;the actual implementation&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;To close the &lt;strong&gt;100% automation&lt;/strong&gt; loop there's now a new &lt;em&gt;CloudWatch Cron Event&lt;/em&gt; that fires every day at 06:00 UTC and triggers &lt;code&gt;feed_poller&lt;/code&gt; Lambda, so this is the updated &lt;code&gt;publish-to-social&lt;/code&gt; architecture diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aa0gZ6th--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://a.l3x.in/img/publish-to-social-final.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aa0gZ6th--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://a.l3x.in/img/publish-to-social-final.svg" alt="Architecture Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback welcome
&lt;/h2&gt;

&lt;p&gt;Please feel free to tell me what you think leaving a comment here or contacting me directly (various options on the &lt;a href="https://a.l3x.in/contact.html"&gt;Contact page&lt;/a&gt;). And, of course, don't forget to subscribe to &lt;a href="https://a.l3x.in/feed.xml"&gt;this blog feed&lt;/a&gt; 😜&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://a.l3x.in/"&gt;Alexander Fortin's tech blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>serverless</category>
      <category>python</category>
    </item>
    <item>
      <title>Automate router WiFi bridge setup with Raspberry Pi, Node.js, Puppeteer and Ansible</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Tue, 28 Apr 2020 06:00:15 +0000</pubDate>
      <link>https://dev.to/shaftoe/automate-router-wifi-bridge-setup-with-raspberry-pi-node-js-puppeteer-and-ansible-40ed</link>
      <guid>https://dev.to/shaftoe/automate-router-wifi-bridge-setup-with-raspberry-pi-node-js-puppeteer-and-ansible-40ed</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://a.l3x.in/"&gt;Alexander Fortin's tech blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One does not accumulate but eliminate. It is not daily increase but daily decrease. The height of cultivation always runs to simplicity.&lt;/p&gt;

&lt;p&gt;― Bruce Lee&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've been a frequent traveller for the last few years and I learnt along the way that reducing the clutter that comes with me to the bare minimum is a &lt;em&gt;very good trend&lt;/em&gt; to follow.&lt;/p&gt;

&lt;p&gt;For example I drastically reduced the amount of clothes that I own to the point they all fit into a cabin luggage, and I'm generally mindful when I buy &lt;strong&gt;any&lt;/strong&gt; new object because I know that either has to fit in my luggages or has to be sold/gifted/trashed next time I'll relocate.&lt;/p&gt;

&lt;p&gt;I confess though that the geeky side of me still requires substantial room in the non-cabin luggage for the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an old MacBook Air dated fall of 2013 (if I'm not wrong)&lt;/li&gt;
&lt;li&gt;Sennheiser Bluetooth headphones with noise cancellation (which of course I take to the cabin when boarding...)&lt;/li&gt;
&lt;li&gt;Sony PlayStation 3 &lt;em&gt;Super Slim&lt;/em&gt; with one gamepad and a few original DVD games&lt;/li&gt;
&lt;li&gt;Raspberry Pi (armv6l)&lt;/li&gt;
&lt;li&gt;1TB USB3 external 2.5" hard drive&lt;/li&gt;
&lt;li&gt;various Ethernet/USB/HDMI/miniJACK cables and plugs&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;unbranded ADSL/4G/WiFi router, which runs a very crappy administrative software but once it's configured it does its job pretty well and comes with a few built-in handy features, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4g/LTE connectivity&lt;/li&gt;
&lt;li&gt;WiFi-to-WiFi bridging&lt;/li&gt;
&lt;li&gt;persistent DHCP configurable table mapping (MAC &amp;lt;-&amp;gt; IP)&lt;/li&gt;
&lt;li&gt;4-ports Ethernet switch&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Nomad multimedia lounge setup
&lt;/h2&gt;

&lt;p&gt;With the above items I'm able to very quickly setup a comfortable yet powerful gaming/media environment whenever I move into a new place where there's some connectivity (at least some mobile network coverage) and/or a TV color.&lt;/p&gt;

&lt;p&gt;The only thing I have to do once settled in the new place is to bring power to router + Ps3 + Raspberry Pi, update the router configuration to connect in "bridge mode" to the available WiFi (or buy a SIM card with a LTE data plan if WiFi is not an option), plug both the Raspberry Pi (with the USB drive attached) and the PlayStation to the router switch with Ethernet cables, PlayStation to the TV with (hopefully) HDMI cable and... voilà!&lt;/p&gt;

&lt;p&gt;Everything is connected to the Internet and ready to serve its purpose, including streaming media (Netflix/Amazon Prime/YouTube) and music (Spotify) directly from the relative PlayStation apps. For all that's not available online I make use of &lt;a href="https://serviio.org/"&gt;Serviio media server&lt;/a&gt;, which lets me consume from the Playstation (via &lt;code&gt;DLNA&lt;/code&gt; protocol) the audio/video/photo media files stored in the external USB hard drive.&lt;/p&gt;

&lt;p&gt;Another side benefit I get with this setup is that with a single shot all my portable devices (MacBook, iPhone and Kindle) get Internet connectivity too: the router WiFi password is already saved and there's no need to configure yet another WiFi and type the relative password for each device.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;One very annoying problem I face with this setup is that every single time there's a power outage and the router reboots, or for whatever other reason the WiFi bridge is broken, the router doesn't automatically reconnect to the source WiFi.&lt;/p&gt;

&lt;p&gt;It might sound like something infrequent to you if you're used to live in developed countries but for example in east Africa, where I've been living the last few years, brief power outages are quite common even in the main urban areas.&lt;/p&gt;

&lt;p&gt;In order to restore the bridge I have to manually log into the router admin interface from my laptop's browser, find the right admin web page from the unintuitive navigation bar, perform a few selects and clicks, retype the source WiFi password and, finally, click to apply and persist the configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UrxZHuca--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/v1/%7B%7B%20%22/img/router-fix.gif%22%20%7D%7D" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UrxZHuca--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/v1/%7B%7B%20%22/img/router-fix.gif%22%20%7D%7D" alt="screencast of the router wifi bridge reset process"&gt;&lt;/a&gt;{: .img-fluid .col}&lt;/p&gt;

&lt;p&gt;The above could be classified as a &lt;em&gt;first-world problem&lt;/em&gt; indeed but, as you might already know if you've been following me for a while, I'm a long time advocate of the &lt;strong&gt;lazy software engineer attitude&lt;/strong&gt; (our motto? life is short!): automate all the tedious and boring tasks so to be able to focus on the most important ones, those that are not so easy to automate and where I believe our true value as engineers is.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;I was initially tempted to approach this using a tool I already know and use for some other projects (&lt;a href="https://www.cypress.io/"&gt;Cypress&lt;/a&gt;). Recently though I've been reading good things about &lt;a href="https://pptr.dev/"&gt;Google's Puppeteer&lt;/a&gt; so I decided to take this chance to see what all the fuss is about.&lt;/p&gt;

&lt;p&gt;This is the tech stack needed to implement the solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a relatively old Raspberry Pi mounting Raspbian 10 (Buster)&lt;/li&gt;
&lt;li&gt;a Node.js runtime (in my case &lt;em&gt;v11.15.0&lt;/em&gt; which is the last available version &lt;a href="https://nodejs.org/dist/latest-v11.x/"&gt;officially distributed&lt;/a&gt; as binary that supports &lt;code&gt;armv6l&lt;/code&gt; architecture)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;puppeteer-core&lt;/code&gt; &lt;em&gt;v3.0.0&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Chromium browser &lt;em&gt;v78&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Ansible &lt;em&gt;v2.9.6&lt;/em&gt; (not strictly needed but that's how I drop things into my Raspberry Pi by default anyway)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/shaftoe/router-wifi-bridge-reset/blob/0.1.0/fix-router.js"&gt;These 80 lines&lt;/a&gt; of Node.js automate the above process thanks to the awesome &lt;a href="https://pptr.dev/#?show=api-puppeteer-vs-puppeteer-core"&gt;&lt;code&gt;puppeteer-core&lt;/code&gt;&lt;/a&gt; library. It runs both on MacOS and Linux:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS: tested on my MacBook Air running Node.js &lt;em&gt;v13.13.0&lt;/em&gt; and &lt;code&gt;puppeteer-core&lt;/code&gt; &lt;em&gt;v3.0.0&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Linux: tested on my Raspberry Pi &lt;code&gt;armv6l&lt;/code&gt;, Node.js &lt;em&gt;v11.15.0&lt;/em&gt; and &lt;code&gt;puppeteer-core&lt;/code&gt; &lt;em&gt;v3.0.0&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To complete the setup, I add this simple cronjob that verifies connectivity every 2 minutes and, in case of failure, runs the Node.js script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*/2 * * * * ping -nq -c 3 1.1.1.1 | grep -q " 100\% packet loss" &amp;amp;&amp;amp; node /opt/routerfix/repo/fix-router.js 2&amp;gt;&amp;amp;1 | logger -t ROUTER_FIX
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I split the above one liner command to better explain how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;*/2 * * * *&lt;/code&gt; tells &lt;code&gt;CRON&lt;/code&gt; daemon to run the command every 2 minutes, it could have been run at 1 minute frequency but the Node.js script takes around 40 seconds to complete execution on my Raspberry Pi so this is meant as a safeguard to avoid running two processes at the same time. The script also commits suicide 90 seconds after execution anyway (refer to the &lt;code&gt;terminate&lt;/code&gt; function &lt;a href="https://github.com/shaftoe/router-wifi-bridge-reset/blob/0.1.0/fix-router.js#L17"&gt;at line &lt;code&gt;17&lt;/code&gt;&lt;/a&gt; for more details) so this race condition should never happen&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ping -nq -c 3 1.1.1.1&lt;/code&gt; sends 3 ICMP ECHO packets to a highly available IP destination (Cloudflare public DNS service), and in case these packets are all lost it will output to stdout something like "3 packets transmitted, 3 packets lost, 100% packet loss"&lt;/li&gt;
&lt;li&gt;output from &lt;code&gt;ping&lt;/code&gt; is piped to &lt;code&gt;grep&lt;/code&gt;, the &lt;code&gt;-q&lt;/code&gt; option suppress output and just returns 0 or non-zero exit code based on the string match. This means that the command after &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; will be executed if and only if all the 3 ICMP packets are lost&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node /opt/routerfix/repo/fix-router.js 2&amp;gt;&amp;amp;1&lt;/code&gt; executes the Node.js script which runs Chromium browser events in the background, it also combines stdin and stdout into a single stream (the &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt; bit)&lt;/li&gt;
&lt;li&gt;by default, crontab commands output is sent to the &lt;em&gt;UNIX&lt;/em&gt; user mailbox that owns the process; piping Node.js output to &lt;code&gt;logger -t ROUTER_FIX&lt;/code&gt; sends it to the &lt;em&gt;syslog&lt;/em&gt; service instead (which in my case is then eventually forwarded to &lt;a href="https://www.papertrail.com/"&gt;Papertrail&lt;/a&gt; where I can read them comfortably from my browser as you can see from the following screenshot)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yU3Sv7Qw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/%7B%7B%20%22/img/router-fix-papertrail.png%22%20%7D%7D" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yU3Sv7Qw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/%7B%7B%20%22/img/router-fix-papertrail.png%22%20%7D%7D" alt="Papertrail log excerpt"&gt;&lt;/a&gt;{: .img-fluid .col}&lt;/p&gt;

&lt;p&gt;You can find all the code involved in the solution in &lt;a href="https://github.com/shaftoe/router-wifi-bridge-reset/"&gt;this GitHub repository&lt;/a&gt; including the &lt;a href="https://github.com/shaftoe/router-wifi-bridge-reset/blob/0.1.0/ansible-tasks.yml"&gt;Ansible playbook&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pitfalls and Caveats
&lt;/h2&gt;

&lt;p&gt;You know when they say you never truly know anyone? that applies to software too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;after ages of CRON usage I just discovered that the percentage char (&lt;code&gt;%&lt;/code&gt;) has a special meaning in a crontab so needs to be properly escaped with a backslash char (&lt;code&gt;\&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;in this pitfall I fell so many times that I think it's always a good idea to refresh our memories: CRON's &lt;code&gt;PATH&lt;/code&gt; environment variable is usually not the same one of the default UNIX shell, so remember to either provide full path for each command or explicitly set it in the crontab (I opted for the latter solution as you can see &lt;a href="https://github.com/shaftoe/router-wifi-bridge-reset/blob/0.1.0/ansible-tasks.yml#L58"&gt;at line 58&lt;/a&gt; of the Ansible playbook)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;About Puppeteer, I barely scratched the surface of what the APIs offer so I can't say much about it beside that's well documented and easy to get started with. If you are already familiar with JavaScript and its callback-driven nature, Puppeteer should pose no real issue and you might be up and running in minutes.&lt;/p&gt;

&lt;p&gt;Personally I find Cypress' promises-inspired style more elegant: chaining promises commands in one or more sequences is usually the way I use to think about interacting programmatically with the browser. As far as I understand they are targeting different users and use cases though so, as is usually the case, choose the right tool for the task, they're both solid projects anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The above automation has been active for the last few days and it's working like a charm, to the point I actually notice there's been an outage only when the microwave clock gets zeroed. I hope this simple example might help you in solving some more complex issue you're facing or give you some more ideas on how to approach it.&lt;/p&gt;

&lt;p&gt;As usual, a reminder to not forget to drop a comment here below... and keep in mind the most important of things: life is short! ⚡&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>javascript</category>
      <category>node</category>
      <category>puppeteer</category>
    </item>
    <item>
      <title>An AWS Lambda execution report webpage built with Lambda Destinations, CDK and Svelte</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Thu, 27 Feb 2020 12:18:12 +0000</pubDate>
      <link>https://dev.to/shaftoe/an-aws-lambda-execution-report-webpage-built-with-lambda-destinations-cdk-and-svelte-114f</link>
      <guid>https://dev.to/shaftoe/an-aws-lambda-execution-report-webpage-built-with-lambda-destinations-cdk-and-svelte-114f</guid>
      <description>&lt;p&gt;Liquid syntax error: Variable '{{%20" alt="Architecture Diagram"&amp;gt;
&lt;/p&gt;
&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;For the frontend side of things I decided to just add a new page to this very &lt;a href="https://jekyllrb.com/" rel="noopener noreferrer"&gt;Jekyll&lt;/a&gt; blog and have it publicly available &lt;a href="https://a.l3x.in/social_report.html" rel="noopener noreferrer"&gt;at this URL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a side note, I already have plans to migrate the blog from Jekyll to &lt;a href="https://gohugo.io/" rel="noopener noreferrer"&gt;Hugo&lt;/a&gt; so I didn't invest too much on the page layout design at this time.&lt;/p&gt;

&lt;p&gt;For the dynamic part I could have added some &lt;a href="https://jquery.com/" rel="noopener noreferrer"&gt;good old &lt;em&gt;jQuery&lt;/em&gt;&lt;/a&gt; given that it's already been used for the &lt;a href="https://a.l3x.in/contact.html" rel="noopener noreferrer"&gt;{% raw %}' was not properly terminated with regexp: /\}\}/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>lambda</category>
      <category>svelte</category>
    </item>
    <item>
      <title>Setup AWS Lambda-to-Lambda invocations between CDK Stacks</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Thu, 27 Feb 2020 07:56:52 +0000</pubDate>
      <link>https://dev.to/shaftoe/setup-aws-lambda-to-lambda-invocations-between-cdk-stacks-1i6m</link>
      <guid>https://dev.to/shaftoe/setup-aws-lambda-to-lambda-invocations-between-cdk-stacks-1i6m</guid>
      <description>&lt;p&gt;Liquid syntax error: Variable '{{' was not properly terminated with regexp: /\}\}/&lt;/p&gt;
</description>
      <category>aws</category>
      <category>cdk</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Automate social media status updates with AWS Lambda, SNS and CDK</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Mon, 17 Feb 2020 09:41:31 +0000</pubDate>
      <link>https://dev.to/shaftoe/automate-social-media-status-updates-with-aws-lambda-sns-and-cdk-49l0</link>
      <guid>https://dev.to/shaftoe/automate-social-media-status-updates-with-aws-lambda-sns-and-cdk-49l0</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://a.l3x.in/"&gt;Alexander Fortin's tech blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As some of you have noticed, recently I've been blessed with some extra time and decided to take &lt;a href="https://a.l3x.in/2020/01/29/my-quest-for-identity-in-software-engineering.html"&gt;the chance&lt;/a&gt; to finally &lt;a href="https://a.l3x.in/2020/01/31/updating-curriculum-with-web-tech.html"&gt;come up&lt;/a&gt; with some fresh &lt;a href="https://a.l3x.in/2020/02/04/migrating-from-terraform-to-cdk.html"&gt;new content&lt;/a&gt; for this blog.&lt;/p&gt;

&lt;p&gt;How to choose a new pet project in the IT world where the choice of things to be learnt and practiced to stay relevant is simply overwhelming?&lt;/p&gt;

&lt;p&gt;With a &lt;em&gt;green field&lt;/em&gt; pet project I generally move from fresh itches that I'd like to scratch because, well... there's never a shortage of them, no matter how serious you are in trying to automate them all away.&lt;/p&gt;

&lt;p&gt;I try then to come up with a feasible solution that won't need any wheel to be reinvented, spend too much money to keep it up and running or require too much time and effort to actually make sense.&lt;/p&gt;

&lt;p&gt;Once the goal and its scope are defined I select the tech stack but, instead of being strict in choosing the right tool for the job (as I'd do while working under contract), I let my curiosity for the new interesting tech lead at least some of my choices.&lt;/p&gt;

&lt;p&gt;To be clear, I don't want to jump on the &lt;em&gt;first buzz&lt;/em&gt; at hand, I still try to pick the best tools for the job but not being yet confident with those tools I can only make educated guesses.&lt;/p&gt;

&lt;p&gt;Beside the curiosity for leveraging abstractions writing CDK code as mentioned in the &lt;a href="https://a.l3x.in/2020/02/04/migrating-from-terraform-to-cdk.html#general-reception"&gt;previous article&lt;/a&gt;, it's been a while that I wanted to better understand the &lt;a href="https://aaronparecki.com/oauth-2-simplified/"&gt;OAuth&lt;/a&gt; flows that today are very common for authenticate/authorize public APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's scratch
&lt;/h2&gt;

&lt;p&gt;Especially now that I'm publishing content more frequently (and planning to keep doing so) I want to inform the ones who follow me when there's a new article available, otherwise what's the point of producing any content?&lt;/p&gt;

&lt;p&gt;In time though the social media accounts and (re)publishing platforms proliferated almost out of control. &lt;a href="https://eev.ee/blog/2020/02/01/old-css-new-css/"&gt;The good old days&lt;/a&gt; when basically everybody in the Internet owned a personal website (&lt;a href="https://gizmodo.com/remember-the-hilarious-horror-of-geocities-with-this-we-5983574"&gt;GeoCities&lt;/a&gt; anyone?) are long gone and apparently for good.&lt;/p&gt;

&lt;p&gt;To be more specific, here is a list of my current digital presence so far:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://a.l3x.in/"&gt;this blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosstodon.org/@alex"&gt;Mastodon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/alexanderfortin"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaftoe"&gt;Dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/alexanderfortin/"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/submitted?id=alexfortin"&gt;Hacker news&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/user/AlexanderFortin"&gt;Reddit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@a_70064"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... and no need to mention how the Instant Messaging situation is even worse, right?&lt;/p&gt;

&lt;p&gt;I suspect I'm forgetting one or two but you get the point: they are already too many to even remember without browser bookmarks hints and it's safe to say the list in time is likely going to grow.&lt;/p&gt;

&lt;p&gt;This means that every time I publish a new article, unless I'm relying only on the &lt;a href="https://a.l3x.in/feed.xml"&gt;RSS feed&lt;/a&gt; to spread the news, for each of the above accounts I should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;open a new tab in the browser&lt;/li&gt;
&lt;li&gt;perform a &lt;em&gt;log in&lt;/em&gt; (with two-factor authentication of course...)&lt;/li&gt;
&lt;li&gt;pass the occasional &lt;a href="https://www.youtube.com/watch?v=Umc9ezAyJv0"&gt;Voight-Kampff&lt;/a&gt; &lt;em&gt;recaptcha&lt;/em&gt; test&lt;/li&gt;
&lt;li&gt;finally, &lt;em&gt;copy&amp;amp;paste&lt;/em&gt; in a &lt;em&gt;textarea&lt;/em&gt; some text that might look like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;New blog post:
&lt;span class="o"&gt;[&lt;/span&gt;a brief description of the content...]
https://link.to.new.article
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;a snippet that will need slightly different content and formatting depending on the targeted platform (e.g. in Twitter prefer &lt;em&gt;title&lt;/em&gt; over &lt;em&gt;description&lt;/em&gt; to fit into the 140 characters limit, etc)&lt;/p&gt;

&lt;p&gt;I know there are multiple professional services like &lt;a href="https://hootsuite.com/"&gt;Hootsuite&lt;/a&gt; that provide this kind of feature (and many more) but, unless you're willing to pay for the premium service, usually they put strict limitations on how many integrations you can use, how many posts per month, and so on.&lt;/p&gt;

&lt;p&gt;Actually I'm even ok paying a monthly fee for something I deem valuable but for such a simple use case a fully featured &lt;em&gt;SaaS&lt;/em&gt; is definitely overkill. On top of that I'm even happier if I can avoid sharing any sensitive data like secret tokens with any 3rd party service.&lt;/p&gt;

&lt;p&gt;As you already have guessed from the spoilering title, this looks like a &lt;em&gt;perfect use case&lt;/em&gt; for a serverless event-driven application on AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tech stack
&lt;/h2&gt;

&lt;p&gt;For the case I'm presenting here, the choice fell on the following technology stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Lambda + SNS to build the infrastructure&lt;/li&gt;
&lt;li&gt;AWS CDK to model the infra plus create/update and wire together its building blocks&lt;/li&gt;
&lt;li&gt;Python 3.8 + &lt;em&gt;Boto3&lt;/em&gt;, &lt;a href="https://requests-oauthlib.readthedocs.io/"&gt;&lt;em&gt;requests_oauthlib&lt;/em&gt;&lt;/a&gt; and &lt;a href="http://www.crummy.com/software/BeautifulSoup/bs4/"&gt;&lt;em&gt;BeautifulSoup4&lt;/em&gt;&lt;/a&gt; 3rd party libraries as runtime for the application&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's the story
&lt;/h2&gt;

&lt;p&gt;The hypothetical &lt;em&gt;user story&lt;/em&gt; for this project might have looked something like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a blog writer, I want to automatically update all my social feeds with a link to the new article as soon as it's published.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and here the features request:&lt;/p&gt;

&lt;h3&gt;
  
  
  hard requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;fully managed infrastructure, near-zero maintenance needed&lt;/li&gt;
&lt;li&gt;cheap to the point of being free (until I learn how to publish new posts at sub-second frequency. Just kidding 😁)&lt;/li&gt;
&lt;li&gt;simple to manage&lt;/li&gt;
&lt;li&gt;easy to add new output services for different social medias&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  soft requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;have the whole process 100% automated&lt;/li&gt;
&lt;li&gt;delivery to all the social networks mentioned above&lt;/li&gt;
&lt;li&gt;highly available&lt;/li&gt;
&lt;li&gt;fun to build&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Show me the code
&lt;/h2&gt;

&lt;p&gt;This time instead of creating a dedicated repository with some example code I decided to clean up the actual codebase I was already using to deploy my personal APIs and make &lt;a href="https://github.com/shaftoe/api-l3x-in/tree/0.3.0"&gt;the repository public&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, speaking of abstractions, I added to the main CDK app (&lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/cdk.py"&gt;&lt;code&gt;lib/cdk.py&lt;/code&gt;&lt;/a&gt;) two more stacks that join the previous single one, &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/stacks/api/__init__.py"&gt;&lt;code&gt;api&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first new stack is called &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/stacks/lambda_layers/"&gt;&lt;code&gt;lambda-layers&lt;/code&gt;&lt;/a&gt; and its only reason to exist is to keep the Lambda Layers isolated from the other stacks unless those stacks need one or more Layer as dependency: in that case &lt;code&gt;stacks.lambda-layers.layers&lt;/code&gt; object will be passed to the needing stack as parameter, making that part of the code DRY and pluggable as needed (more on this later).&lt;/p&gt;

&lt;p&gt;If you don't know already Lambda Layers, it's a powerful AWS Lambda feature that lets you provide additional code to your Lambda runtime, removing for example the need for bundling external libraries together with your actual Lambda code. You can read more about how Lambda Layers work &lt;a href="https://read.iopipe.com/cutting-through-the-layers-aws-lamba-layers-explained-28e8a8d7bda8"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Speaking of CDK, I've restructured a bit the codebase compared to the one you find in the &lt;a href="https://github.com/shaftoe/api-gateway-lambda-cdk-example"&gt;mini-tutorial&lt;/a&gt; from the &lt;a href="https://a.l3x.in/2020/02/04/migrating-from-terraform-to-cdk.html"&gt;previous article&lt;/a&gt;. Instead of the default scaffolding provided by &lt;code&gt;cdk init&lt;/code&gt; I preferred a files and folders structure that I find easier to understand and reason about. There are now no redundant folders and everything that relates directly to CDK is either in a &lt;code&gt;cdk.py&lt;/code&gt; file or in &lt;code&gt;stacks/&amp;lt;stack_name&amp;gt;/__init__.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another improvement to the DRYness is that now default values and helpers for CDK live in the &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/utils/cdk.py"&gt;&lt;code&gt;utils.cdk&lt;/code&gt; library&lt;/a&gt; that's shared by all the stacks.&lt;/p&gt;

&lt;p&gt;The actual code for each individual application (&lt;code&gt;api&lt;/code&gt;, &lt;code&gt;publish-to-social&lt;/code&gt;, &lt;code&gt;lambda-layers&lt;/code&gt;) can be found under &lt;code&gt;stacks/&amp;lt;stack_name&amp;gt;&lt;/code&gt; &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/stacks/"&gt;folder&lt;/a&gt;, packaged according to the specific application needs.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;api-l3x-in&lt;/code&gt; project is basically a container for different applications, the new one I'm going to focus on now is &lt;code&gt;publish-to-social&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  publish-to-social
&lt;/h2&gt;

&lt;p&gt;Here a diagram that shows how the building blocks of &lt;code&gt;publish-to-social&lt;/code&gt; are connected:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://a.l3x.in/img/publish-to-social.svg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K53J99aV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/%7B%7B%20%22/img/publish-to-social.svg%22%20%7D%7D" alt="Architecture Diagram"&gt;{: .img-fluid }&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a simple and fairly typical &lt;em&gt;pub-sub&lt;/em&gt; (and stateless) &lt;em&gt;serverless&lt;/em&gt; application with one main Lambda &lt;em&gt;publisher&lt;/em&gt; that sends messages to an SNS topic and multiple Lambda &lt;em&gt;subscribers&lt;/em&gt; that are executed every time there's a new message in the topic.&lt;/p&gt;

&lt;p&gt;One of the main benefits of decoupling services communication via a messaging bus like SNS is that publishers can be oblivious of subscribers existence, hence we can add as many Lambda subscribers as we need without making any change to the publisher(s) code (another main benefit is &lt;em&gt;increased scalability&lt;/em&gt; which is obviously not a requirement in this case, I just mention for the sake of completeness).&lt;/p&gt;

&lt;p&gt;I started with the idea of having a small prototype to share but somehow I quickly came up with what I think is a decent solution and something that I might want to maintain for a while, so I'll soon freeze the API at v1.0.0 after adding some unit tests (I know, I know...) and maybe a couple of more features and/or support for other social services.&lt;/p&gt;

&lt;p&gt;Speaking of services, a few adapters for the social networks mentioned above are already implemented, you can find them all in the &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/stacks/publish_to_social/lambda/services/"&gt;&lt;code&gt;publish-to-social.lambda.services&lt;/code&gt;&lt;/a&gt; module with links to APIs documentation. All the heavy lifting for logging, handling exceptions, parsing events and implementing proper response interface are shared by all the lambdas so the code needed for each service is very small, you can have a look at &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/utils/handlers.py"&gt;&lt;code&gt;utils.handlers&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/utils/helpers.py"&gt;&lt;code&gt;utils.helpers&lt;/code&gt;&lt;/a&gt; modules for more details.&lt;/p&gt;

&lt;p&gt;Probably so far the most interesting implemented one is &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/stacks/publish_to_social/lambda/services/devto.py"&gt;&lt;code&gt;devto&lt;/code&gt;&lt;/a&gt;. It's slightly more complex compared to the others given that instead of a simple text message it publishes the whole article (with the original link set as &lt;em&gt;canonical url&lt;/em&gt;); this is how the flow looks like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/stacks/publish_to_social/lambda/publish_to_social.py"&gt;&lt;code&gt;publish-to-social&lt;/code&gt; main lambda&lt;/a&gt; is triggered via CLI, then:

&lt;ol&gt;
&lt;li&gt;scrapes the URL given as input in search for content&lt;/li&gt;
&lt;li&gt;if point &lt;code&gt;1.1&lt;/code&gt; is successful, publishes a message to the SNS topic&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/stacks/publish_to_social/lambda/services/devto.py"&gt;&lt;code&gt;devto&lt;/code&gt; lambda&lt;/a&gt; is triggered (simultaneously with all the other services) by the SNS subscription, then:

&lt;ol&gt;
&lt;li&gt;authenticates to &lt;a href="https://developer.github.com/v3/"&gt;GitHub APIs&lt;/a&gt; and fetch some Markdown content from my private &lt;em&gt;blog&lt;/em&gt; repository&lt;/li&gt;
&lt;li&gt;finally, publishes the article using &lt;em&gt;dev.to/articles/&lt;/em&gt; endpoint&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  User Interface
&lt;/h3&gt;

&lt;p&gt;The soft requirement of &lt;em&gt;100% automation&lt;/em&gt; is not met yet, ideally the process should be initiated by some kind of &lt;em&gt;cronjob&lt;/em&gt; action using the &lt;a href="https://a.l3x.in/feed.xml"&gt;blog RSS feed&lt;/a&gt; as source for new articles data. It should be fairly easy to implement but I felt there's already enough beef to share right now.&lt;/p&gt;

&lt;p&gt;Currently to publish a new article link on (some of) my feeds I just need to trigger a Lambda execution from the terminal (I'm actually using a wrapper script to just provide the url as argument, here I show the internals):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws &lt;span class="nt"&gt;--profile&lt;/span&gt; api-l3x-in &lt;span class="se"&gt;\&lt;/span&gt;
    lambda invoke &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--function-name&lt;/span&gt; publish-to-social-publishtosocial17FB96F7-LXWRTUFARTKU &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--invocation-type&lt;/span&gt; RequestResponse  &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--payload&lt;/span&gt; &lt;span class="s1"&gt;'{ "url": "https://a.l3x.in/link/to/the/article.html" }'&lt;/span&gt;

&lt;span class="c"&gt;# Response that implements Api Gateway interface&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"isBase64Encoded"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
&lt;span class="s2"&gt;"headers"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Access-Control-Allow-Origin"&lt;/span&gt;: &lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="s2"&gt;"body"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"message"&lt;/span&gt;: &lt;span class="s2"&gt;"messageId '5ad1368b-fda5-5c89-8b9e-604bad6433f9' with content scraped from source https://a.l3x.in/link/to/the/article.html delivered successfully"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="s2"&gt;"statusCode"&lt;/span&gt;: 200&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For now I'm ok with the UX bit and I don't have to deal with any (inconsistent) state nor double posting issues but it's definitely something I might add eventually and might end up in a dedicated article.&lt;/p&gt;

&lt;p&gt;As a side note, Python &lt;a href="https://pythonhosted.org/feedparser/introduction.html#parsing-a-feed-from-a-remote-url"&gt;Feedparser&lt;/a&gt; external library is powerful and easy to use, I &lt;a href="https://a.l3x.in/2016/02/23/sonos-spreaker.html"&gt;had the pleasure&lt;/a&gt; to work with it &lt;a href="https://github.com/home-assistant/home-assistant/pull/1836"&gt;back in the days&lt;/a&gt; when I was actively contributing to the awesome &lt;a href="https://www.home-assistant.io"&gt;Home Assistant&lt;/a&gt; Open Source project. Now I just have to figure out how to handle the last submission state, for example keeping a record updated in a &lt;em&gt;DynamoDB&lt;/em&gt; table. For the &lt;em&gt;cronjob&lt;/em&gt; part, I already know where to get inspiration thanks to &lt;a href="https://greengocloud.com/2020/01/24/Build-a-Twitter-bot-with-AWS-Lambda-and-CDK/"&gt;this great article&lt;/a&gt; by Blake Green.&lt;/p&gt;

&lt;h2&gt;
  
  
  404 Social not found
&lt;/h2&gt;

&lt;p&gt;Unfortunately I discovered exploring the various APIs' documentation that some of the most popular social networks don't allow this kind of workflow. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LinkedIn support answered me they don't let developers use &lt;em&gt;OAuth 2-legged authorization&lt;/em&gt; anymore:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"All of our API calls run through 3-legged authorization. We are not assigning &lt;a href="https://docs.microsoft.com/en-us/linkedin/shared/authentication/client-credentials-flow"&gt;2-legged auth&lt;/a&gt; for any of our partners"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Hacker News is &lt;a href="https://blog.ycombinator.com/hacker-news-api/"&gt;read-only&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Facebook posting via the &lt;code&gt;/feed&lt;/code&gt; endpoint is &lt;a href="https://developers.facebook.com/docs/graph-api/reference/v6.0/user/feed"&gt;disabled since April 24, 2018&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a side note, I honestly find this lack of faith in their users... &lt;a href="https://imgflip.com/i/3pfgj4"&gt;&lt;em&gt;disturbing&lt;/em&gt;&lt;/a&gt;. In a world &lt;em&gt;eaten by software&lt;/em&gt; like the one we live in today I'd consider this a basic feature and its unavailability a strong factor for deciding which platform is less worth investing in (professionally speaking).&lt;/p&gt;

&lt;h2&gt;
  
  
  Pitfalls
&lt;/h2&gt;

&lt;p&gt;I list here some of the missteps I made along the way in the hope they might help someone avoid time waste (and frustration):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;making changes to &lt;code&gt;lambda-layers&lt;/code&gt; stack after the initial deploy leads to CDK inconsistent states because of an open &lt;a href="https://github.com/aws/aws-cdk/issues/1972"&gt;CloudFormation bug&lt;/a&gt;. As a workaround you could think of bundling external libraries' code in the stack or use the Layer resource name (instead of the &lt;code&gt;stacks.lambda-layers.layers&lt;/code&gt; &lt;em&gt;object&lt;/em&gt;) as input for other stacks. This obviously &lt;strong&gt;clashes with the premise of CDK abstractions&lt;/strong&gt; in the first place. I'm ok with what I have right now but I might change my mind in the future if I see my need for Layers increases. If you end up in an inconsistent state for whatever reason, you might need to either destroy/recreate all the stacks one by one in reverse hierarchical order (&lt;code&gt;lambda-layers&lt;/code&gt; at last) or, in case you can't sustain any downtime, deploy to a different target region and/or account, migrate the service and finally destroy the original setup&lt;/li&gt;
&lt;li&gt;as already mentioned above, many platforms don't give write access via APIs and you might end up spending quite some time implementing what you found in the documentation... before discovering that they won't work as expected because of missing support for &lt;em&gt;Oauth 2-legged flow&lt;/em&gt; for example.&lt;/li&gt;
&lt;li&gt;in the official &lt;a href="https://github.com/aws-samples/aws-cdk-examples"&gt;example repositories&lt;/a&gt; you might find outdated CDK code, nothing major popped up so far but at least some deprecated code here and there (for example the use of &lt;code&gt;core.Code.Asset&lt;/code&gt; instead of &lt;code&gt;core.Code.fromAsset&lt;/code&gt;), so better to double check the docs all the time after the occasional &lt;a href="https://en.wikipedia.org/wiki/Cargo_cult_programming"&gt;&lt;em&gt;cargo cult&lt;/em&gt;&lt;/a&gt; rushes&lt;/li&gt;
&lt;li&gt;just increase memory when lambdas are dying early without producing logs even if logs &lt;code&gt;REPORT&lt;/code&gt; from &lt;em&gt;CloudWatch Logs&lt;/em&gt; say max memory limit was not reached (in my experiments I never went over 80Mb but a few Lambdas were dying silently when running with the 128Mb default ram size, just doubling the allotted memory solved the problem)&lt;/li&gt;
&lt;li&gt;deleting CloudWatch &lt;em&gt;LogGroups&lt;/em&gt; manually lose their default retention period when automatically recreated by Lambda's execution&lt;/li&gt;
&lt;li&gt;prefer &lt;code&gt;requests&lt;/code&gt; over the more complex standard &lt;code&gt;urlilib&lt;/code&gt; Python library if you don't mind adding an external dependency, it will make your life &lt;em&gt;much&lt;/em&gt; easier&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;Lambdas defined in the same stack are always redeployed when there's any change in the stack folder (excluding the CDK code) or in the &lt;code&gt;utils&lt;/code&gt; library. This is by design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;given the tiny blob size (64Kb unzipped, less than 8Kb after compression) the code is the same for all the lambdas anyway and in this way I ensure they're always running the latest version&lt;/li&gt;
&lt;li&gt;the blob &lt;em&gt;zip&lt;/em&gt; archive is uploaded only once per deploy so only a new &lt;code&gt;UpdateFunctionCode&lt;/code&gt; call per Lambda with a link to the new uploaded code archive in S3 is triggered, i.e. there's no bandwidth waste anyway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If that doesn't fit your needs you might want to tinker with &lt;em&gt;symlinks&lt;/em&gt;, CDK &lt;code&gt;core.Code.fromAsset.exclude&lt;/code&gt; &lt;em&gt;glob patterns&lt;/em&gt;, make a different use of Lambda Layers (the last one at your own peril!) or rethink the folders layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I think what I have is already in a decent enough shape to be shared but I won't consider this production ready yet. To close the gap some important pieces are missing, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add unit tests for the core (especially the &lt;code&gt;utils&lt;/code&gt; lib)&lt;/li&gt;
&lt;li&gt;add monitoring/alerting&lt;/li&gt;
&lt;li&gt;run some security audit (e.g. review the &lt;em&gt;IAM roles and policies&lt;/em&gt; autogenerated by CDK)&lt;/li&gt;
&lt;li&gt;add a local testing environment to easily test Lambdas, and maybe a remote &lt;em&gt;staging&lt;/em&gt; one too&lt;/li&gt;
&lt;li&gt;add support for automatic deploys with &lt;code&gt;cdk deploy&lt;/code&gt; run by a CI/CD pipeline&lt;/li&gt;
&lt;li&gt;introduce some state management to:

&lt;ul&gt;
&lt;li&gt;save the latest status update to avoid double submissions&lt;/li&gt;
&lt;li&gt;keep a well formatted log of the &lt;em&gt;publish events&lt;/em&gt; (to be displayed via a web dashboard for example)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;add meta tags to the blog with a small size preview image to increase &lt;em&gt;clickability&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;... let me know about &lt;strong&gt;what you think&lt;/strong&gt; might be a &lt;em&gt;must to have&lt;/em&gt; for production&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Python
&lt;/h2&gt;

&lt;p&gt;One might ask why not to choose some other runtime given that CDK offers a wide option of programming languages for our &lt;em&gt;Infra as Code&lt;/em&gt;. Here I list few of the reasons why I think Python v3.8 &lt;strong&gt;fits really well in this context&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;em&gt;standard library&lt;/em&gt; is &lt;a href="https://xkcd.com/353/"&gt;fully featured&lt;/a&gt; so usually very little dependencies are needed, in this case only &lt;code&gt;Boto3&lt;/code&gt; (which is already bundled with the default Python Lambda runtime), &lt;code&gt;BeautifulSoup4&lt;/code&gt; and &lt;code&gt;requests_oauthlib&lt;/code&gt;. Less dependencies mean smaller memory footprint and code size, ease of maintenance and reduced risk of hidden (security) bugs&lt;/li&gt;
&lt;li&gt;the inheritance model is neat, for example the &lt;em&gt;OO pattern&lt;/em&gt; I followed in the &lt;a href="https://github.com/shaftoe/api-l3x-in/blob/0.3.0/lib/utils/handlers.py"&gt;&lt;code&gt;utils.handlers&lt;/code&gt;&lt;/a&gt; library helps me to reason about that particular piece of logic in a way that I feel natural&lt;/li&gt;
&lt;li&gt;Python has been a &lt;em&gt;first class citizen&lt;/em&gt; in AWS Lambda land since it was initially released, the latest stable version (3.8.1) of the runtime is supported, and generally I bet it's a language that's going to stay relevant for long&lt;/li&gt;
&lt;li&gt;I'm used to dynamic typing but I find that the &lt;a href="https://docs.python.org/3/library/typing.html"&gt;&lt;code&gt;typing&lt;/code&gt;&lt;/a&gt; built-in library is quite useful for properly documenting the core classes and methods while at the same time lets me free to use it only when and where it makes sense to me (to be more specific, it's not an &lt;em&gt;all or nothing&lt;/em&gt; choice like the one between JavaScript and TypeScript for example). Disclaimer: differently from statically typed languages it won't help you with correctness at runtime though.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was a really fun project to work on, it reminded me many times of the good old days when I used to spend massive amount of time just playing with those little LEGO© bricks.&lt;/p&gt;

&lt;p&gt;Back to &lt;em&gt;now&lt;/em&gt;, the more I get to know CDK (and AWS in general) the more I understand the huge potential for quickly developing robust, secure and massively scalable applications while having some fun along the way. Adding new AWS resources and dependencies with CDK is very straightforward so you don't have to waste time defining resources' details and can focus almost completely on the actual business logic of your application.&lt;/p&gt;

&lt;p&gt;Speaking of the different building blocks offered by AWS nowadays, they are so many that I find it hard to believe anything to cover all the possible compute needs is missing; I'm sure there are such cases, I just haven't found them yet personally. I know for example lots of developers are asking for some kind of serverless &lt;em&gt;ElasticSearch&lt;/em&gt; offer, I won't be surprised if that's announced at one of the next &lt;a href="https://reinvent.awsevents.com/"&gt;AWS re:Invent&lt;/a&gt; events: the rate at which new AWS features and services are released is truly astounding and it makes it harder and harder for who like me wants to keep up with the available options.&lt;/p&gt;

&lt;p&gt;Finally, speaking of the &lt;em&gt;CDK abstractions considered harmful&lt;/em&gt; argument I can only say that, at least with a simple project like this, they deliver exactly what I'd expect: &lt;strong&gt;eliminate code duplication&lt;/strong&gt; and seamlessly add &lt;strong&gt;dependencies between stacks&lt;/strong&gt;. It's true there are still a few rough edges to be softened here and there (for example the &lt;a href="https://github.com/aws/aws-cdk/issues/1972"&gt;Lambda Layers dependency bug&lt;/a&gt; mentioned above, or this &lt;a href="https://github.com/aws/aws-cdk/issues/2089"&gt;about CloudWatch Alarms&lt;/a&gt; that I recently stumbled upon) but I see the project is quite active and I'm confident they'll be all fixed as the project becomes more mature.&lt;/p&gt;

&lt;p&gt;As usual I'd like to close the write up reminding you that I'd love to &lt;strong&gt;hear some feedback&lt;/strong&gt; (of ANY kind) from you, including suggestions on how to improve the codebase. Drop a message here below or &lt;a href="https://a.l3x.in/contact.html"&gt;contact me directly&lt;/a&gt; if you prefer, I'm looking forward to hear your thoughts.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>serverless</category>
      <category>sns</category>
    </item>
    <item>
      <title>Introducing AWS CDK with a real life Lambda and API gateway example</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Tue, 04 Feb 2020 10:12:29 +0000</pubDate>
      <link>https://dev.to/shaftoe/introducing-aws-cdk-with-a-real-life-lambda-and-api-gateway-example-7kg</link>
      <guid>https://dev.to/shaftoe/introducing-aws-cdk-with-a-real-life-lambda-and-api-gateway-example-7kg</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://a.l3x.in/"&gt;Alexander Fortin's tech blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Amazon Web Services &lt;a href="https://aws.amazon.com/blogs/aws/aws-cloud-development-kit-cdk-typescript-and-python-are-now-generally-available/"&gt;recently announced&lt;/a&gt; a new tool meant to ease the management of AWS resources in a fully programmatic way: &lt;strong&gt;AWS Cloud Development Kit&lt;/strong&gt; (&lt;strong&gt;CDK&lt;/strong&gt; for short).&lt;/p&gt;

&lt;p&gt;As stated in the &lt;a href="https://aws.amazon.com/cdk/"&gt;project homepage&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[&lt;em&gt;CDK is&lt;/em&gt;] an open source software development framework to model and provision your cloud application resources using familiar programming languages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To me it represents a welcome addition to the likes of &lt;a href="https://aws.amazon.com/serverless/sam/"&gt;AWS SAM&lt;/a&gt; and HashiCorp's &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt;, extending the realm of what's possible today with &lt;em&gt;Infrastructure as Code&lt;/em&gt; (IaC) on AWS.&lt;/p&gt;

&lt;p&gt;It's also introducing a new paradigm compared to other DSL-based tools, i.e. write &lt;em&gt;Infra as Code&lt;/em&gt; in your favorite popular OO language, together with some interesting new features that weren't available until now (more on these later).&lt;/p&gt;

&lt;p&gt;CDK is available in different popular programming languages like TypeScript (used as default for the examples in the official developer guide), JavaScript, Python (the one I use in my mini tutorial), C#/.NET and Java.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CDK
&lt;/h2&gt;

&lt;p&gt;From a practical standpoint, CDK is a Node.js application coupled with a bunch of packaged libraries that let you design, deploy and manage your AWS resources in a programmatic way.&lt;/p&gt;

&lt;p&gt;As you might expect from such a tool, you can define resources and interconnect them in dependency chains letting you leverage powerful abstractions (I'll discuss briefly about pros and cons of using abstractions in another chapter) and enforce various best practices out of the box.&lt;/p&gt;

&lt;p&gt;For example one of the most welcome features for me is that it removes almost completely the tedious task of properly defining (and attaching) IAM resources.&lt;/p&gt;

&lt;p&gt;Behind the scenes, when a CDK app is executed, it creates (&lt;strong&gt;synthesizes&lt;/strong&gt; in CDK parlance) a model of the infrastructure the user has defined in code leveraging CDK primitives.&lt;/p&gt;

&lt;p&gt;If no errors are found (wrong syntax, missing imported libraries, etc.) it generates an artifact (&lt;em&gt;synth&lt;/em&gt; phase) that is used as input by an AWS CloudFormation CLI process. CloudFormation will then be responsible for provisioning (&lt;em&gt;deploy&lt;/em&gt; phase) the actual resources in AWS.&lt;/p&gt;

&lt;p&gt;The latter point doesn't mean you need to run CloudFormation CLI manually (even if you could just stop at the &lt;em&gt;synth&lt;/em&gt; phase and use the CFN artifacts as you wish), just that CDK main process doesn't interact directly in the creation/destruction of resources and it's basically oblivious to what's happening while waiting for CloudFormation to be done.&lt;/p&gt;

&lt;p&gt;For example you can't add any &lt;em&gt;if this fails, than&lt;/em&gt; kind of logics but I don't think this is a unhealthy limitation to have, nor the other mentioned tools provide this kind of possibility as far as I know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why CDK
&lt;/h2&gt;

&lt;p&gt;CDK is not only the CLI tool and libraries, it's a new take on the whole idea of defining AWS resources all-together. The tool is so powerful it makes you truly feel the sky is the only limit.&lt;/p&gt;

&lt;p&gt;For example, one idea that I'm already toying around is to have a reusable &lt;em&gt;stack&lt;/em&gt; that makes it dumb simple to have Lambda outputted errors delivered to some (easily replaceable) notification service of choice, together with proper alarms in place, and so on. If done right in future CDK projects should be possible to just pass this hypothetical &lt;em&gt;errors delivery&lt;/em&gt; stack as a property to pass to new stacks and have the functionality automatically applied in their scope.&lt;/p&gt;

&lt;p&gt;If I get it right, &lt;strong&gt;the most ambitious goal&lt;/strong&gt; of CDK is to make these kind of abstractions the common &lt;strong&gt;way of thinking&lt;/strong&gt; about your AWS-based projects, all while transparently enforcing best practices like modularity, security, and so on.&lt;/p&gt;

&lt;p&gt;One of the common troubles when defining infrastructure as code in AWS has been the tedious task of writing CloudFormation templates by hand. Tools like Terraform and SAM ease that burden but they generally lack the unlimited flexibility provided by CDK.&lt;/p&gt;

&lt;p&gt;It's true, since long we could use libraries &lt;a href="https://boto3.amazonaws.com/v1/documentation/api/latest/index.html"&gt;like Boto&lt;/a&gt; to interact programmatically with AWS resources. So far though we were lacking a general purpose framework that saved us from the burden of implementing a dependency graph, or writing a lot of IAM boilerplate, or enforcing &lt;em&gt;least privilege principle&lt;/em&gt; all the time, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;General availability for TS and Python has been &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/07/the-aws-cloud-development-kit-aws-cdk-is-now-generally-available1/"&gt;announced&lt;/a&gt; not long ago but to my surprise I found there's already a good amount of (official and not) resources to quickly get up and running with CDK.&lt;/p&gt;

&lt;p&gt;As a side note, I got to know about CDK in the first place via this &lt;a href="https://www.youtube.com/user/AWSwebinars"&gt;YouTube feed&lt;/a&gt;. I then moved my first steps from the official &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html"&gt;getting started&lt;/a&gt; documentation and subsequently landed on the excellent workshop at &lt;a href="https://cdkworkshop.com"&gt;https://cdkworkshop.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core concepts
&lt;/h2&gt;

&lt;p&gt;In the following paragraphs I list the basic CDK concepts needed to get started with a simple project like the one used in the tutorial:&lt;/p&gt;

&lt;h3&gt;
  
  
  constructs
&lt;/h3&gt;

&lt;p&gt;Constructs are the main building blocks you're going to define with CDK. From the &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/constructs.html"&gt;official docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Constructs are the basic building blocks of AWS CDK apps. A construct represents a "cloud component" and encapsulates everything AWS CloudFormation needs to create the component.&lt;/p&gt;

&lt;p&gt;[...]&lt;/p&gt;

&lt;p&gt;For example, a central team can define a construct that implements the company's best practice for a DynamoDB table with backup, global replication, auto-scaling, and monitoring, and share it with teams across a company or publicly&lt;/p&gt;

&lt;p&gt;[...]&lt;/p&gt;

&lt;p&gt;AWS constructs make least-privilege permissions easy to achieve by offering simple, intent-based APIs to express permission requirements.&lt;/p&gt;

&lt;p&gt;[...]&lt;/p&gt;

&lt;p&gt;AWS constructs are designed around the concept of "sensible defaults."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Follows a code example from my tutorial.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/shaftoe/api-gateway-lambda-cdk-example/blob/master/src/stack.py"&gt;&lt;code&gt;src/stack.py&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from os import environ
from aws_cdk import (
    [...]
    aws_certificatemanager,
)

[...]

cert = aws_certificatemanager.Certificate(
    self,
    '{}-certificate'.format(environ['CDK_APP_NAME']),
    domain_name=environ['CDK_BASE_DOMAIN'],
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  stacks
&lt;/h3&gt;

&lt;p&gt;A &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/stacks.html"&gt;Stack&lt;/a&gt; is an abstraction that helps in grouping resources:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All AWS resources defined within the scope of a stack, either directly or indirectly, are provisioned [&lt;em&gt;by CloudFormation&lt;/em&gt;] as a single unit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our example we make use of a single stack but we could define more in our application. Refer to &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/stacks.html"&gt;the docs&lt;/a&gt; if you want to know more.&lt;/p&gt;

&lt;p&gt;A code example from the tutorial:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/shaftoe/api-gateway-lambda-cdk-example/blob/master/src/stack.py"&gt;&lt;code&gt;src/stack.py&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ApiStack(core.Stack):  ## Stack class definition

    def __init__(self, scope: core.Construct, id: str, **kwargs) -&amp;gt; None:
        super().__init__(scope, id, **kwargs)

        ### the constructs belonging to the scope of ApiStack follow below...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  identifiers
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/identifiers.html"&gt;Identifiers&lt;/a&gt; are the labels we give to our resources:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Identifiers must be unique within the scope in which they are created; they do not need to be globally unique in your AWS CDK application&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The list above is by no any means exhaustive, I suggest you to read the &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/core_concepts.html"&gt;documentation&lt;/a&gt; to get acquainted with all the other CDK core concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices
&lt;/h2&gt;

&lt;p&gt;CDK is relatively new so it's too early to discuss thoroughly about best practices, as usual they should emerge in time (assuming of course that the tool will gain adoption and stay relevant).&lt;/p&gt;

&lt;p&gt;I found this interesting one in &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/apps.html"&gt;the docs&lt;/a&gt; though:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generally, we recommend that you perform validation as soon as possible (usually as soon as you get some input) and throw exceptions as early as possible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I also dare adding a related recommendation: add constructs iteratively and frequently test syntax and semantics running &lt;code&gt;cdk diff&lt;/code&gt;. This will help you identify problems early on sparing you from debugging complex mixed code exceptions (both Node.js and Python in my case).&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial: migrate Lambda and API Gateway from Terraform to CDK
&lt;/h2&gt;

&lt;p&gt;Enough talk, about time to see something practical.&lt;/p&gt;

&lt;p&gt;In this &lt;a href="https://github.com/shaftoe/api-gateway-lambda-cdk-example/"&gt;mini tutorial&lt;/a&gt; we see how to make use of CDK to connect both a Lambda and an ACM SSL certificate to a (CORS enabled) API Gateway, so to be able to trigger a synchronous Lambda execution via an unauthenticated POST request to an HTTPS endpoint like &lt;code&gt;https://api.mydomain.com/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The code in the tutorial is currently used for a real backend that receives POST data from an HTML form and (in absence of errors) delivers the message content to a 3rd party API messaging service. You can find &lt;a href="https://github.com/shaftoe/api-gateway-lambda-cdk-example/"&gt;more details&lt;/a&gt; about both architecture and implementation in the README.&lt;/p&gt;

&lt;p&gt;As a side note, the migration that gave birth to this mini-tutorial was relatively simple and went very smoothly, with &lt;strong&gt;all the functionalities&lt;/strong&gt; replicated in a new AWS region without any downtime for the API service.&lt;/p&gt;

&lt;p&gt;It also brought significant reduction in the amount of code required for the exact &lt;strong&gt;same AWS setup&lt;/strong&gt; (&lt;em&gt;almost&lt;/em&gt; exact to be precise: CDK introduced a few new CloudFormation and S3 resources that were not there):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# OLD SETUP&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; infra/&lt;span class="k"&gt;*&lt;/span&gt; src/&lt;span class="k"&gt;*&lt;/span&gt; Makefile
  121 infra/api-gateway.tf
   58 infra/lambda.tf
   24 infra/main.tf
    4 infra/versions.tf
   97 src/lambda.py
   19 Makefile
  323 total

&lt;span class="c"&gt;# NEW SETUP&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; lambda/&lt;span class="k"&gt;*&lt;/span&gt; src/&lt;span class="k"&gt;*&lt;/span&gt; Makefile
   92 lambda/contact_us.py
   20 src/app.py
   54 src/stack.py
   51 Makefile
  217 total
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If we consider only &lt;em&gt;Infra as Code LoC&lt;/em&gt; the simplification is even more evident:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# OLD SETUP&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; infra/&lt;span class="k"&gt;*&lt;/span&gt;
  121 infra/api-gateway.tf
   58 infra/lambda.tf
   24 infra/main.tf
    4 infra/versions.tf
  207 total

&lt;span class="c"&gt;# NEW SETUP&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; src/&lt;span class="k"&gt;*&lt;/span&gt;
  20 src/app.py
  54 src/stack.py
  74 total
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I know, &lt;em&gt;Lines of Code&lt;/em&gt; is a rough indicator that doesn't tell the full story about complexity but it's common belief that less code means less surface for introducing bugs and less data to have stacked in your mind while coding. Personally I find the CDK code very easy to read but I'm a long time Python developer so it's hard for me to tell how much my bias is involved in this judgement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pros and Cons
&lt;/h2&gt;

&lt;p&gt;I haven't had enough time yet to properly experiment with CDK, adopting it for new projects (where it makes sense) will help me grow a better understanding of the tool and give a better informed opinion about it.&lt;/p&gt;

&lt;p&gt;That said, I list here some of the pros and cons that I noticed so far:&lt;/p&gt;

&lt;h3&gt;
  
  
  pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;very powerful and flexible&lt;/li&gt;
&lt;li&gt;sane and secure defaults&lt;/li&gt;
&lt;li&gt;DRY-friendly&lt;/li&gt;
&lt;li&gt;built-in support for multi-region, multi-account and multi-environment (&lt;em&gt;dev&lt;/em&gt;, &lt;em&gt;prod&lt;/em&gt;, etc) setups&lt;/li&gt;
&lt;li&gt;support for popular languages may get you (and/or other team members) quickly up to speed&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;physical&lt;/em&gt; resource names (e.g. an actual S3 bucket name) are name-spaced to avoid name clashes, making it much easier to manage global resources (e.g. S3) in multi-region/multi-account setups&lt;/li&gt;
&lt;li&gt;custom resources enhanced &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html"&gt;reusability&lt;/a&gt;:
&amp;gt; "[...] wrap them up into a regular construct interface, so from another user's perspective the feature feels native".&lt;/li&gt;
&lt;li&gt;AWS has grown a reputation for maintaining products and services for long (actually I'm not aware of &lt;em&gt;any&lt;/em&gt; AWS product that has been discontinued) which should make investing in CDK a safer bet compared to other &lt;em&gt;Open Source&lt;/em&gt; tools that have no powerful company on their back and might disappear from the radar if not properly financed&lt;/li&gt;
&lt;li&gt;nice to have features like apply tags recursively to all children &lt;em&gt;constructs&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;not cloud agnostic: works only on AWS&lt;/li&gt;
&lt;li&gt;it feels like it's still in early stage of development, e.g.:

&lt;ul&gt;
&lt;li&gt;CloudFormation APIs are not 100% covered yet&lt;/li&gt;
&lt;li&gt;links to examples &lt;a href="https://stackoverflow.com/a/60015276/2274124"&gt;are disappearing&lt;/a&gt; and/or changing URL&lt;/li&gt;
&lt;li&gt;documentation could definitely be improved adding more examples on common scenarios and best practices&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;inflexible (and AFAIK unavoidable) way of setting up new projects&lt;/li&gt;
&lt;li&gt;it's reasonable to believe at least some of CDK metadata files (kept in the &lt;code&gt;&amp;lt;app_name&amp;gt;/cdk.out&lt;/code&gt; folder) should be managed in the way &lt;code&gt;terraform.tfstate&lt;/code&gt; files need to be. Currently there's no built-in support for decentralization (for example in the way Terraform is doing supporting S3 buckets and other backends), making it harder to fit in a CI/CD pipeline for example&lt;/li&gt;
&lt;li&gt;minor: &lt;code&gt;cdk destroy&lt;/code&gt; leaves &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/troubleshooting.html#troubleshooting_nobucket"&gt;some leftovers&lt;/a&gt; in CFN and CloudWatch Logs that need to be deleted manually&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TF vs CDK... FIGHT!
&lt;/h2&gt;

&lt;p&gt;I'm a HashiCorp's products user since the early &lt;em&gt;Vagrant&lt;/em&gt; days. Terraform in particular has been one of my favorite tools and I've been using it for quite some time now.&lt;/p&gt;

&lt;p&gt;To clear up any doubt, I'm &lt;strong&gt;not planning to drop TF&lt;/strong&gt; any soon nor to replace it with CDK in &lt;em&gt;any&lt;/em&gt; new project.&lt;/p&gt;

&lt;p&gt;Even if I focus mainly on AWS, now and then I need to interact with other service providers which is obviously not doable with CDK, and this blocker is already enough to keep Terraform safe and warm in the toolbox.&lt;/p&gt;

&lt;p&gt;That said, I like &lt;em&gt;very much&lt;/em&gt; the way CDK is doing a lot of heavy lifting that TF does not when setting up VPC or IAM resources for example. The boilerplate reduction is consistent as it is the reduction of security risks not having to write IAM policies by hand.&lt;/p&gt;

&lt;p&gt;Disclaimer: I trust AWS developers here, personally I haven't dug much into the actual soundness of any of the IAM setup CDK automatically generated for me. IAM correctness is described multiple times in the docs as a strong selling point though and I think it's reasonable to believe it. &lt;strong&gt;Double checking IAM&lt;/strong&gt; policies and roles before going to production &lt;strong&gt;is always a good idea&lt;/strong&gt; though, regardless of how IAM resources were defined.&lt;/p&gt;

&lt;p&gt;Finally, I'm undecided yet how the &lt;em&gt;object oriented&lt;/em&gt; approach maps the domain compared to something more declarative like the Terraform DSL. In the past (before the &lt;a href="https://a.l3x.in/2020/01/29/my-quest-for-identity-in-software-engineering.html"&gt;Serverless epiphany&lt;/a&gt; struck me...) I made extensive daily use of &lt;a href="https://puppet.com"&gt;Puppet&lt;/a&gt; and generally thought that using declarative code for defining system/infrastructure setups helped in maintaining and reasoning about the codebase in general, about resources' interdependencies in particular.&lt;/p&gt;

&lt;p&gt;In practice though declaring resources in hierarchical order doesn't feel much of a limitation given that's how I'm used to think about the infrastructure anyway (i.e. &lt;em&gt;first&lt;/em&gt; I create an Ec2 VPC, &lt;em&gt;then&lt;/em&gt; I add an ECS cluster to it, &lt;em&gt;then&lt;/em&gt; I add a tasks definition to the ECS cluster, &lt;em&gt;etc..&lt;/em&gt; ).&lt;/p&gt;

&lt;p&gt;I briefly discussed the topic with a good friend of mine who's working daily on AWS at (&lt;em&gt;very big&lt;/em&gt;) scale, he rises the argument of how mature those declarative DSL approaches are today compared to other battle tested &lt;em&gt;old&lt;/em&gt; technologies like Python. He's right when he says there has been not enough time yet to squash all the most annoying bugs, reduce the workarounds needed in some common scenarios like multi-region/multi-account setups and to consolidate best practices.&lt;/p&gt;

&lt;p&gt;Honestly it's a hard call and I don't feel like I have enough hands-on experience yet with CDK and the other tools to make it.&lt;/p&gt;

&lt;h2&gt;
  
  
  General reception
&lt;/h2&gt;

&lt;p&gt;Apparently CDK approach is not very welcome in the &lt;em&gt;Serverless&lt;/em&gt; world. For example in the latest &lt;a href="https://www.serverlesschats.com/33"&gt;"Serverless Chats" podcast&lt;/a&gt; episode (around minute 34') CDK doesn't get much appreciation compared to other tools like &lt;em&gt;Serverless Framework&lt;/em&gt; or &lt;em&gt;SAM&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I understand the argument validity, that those tools might be a better fit compared to CDK in specific &lt;em&gt;serverless scenarios&lt;/em&gt;, like the ones involving mostly Lambdas and API Gateways. So far though I still believe CDK might fit decently even in those cases. If I got it right from the start, &lt;em&gt;sane defaults&lt;/em&gt; and &lt;em&gt;built in secure IAM setup&lt;/em&gt; are the biggest key selling points of CDK, not the wide choice of languages available.&lt;/p&gt;

&lt;p&gt;On top of that, being an AWS &lt;em&gt;general purpose&lt;/em&gt; framework means that you might use it in any kind of projects involving AWS and not only when following a strict &lt;em&gt;serverless&lt;/em&gt; approach.&lt;/p&gt;

&lt;p&gt;Talking about the possibility for abstractions, they are definitely a selling point too and I agree they are a double edged sword that can get out of hand if used improperly. That said, CDK is not very opinionated on that, leaving you the &lt;strong&gt;freedom to choose&lt;/strong&gt; if and when to make use of abstractions, as well as to proceed in a more &lt;em&gt;procedural&lt;/em&gt; way if preferred. You can still &lt;em&gt;choose your poison&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;My recommendation, in this case as in many others, is to try it out and decide by yourself if CDK gives you better results compared to other approaches. On one thing I believe we all agree though: &lt;strong&gt;don't reinvent the wheel&lt;/strong&gt; trying to write your own IaC deployment system, just choose the one between the popular ones that better fits to your context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature requests for CDK devs
&lt;/h2&gt;

&lt;p&gt;I guess these will never make it to the top of their roadmap but it's honestly what I'd like to have changed with the CLI tool at this phase: being able to bootstrap a new project with an empty template (i.e. no automatic scaffolding) with decentralized handling of CDK local metadata (&lt;code&gt;cdk.out&lt;/code&gt; folder).&lt;/p&gt;

&lt;p&gt;This would fit well with my usual workflow that goes similar to this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;initialize the project repository&lt;/li&gt;
&lt;li&gt;add commands to &lt;code&gt;Makefile&lt;/code&gt; as I go&lt;/li&gt;
&lt;li&gt;iterate until the project is up and running&lt;/li&gt;
&lt;li&gt;in the future, when I'll have most probably forgotten the implementation details, just run &lt;code&gt;git clone &amp;amp;&amp;amp; make all&lt;/code&gt; to have the project up and running on a different workstation or CI/CD setup, or deployed on a different target AWS account/region.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Currently the last CDK CLI stable version (1.22.0) only provides an option (&lt;code&gt;--output&lt;/code&gt;) to place &lt;code&gt;cdk.out&lt;/code&gt; folder in a different &lt;em&gt;PATH&lt;/em&gt; which might help when in a CI/CD but it doesn't when working in a team for example.&lt;/p&gt;

&lt;p&gt;Coupled with the feature above it would be nice to have some kind of &lt;code&gt;cdk sync&lt;/code&gt; command that reads &lt;code&gt;cdk.out&lt;/code&gt; URI from a local config file and fetches whatever needed (locally) to be able to apply new functionalities (remotely).&lt;/p&gt;

&lt;p&gt;Lastly, a nice to have: some kind of &lt;code&gt;--force&lt;/code&gt; switch for &lt;code&gt;cdk destroy&lt;/code&gt; to remove every resource created, including the CloudFormation CDK &lt;em&gt;meta&lt;/em&gt; stack and CloudWatch Logs (if any).&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;I hope this article and &lt;a href="https://github.com/shaftoe/api-gateway-lambda-cdk-example/"&gt;the mini tutorial&lt;/a&gt; give you a good introduction to CDK, the external links above should also point you in the right direction if willing to try CDK out or know more about it.&lt;/p&gt;

&lt;p&gt;It's definitely a powerful tool that has just entered the landscape joining other projects that are at a more mature phase of development.&lt;/p&gt;

&lt;p&gt;My recommendation is to spare some time and have some fun with it so to make a better informed decision next time you bootstrap a project backed by AWS.&lt;/p&gt;

&lt;p&gt;I'm curious to know what you think about it! If you have anything to say or errors and imprecisions to correct please leave a message here or &lt;a href="https://dev.to/contact"&gt;contact me directly&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>lambda</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Remaking my curriculum vitæ with modern web technologies</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Fri, 31 Jan 2020 12:15:26 +0000</pubDate>
      <link>https://dev.to/shaftoe/remaking-my-curriculum-vitae-with-modern-web-technologies-39nl</link>
      <guid>https://dev.to/shaftoe/remaking-my-curriculum-vitae-with-modern-web-technologies-39nl</guid>
      <description>&lt;p&gt;As I wrote in my &lt;a href="https://a.l3x.in/2020/01/29/my-quest-for-identity-in-software-engineering.html"&gt;previous blog article&lt;/a&gt;, recently I became quite hooked by the web and all that's orbiting around it.&lt;/p&gt;

&lt;p&gt;This time I'm going to show you a simple project developed following Jamstack guidelines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML5 for responsive and accessible markup&lt;/li&gt;
&lt;li&gt;CSS3 to lay out the presentation&lt;/li&gt;
&lt;li&gt;Hugo to statically generate the content&lt;/li&gt;
&lt;li&gt;GitHub for source hosting&lt;/li&gt;
&lt;li&gt;Netlify for content hosting and (fast) delivery&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The project
&lt;/h2&gt;

&lt;p&gt;In the past I've been relying on a free offer from &lt;a href="https://www.visualcv.com"&gt;VisualCV&lt;/a&gt; to host my curriculum vitæ online. Recently though (end of 2019) they changed their terms and removed the free offer, so no more CV online for free.&lt;/p&gt;

&lt;p&gt;For a while I wanted to manage my CV in a more flexible way than VisualCV or any other job platform like LinkedIn offer. Almost at the same time of VisualCV move I stumbled upon this &lt;a href="https://css-tricks.com/new-year-new-job-lets-make-a-grid-powered-resume/"&gt;great article on CSS-Tricks&lt;/a&gt; by Ali Churcher, I liked it quite and made me think I had no more excuses to procrastinate.&lt;/p&gt;

&lt;p&gt;I thought a while about how to best approach the issue and then put in practice my new acquired skills as a web developer (while having quite some fun in the process).&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature request
&lt;/h2&gt;

&lt;p&gt;CSS-Tricks' article is showing flawlessly how to setup the layout, and that's exactly what I'd expect from an article by CSS-Tricks, but to me it is missing a few key features to make it really shine.&lt;/p&gt;

&lt;p&gt;One feature I definitely want to have is a comfortable way of managing the CV content. In the end what's the point of having it online if I can't keep it up to date with ease?&lt;/p&gt;

&lt;p&gt;In practice I just want to open my IDE, edit some Markdown, run &lt;code&gt;git commit &amp;amp;&amp;amp; git push&lt;/code&gt; and have the new changes deployed online asap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;To add my features I pair the cited HTML5+CSS3 &lt;em&gt;tricks&lt;/em&gt; with Hugo and its powerful content management features.&lt;/p&gt;

&lt;p&gt;A couple of more clicks in the Netlify app (plus a few new DNS records in my &lt;code&gt;l3x.in&lt;/code&gt; domain) and everything is live at &lt;a href="https://cv.l3x.in"&gt;https://cv.l3x.in&lt;/a&gt;, replacing the old HTTP redirect to VisualCV.&lt;/p&gt;

&lt;p&gt;You can find the actual source in &lt;a href="https://github.com/shaftoe/curriculum-vitae/"&gt;the GitHub repository&lt;/a&gt; with all the details in &lt;a href="https://github.com/shaftoe/curriculum-vitae/blob/master/README.md"&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/a&gt;, together with a mini tutorial on how to use it to build your own CV.&lt;/p&gt;

&lt;h2&gt;
  
  
  Highlights
&lt;/h2&gt;

&lt;p&gt;I take the chance to highlight a couple of more things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;differently from the layout proposed by CSS-Tricks, I opted for keeping the header (name, title and photo) outside the main grid. To me it makes more sense to have it decoupled from the content (i.e. from the grid) because the likelihood I'll put it anywhere else than the top is close to zero, and having picture and text box swap relative position with &lt;code&gt;display: flex&lt;/code&gt; is trivial&lt;/li&gt;
&lt;li&gt;in &lt;a href="https://github.com/shaftoe/curriculum-vitae/blob/master/layouts/partials/head.html"&gt;layouts/partials/head.html&lt;/a&gt; there are a few tricks to make the site load faster, thanks to Google Insights. At the time writing the live site &lt;a href="https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fcv.l3x.in%2F&amp;amp;tab=desktop"&gt;scores above 99%&lt;/a&gt; for both mobile and desktop&lt;/li&gt;
&lt;li&gt;Hugo makes it fun to add all sort of functionalities, &lt;a href="https://github.com/shaftoe/curriculum-vitae/blob/master/layouts/partials/footer.html"&gt;for example&lt;/a&gt; the &lt;em&gt;copyright&lt;/em&gt; and &lt;em&gt;last modified&lt;/em&gt; info in the footer&lt;/li&gt;
&lt;li&gt;another cool thing when using Hugo, you might think of partials as sort of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components"&gt;web components&lt;/a&gt;, that might help &lt;a href="https://github.com/shaftoe/curriculum-vitae/blob/master/layouts/_default/baseof.html"&gt;structuring your markup&lt;/a&gt; in a way clean and easy to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Possible enhancements
&lt;/h2&gt;

&lt;p&gt;I'm quite happy with the results and I don't think I'll change the layout for a while (I always need to improve the content but that's out of scope eheh).&lt;/p&gt;

&lt;p&gt;If you have any suggestions or recommendations please let me know either by leaving a comment below, &lt;a href="https://a.l3x.in/contact.html"&gt;contacting me directly&lt;/a&gt; or (even better) opening a &lt;a href="https://github.com/shaftoe/curriculum-vitae/pulls"&gt;pull request&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I list here a few of the possible improvements that came to my mind so far:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I've used SCSS (in &lt;a href="https://github.com/shaftoe/curriculum-vitae/tree/master/assets"&gt;assets/&lt;/a&gt; folder) to keep the style DRY but I'm pretty sure there is a lot of room for improvement&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;scroll to top&lt;/em&gt; icon should appear only after some scrolling, I don't know if that's possible in pure CSS alone and I'd like to keep the project JS free. If you know how to do it in CSS please let me know, thanks!&lt;/li&gt;
&lt;li&gt;The footer could appear at the very end of the viewport even when content is not filling up the page completely (e.g. &lt;a href="https://cv.l3x.in/programming/"&gt;https://cv.l3x.in/programming/&lt;/a&gt;). It should be easily done with flexbox, I'm ok as it is for now&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Feedback always welcome
&lt;/h2&gt;

&lt;p&gt;I hope you like it and hopefully find it useful too.&lt;/p&gt;

&lt;p&gt;As usual I'm more than happy to hear what you think, &lt;em&gt;especially&lt;/em&gt; if you have something critic to say about it: for sure it will hurt a bit but so far I don't know any better way to improve my skills ;)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://a.l3x.in/"&gt;Alexander Fortin's tech blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>jamstack</category>
      <category>html</category>
      <category>hugo</category>
    </item>
    <item>
      <title>My quest for identity in Software Engineering</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Wed, 29 Jan 2020 10:17:04 +0000</pubDate>
      <link>https://dev.to/shaftoe/my-quest-for-identity-in-software-engineering-nm0</link>
      <guid>https://dev.to/shaftoe/my-quest-for-identity-in-software-engineering-nm0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Keep walking. If I look back I am lost.&lt;/p&gt;

&lt;p&gt;― George R.R. Martin, &lt;em&gt;A Dance with Dragons&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They say that the sum of all our past choices and experiences makes up most of who we are. Despite that knowledge I never liked to invest much of my free time thinking about the past.&lt;/p&gt;

&lt;p&gt;I always thought there's such a thing as the right to forget and to be forgotten, that the opposite (to look &lt;em&gt;forward&lt;/em&gt;, to imagine how the future could be, some might say &lt;a href="https://open.spotify.com/track/3uYrVtgRuz4XiOOXDV805X"&gt;&lt;em&gt;to dream&lt;/em&gt;&lt;/a&gt;) is one choice that more consistently brings better outcomes.&lt;/p&gt;

&lt;p&gt;If only I could perfectly stabilize &lt;a href="https://en.wikipedia.org/wiki/Nirvana_(Buddhism)"&gt;between those two opposites&lt;/a&gt;... but that's maybe for another time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Faulty Random Access Memory
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;I have to believe that my actions still have meaning, even if I can't remember them.&lt;/p&gt;

&lt;p&gt;― &lt;em&gt;Memento&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There's more. It turns out that our brain is not very accurate at &lt;em&gt;remembering&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Contrary to the common belief, it doesn't store a perfect snapshot of reality as a calculator might do: immutable, &lt;em&gt;pixel perfect&lt;/em&gt; and ready to be retrieved at will. Multiple loads, same exact results.&lt;/p&gt;

&lt;p&gt;The human brain apparently doesn't care much about &lt;em&gt;von Neumann&lt;/em&gt; architecture and goes its own way as it did for the latest &lt;a href="https://www.britannica.com/story/just-how-old-is-homo-sapiens"&gt;three hundred thousand years&lt;/a&gt; or so.&lt;/p&gt;

&lt;p&gt;When remembering, i.e. creating a representation of a past experience in our mind, the hippocampus &lt;a href="https://www.psychologytoday.com/us/blog/the-athletes-way/201507/the-neuroscience-recalling-old-memories"&gt;coordinates&lt;/a&gt; the decoding of various &lt;em&gt;media chunks&lt;/em&gt;, loading the bits and pieces scattered (should I say sharded?) between different areas of the neocortex into a coherent entity.&lt;/p&gt;

&lt;p&gt;There's consensus between neuroscientists on how incredibly plastic our brain actually is, how it's constantly and frenetically rewiring itself, creating new pathways and dropping established ones to make room for new models of reality. All of that while running countless maintenance jobs in the background. It is hard to say to what extent this frantic synaptic rewiring activity is messing with the memories of what actually happened.&lt;/p&gt;

&lt;p&gt;That's not the point I want to make with this article though, neither &lt;em&gt;looking forward&lt;/em&gt; is a dogma for me. I always liked to keep flexible, leave doors open and go out of the ordinary &lt;em&gt;when it makes sense&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The more I get older, the more I recognize circumstances where it does make sense so, with my 40th birthday knocking at the door, it feels safe and fair enough to &lt;em&gt;look back&lt;/em&gt; and indulge in some wrap-ups and &lt;em&gt;where to go from here&lt;/em&gt; kind of reflections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Special Circumstances
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Just remember that the things you put into your head are there forever, he said.&lt;/p&gt;

&lt;p&gt;― Cormac McCarthy, &lt;em&gt;The Road&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If I do look back now I can't think anything other than "it has been a great ride". Sometimes harsh. Sometimes exhilarating. Never boring.&lt;/p&gt;

&lt;p&gt;The truth is, I was lucky. Not because of some mysterious invisible force keeping me pointed in the right direction, neither because of the average quality of the choices I made.&lt;/p&gt;

&lt;p&gt;I was born lucky.&lt;/p&gt;

&lt;p&gt;Lucky enough to spend the formative years in a rich economy that gave me, between other things that today for many are still mere luxuries, a solid education paired with early access to new fancy technologies.&lt;/p&gt;

&lt;p&gt;On top of that, I had a very timely chance to give a closer look at what back then was the new rising star in the global sky: the "World Wide Web".&lt;/p&gt;

&lt;h2&gt;
  
  
  How &lt;em&gt;deep&lt;/em&gt; is your learning?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;I’m floating around the universe on this giant sphere, suspended here by gravity and going for a ride.&lt;/p&gt;

&lt;p&gt;― Anita Harris, &lt;em&gt;Conscious&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With a growing stack of knowledge and experiences coming along wherever I've been, I was allowed to keep building new skills needed to constantly practice a profession that I still love today as much as I did when moving my first steps, if not more.&lt;/p&gt;

&lt;p&gt;Since then I could travel far and abroad, always to find a more than decent occupation virtually everywhere without much of a sweat. Titles have been ranging ever since from &lt;em&gt;Sysadmin&lt;/em&gt; to &lt;em&gt;System Engineer&lt;/em&gt; to &lt;em&gt;DevOps/SRE Engineer&lt;/em&gt; but those labels never truly mattered to me: I just wanted to deliver value doing what the business needed and it turned out their IT infrastructures needed a lot of care.&lt;/p&gt;

&lt;p&gt;I met a lot of professionals on the way. Some of them brilliant and ingenuous. Some flamboyant and inspiring. Some pretentious and annoying. Many though, if not all, joined by a common passion for what we &lt;em&gt;crafted&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That multicolored &lt;em&gt;culture within the culture&lt;/em&gt;, made up of uncountable people and ideas and cool new technologies and funny but &lt;a href="https://devsum-archive.s3.amazonaws.com/automate-meme.jpg"&gt;visually ugly&lt;/a&gt; memes (...and on and on), soon became fuel for virtuous loops of &lt;strong&gt;discover =&amp;gt; learn =&amp;gt; practice =&amp;gt; discover&lt;/strong&gt; that, almost mindlessly, kept me moving ahead doing what I liked and getting a pretty good living out of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eventually inconsistent
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Do not confuse &lt;em&gt;duty&lt;/em&gt; with what other people expect of you.&lt;/p&gt;

&lt;p&gt;― Robert A. Heinlein, &lt;em&gt;Time Enough for Love&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm increasingly unsure about how to answer the question &lt;em&gt;"Would you do any different?"&lt;/em&gt; though.&lt;/p&gt;

&lt;p&gt;I dropped out in the middle of my engineering studies and still forget &lt;em&gt;big O&lt;/em&gt; magnitudes 10 minutes after a coding interview is over. I never worked for a &lt;em&gt;BIG&lt;/em&gt; nor visited Silicon Valley. I didn't write a kernel module nor create any widely used (Free and Open Source) software. I was never fully in charge of any product beside when freelancing or working on a personal one.&lt;/p&gt;

&lt;p&gt;I could go on indefinitely.&lt;/p&gt;

&lt;p&gt;Heck, let's be honest, I was never &lt;strong&gt;the best&lt;/strong&gt; in anything I did, neither while studying the theory nor putting it into practice. For sure I missed out interesting but invisible (to me) opportunities, and a few times I even dropped already taken good ones while pursuing other more important goals.&lt;/p&gt;

&lt;p&gt;Finally, I lost the count of the many pet projects I fell in love with and forgot soon after, a behavior that at this point we can safely call chronic. It's true that some of those projects left a mark on me, increasing my knowledge and experience, but none of them ever truly conquered my heart.&lt;/p&gt;

&lt;p&gt;Maybe it's true what they say then: the &lt;em&gt;first love&lt;/em&gt; you can't ever forget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contacts[0]
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Common misconception that; that fun is relaxing. If it is, you’re not doing it right.&lt;/p&gt;

&lt;p&gt;― Iain M. Banks, &lt;em&gt;The Player of Games&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I first got interested in &lt;em&gt;the web&lt;/em&gt; around the late Nineties, when I connected to the Internet with a noisy &lt;em&gt;28.800 baud&lt;/em&gt; modem and upgraded to a &lt;em&gt;PPPoE&lt;/em&gt; ADSL soon after.&lt;/p&gt;

&lt;p&gt;Broadband connectivity in 1999... &lt;strong&gt;that&lt;/strong&gt; was luxury!&lt;/p&gt;

&lt;p&gt;The initial heat soon dissipated though. I started to believe I missed a key natural skill required to become a decent (web) designer, and if you see my hand writing you'd know instantly what I mean.&lt;/p&gt;

&lt;p&gt;The first attempts to set up from scratch a decently looking template for a website put me off as an impossible feat and made me wish for something... &lt;a href="https://devsum-archive.s3.amazonaws.com/css-meme.gif"&gt;more ergonomic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To be truly honest with you, the main reason I gave up is because &lt;em&gt;it wasn't fun&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Got &lt;em&gt;ROOTs&lt;/em&gt;?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Control can sometimes be an illusion. But sometimes you need illusions to gain control.&lt;/p&gt;

&lt;p&gt;— &lt;em&gt;Mr. Robot&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sending cryptic commands to an IRC bot &lt;a href="https://www.xkcd.com/303/"&gt;while waiting&lt;/a&gt; for a recompiling (2.x) Linux kernel. Configuring MTAs to securely relay and deliver &lt;code&gt;root@localhost&lt;/code&gt; emails to &lt;a href="https://www.xkcd.com/838/"&gt;&lt;em&gt;some mbox&lt;/em&gt;&lt;/a&gt;. All while jumping between one (hopefully &lt;em&gt;secure&lt;/em&gt;) &lt;em&gt;shell&lt;/em&gt; to another, palms sweating before hitting &lt;code&gt;enter&lt;/code&gt; when commands ended in something like &lt;code&gt;-j DROP&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All that was way too much fun to go in search of any other digital adventure. Even better, I discovered almost without realizing it that businesses were already willing to pay me to do that, full time.&lt;/p&gt;

&lt;p&gt;I soon lost interest in the new world of web development while I dived deeper and deeper in the one of networked computer systems. The &lt;em&gt;back&lt;/em&gt;, the infrastructure, the invisible &lt;em&gt;end&lt;/em&gt; felt like the right place to be: abstract, loosely constrained, wild and unexplored. So there I decided to put my &lt;em&gt;roots&lt;/em&gt; (pun intended).&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;web&lt;/em&gt;, the &lt;strong&gt;front&lt;/strong&gt;end, was out of my way for good. Or so I thought.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking (out of) DevOps
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"Evolution [is] a random walk across a minefield, not a pre-ordained trajectory, onward and upward toward &lt;em&gt;perfection&lt;/em&gt;."&lt;/p&gt;

&lt;p&gt;― Greg Egan, &lt;em&gt;Permutation City&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Jump forward a couple of decades and here I am, microblogging &lt;a href="https://fosstodon.org/web/statuses/103039798260051036"&gt;about Vue.js&lt;/a&gt;, discussing &lt;a href="https://a.l3x.in/2018/07/25/lambda-api-custom-domain-tutorial.html"&gt;about&lt;/a&gt; &lt;em&gt;Serverless&lt;/em&gt; &lt;a href="https://a.l3x.in/2017/02/28/lastversion.html"&gt;every&lt;/a&gt; chance &lt;a href="https://a.l3x.in/2017/05/30/sslnotifyme.html"&gt;I get&lt;/a&gt; and subscribing to (&lt;a href="https://www.youtube.com/user/DesignCourse/"&gt;excellent&lt;/a&gt;) web design courses and &lt;a href="https://css-tricks.com/"&gt;CSS&lt;/a&gt; RSS feeds.&lt;/p&gt;

&lt;p&gt;Wait... WHAT?&lt;/p&gt;

&lt;p&gt;It looks suspiciously like the classical mid-career/mid-life crisis, right? Just a feral impulse to an easy change: let's get rid of the past with one clean stroke, reject altogether the idea of self-managing backend systems for good. &lt;code&gt;ssh&lt;/code&gt; is dead. Get over it.&lt;/p&gt;

&lt;p&gt;I'm afraid the truth is far from that, even though my opinion is that today there's generally no real need to orchestrate any fleet of servers running SSHd... unless you are deep in &lt;em&gt;legacy territory&lt;/em&gt;, a (ti)&lt;em&gt;tty&lt;/em&gt; fetishist or some kind of (cloud) service provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  SSH is dead. Long live SSH
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Humans are meant to work and sweat to earn a living. That's the lesson.&lt;/p&gt;

&lt;p&gt;― &lt;em&gt;Cowboy Bebop, Jupiter Jazz (part 1)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm not saying server administration is dead. Heck no, long life to Linux &amp;amp; Co. and all the community around it, and most importantly, my sincere compliments to all the &lt;em&gt;System / DevOps / SRE Engineers&lt;/em&gt; out there for keeping the Internet alive and kicking, 24/7 365 days per year.&lt;/p&gt;

&lt;p&gt;Taming the operational complexities in this era of &lt;em&gt;Global Scales&lt;/em&gt;, &lt;em&gt;Spot Instances&lt;/em&gt;, &lt;em&gt;Chaos Monkeys&lt;/em&gt; and &lt;em&gt;Multi Regions&lt;/em&gt; is a &lt;strong&gt;very difficult task&lt;/strong&gt; and one that most of the time gets only little appreciation (to the point of &lt;a href="https://sysadminday.com/"&gt;publicly asking for some&lt;/a&gt; every new year) because it's not &lt;em&gt;on the front&lt;/em&gt; of IT.&lt;/p&gt;

&lt;p&gt;A smarter move might just be to let those brilliant and hard working professionals take care of &lt;em&gt;that&lt;/em&gt;, those who have been running the (invisible to the end user) Internet infrastructure for the last decades or so and today most probably work at AWS, Digital Ocean, Netlify and so on. They are the best suited to take care of &lt;em&gt;that&lt;/em&gt; anyway, so everybody else can focus on tasks more directly tight to the actual business at hand.&lt;/p&gt;

&lt;p&gt;I'm not saying that you absolutely must avoid &lt;em&gt;old style&lt;/em&gt; operations involving servers either. I'm just stating that if you're starting a new software project today, adopting one or more servers (because that's what it is going to be: you'll need to nurture them for as long as your business runs) &lt;strong&gt;might not be the best way to go&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Simply put, today system administration is most probably a skill you don't want to internalize unless it's a core pillar of what you're building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free lunch? Maybe just a cheaper one
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The task of a craftsman is not to generate meaning, but rather to cultivate in himself the skill of discerning the meanings that are already there.&lt;/p&gt;

&lt;p&gt;― Cal Newport, &lt;em&gt;Deep Work&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unless you operate in some specific and/or constrained context (or if you're operating at &lt;em&gt;Netflix&lt;/em&gt; kind of scale and it might economically make sense to &lt;em&gt;self host&lt;/em&gt;), today there are simpler and more effective methodologies that help you build and deliver highly performant (and massively scalable) software products without the need to own nor manage any (virtual or otherwise) server.&lt;/p&gt;

&lt;p&gt;Even if you are a strong &lt;em&gt;old school&lt;/em&gt; advocate or worry about &lt;em&gt;cloud lock-in&lt;/em&gt;, you should at least agree with me that the benefits (auto-management, built-in autoscaling, reduced attack surface, cheaper bills, faster &lt;em&gt;Time To Market&lt;/em&gt;... to name just a few) are too many and too appealing for discarding those new trends as &lt;em&gt;just a buzz&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Serverless Computing, Jamstack, Static Site Generators&lt;/em&gt;... today all of those technologies are mature and my opinion is &lt;a href="https://www.wsj.com/articles/nasdaq-cio-sees-serverless-computing-as-a-2020-tech-trend-11579213416"&gt;not mine alone&lt;/a&gt;: many successful enterprises are adopting them as we speak and at an increasingly faster pace. &lt;a href="https://read.acloud.guru/journey-to-serverless-d3256d91af16"&gt;A few&lt;/a&gt; of &lt;a href="https://winterwindsoftware.com/real-world-serverless-case-studies/"&gt;those&lt;/a&gt; are &lt;a href="https://thenewstack.io/irobot-confronts-challenges-running-serverless-scale/"&gt;kind&lt;/a&gt; enough to openly &lt;a href="https://www.youtube.com/watch?v=mtZvA7ARepM"&gt;discuss&lt;/a&gt; about them so we can make better informed decisions when evaluating adoption of those technologies and adapt them to our context.&lt;/p&gt;

&lt;p&gt;To embrace the new a small but sensible paradigm shift is needed and letting go of old familiar practices always comes with some friction. In my opinion though there's nothing truly dreadful as you might soon realize by yourself once you break the ice with those (only &lt;a href="https://dev.to/shortdiv/what-makes-a-site-jamstack-ib1"&gt;relatively new&lt;/a&gt;) concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sir &lt;em&gt;Chaos&lt;/em&gt;, please have a sit
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;I must not fear. Fear is the mind-killer.&lt;/p&gt;

&lt;p&gt;― Frank Herbert, &lt;em&gt;Dune&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We've been using web services to build our applications for long now. We even survived the global Micro-services invasion that, even though they massively increased the complexity burden on &lt;em&gt;operations&lt;/em&gt;, forced us to think about our applications more in terms of &lt;em&gt;fault tolerant, self-contained and independent blocks&lt;/em&gt;, orchestrated by asynchronous communication delivered via remote APIs.&lt;/p&gt;

&lt;p&gt;I'm oversimplifying here, but I guess you get the point. AWS Lambda today is only 5 years old, Fargate 2, but if you (try to) keep up with the tech landscape it truly feels like those kind technologies have already become a commodity. In time even &lt;em&gt;BOFH&lt;/em&gt; kind of sysadmins are learning how not to worry about it... and even love it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chaos, unpredictability, is not to be feared.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maybe it's even your best ally, the one who keeps your edge sharp: &lt;em&gt;listen to relevant events only, embrace eventual consistency, expect peers unavailability&lt;/em&gt;. These are the new kinds of mantras.&lt;/p&gt;

&lt;p&gt;At this point should be obvious that personally I bet the new &lt;em&gt;dudes and dudettes&lt;/em&gt; are here to stay, so if you have read so far and you're not thinking yet I'm completely delusional, try to follow me a little longer while I &lt;em&gt;look forward&lt;/em&gt; and &lt;em&gt;keep walking&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  join(past, present, future)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Who are you?” he would ask her every day.&lt;/p&gt;

&lt;p&gt;“No one”, she would answer.&lt;/p&gt;

&lt;p&gt;― George R.R. Martin, &lt;em&gt;A Feast for Crows&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't feel there's any discontinuity, neither practical nor ethical (professionally speaking), between &lt;em&gt;being in operations&lt;/em&gt; and developing applications following &lt;a href="https://jamstack.org/best-practices/"&gt;Jamstack principles&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At this point we've been sharing tools and practices between &lt;em&gt;devs&lt;/em&gt; and &lt;em&gt;ops&lt;/em&gt; for so long that they're overlapping to the point of fading almost completely. Take this example of a common daily workflow of one of your colleagues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;pick an assigned ticket&lt;/li&gt;
&lt;li&gt;analyze its content in search of meaning&lt;/li&gt;
&lt;li&gt;identify the relevant repositories and relative file(s)&lt;/li&gt;
&lt;li&gt;fire up an IDE to edit these files(s)&lt;/li&gt;
&lt;li&gt;implement the requested functionality in a feature branch enforcing best practices&lt;/li&gt;
&lt;li&gt;add (unit/E2E/whatever makes sense) tests (move this one up if you prefer)&lt;/li&gt;
&lt;li&gt;iterate until all tests are green&lt;/li&gt;
&lt;li&gt;push the patch and wait for code review&lt;/li&gt;
&lt;li&gt;wait for thumbs up, go back to #2 until done&lt;/li&gt;
&lt;li&gt;squash/rebase/push the new commit to &lt;code&gt;master&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;wait for the CI/CD system to run tests, build artifacts implementing the requested change and deploy them to production&lt;/li&gt;
&lt;li&gt;keep an eye on telemetry and alerts to ensure everything is fine&lt;/li&gt;
&lt;li&gt;set the ticket state to &lt;em&gt;closed&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It might be a bit contrived but it shouldn't be &lt;em&gt;that far&lt;/em&gt; from the real thing. Can you tell his/her job title from this list alone? &lt;em&gt;That&lt;/em&gt; old fence line has become quite thin, hasn't it?&lt;/p&gt;

&lt;p&gt;And it might be a very good thing too. We're left with &lt;em&gt;only&lt;/em&gt; engineers working on some task at hand regardless of age or gender or (previous) job titles, everything redundant like labeled hats dropped for good.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open &lt;em&gt;Change&lt;/em&gt;, last revision
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Law 25: Re-Create yourself&lt;/p&gt;

&lt;p&gt;― Robert Greene, &lt;em&gt;The 48 Laws of Power&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With that said, whether you consider yourself a &lt;em&gt;dev&lt;/em&gt; or an &lt;em&gt;ops&lt;/em&gt; or just a &lt;em&gt;wildcard&lt;/em&gt; engineer, it seems to me that the foundations of software development and operations did not change much from my first days as a junior.&lt;/p&gt;

&lt;p&gt;Performance, maintainability, availability, data consistency and confidentiality... all those needs are still with us and relatively unchanged. Some might be new (I might be wrong but for example &lt;em&gt;accessibility&lt;/em&gt; didn't feel &lt;em&gt;that&lt;/em&gt; important back then), most of them are not.&lt;/p&gt;

&lt;p&gt;To sum up with a one-liner, the need to &lt;strong&gt;get the work done&lt;/strong&gt; is with us today as much as it was in the past, virtually untouched. Stakeholders needed to &lt;em&gt;get it done&lt;/em&gt; then as much as they need it today, they never cared much how fancy were the names filling up the tech stack as long as they were functional in hitting the mark.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Matter
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;No fear. No distractions. The ability to let that which does not matter truly slide.&lt;/p&gt;

&lt;p&gt;― Chuck Palahniuk, &lt;em&gt;Fight Club&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm sure some of you grins at the idea of a Perl runtime serving traffic via Apache and CGI today. Try to explain why that's funny to &lt;em&gt;amazon.com&lt;/em&gt; or &lt;em&gt;booking.com&lt;/em&gt; shareholders, to name just a couple.&lt;/p&gt;

&lt;p&gt;Trust me, I don't reject old and battle tested &lt;em&gt;boring&lt;/em&gt; technologies at all, quite the opposite.&lt;/p&gt;

&lt;p&gt;I work with the terminal every day and I couldn't get anything done without the repeated use of &lt;code&gt;grep&lt;/code&gt; and Unix pipes. I still love to code in &lt;em&gt;Python&lt;/em&gt; even if I feel like everybody else is jumping on &lt;em&gt;Go&lt;/em&gt; or &lt;em&gt;Rust&lt;/em&gt; or... put your favorite modern language here.&lt;/p&gt;

&lt;p&gt;I just say, let's use the best tools at hand today for the job avoiding all that's unnecessary.&lt;/p&gt;

&lt;p&gt;Sure, the tech landscape evolved in the meanwhile: tools, frameworks, methodologies got new names and restyling. Practices to better manage complexity evolved along with them but nothing ever truly got rid of it completely.&lt;/p&gt;

&lt;p&gt;For each new approach that gave us a step forward in simplicity, soon we got more pressure in the opposite direction, pushed by needs like bigger scales of operations, more integrations, more &lt;em&gt;delightful&lt;/em&gt; user experiences, smaller &lt;em&gt;time to market&lt;/em&gt;... and the list goes on.&lt;/p&gt;

&lt;p&gt;Once the two opposed forces have stabilized, new balances emerge making room for bigger amount of business value, delivered faster and to more (hopefully &lt;em&gt;delighted&lt;/em&gt;) users.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQEmpathy injection
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Anyone got time for a &lt;a href="https://devsum-archive.s3.amazonaws.com/dont-git-blame-me.jpeg"&gt;code review&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;― anonymous colleague&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most importantly, the need for &lt;em&gt;empathy&lt;/em&gt; is also unchanged. We might write in &lt;a href="https://abstrusegoose.com/249"&gt;computer languages&lt;/a&gt;, but the ones who read and write in those languages all the time are, as far as I know, still &lt;em&gt;humans&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Empathy for the end user AND for the developer both should drive our efforts, and if you're reading this article there's a good chance you are one of the latter... so do yourself a favor today and remember to be empathetic to &lt;em&gt;your future self&lt;/em&gt; too.&lt;/p&gt;

&lt;p&gt;I don't refer to the kind of empathy you might feel for someone recently &lt;a href="https://devsum-archive.s3.amazonaws.com/friendzoned-meme.jpg"&gt;&lt;em&gt;friend-zoned&lt;/em&gt;&lt;/a&gt; but the kind you feel when you see a colleague struggling with something unnecessarily complex.&lt;/p&gt;

&lt;p&gt;Care for user experience is an act of empathy. Why should only the &lt;em&gt;end&lt;/em&gt; user benefit from it? Software engineers are software users too and have the same right for a good experience.&lt;/p&gt;

&lt;p&gt;That's at least what I always tried to keep in mind while trying to write maintainable (Configuration as) Code automation. (If you are a former colleague, trust me when I say I truly hope you didn't have to swear too much when dealing with what I left behind).&lt;/p&gt;

&lt;p&gt;I believe that maximizing value delivery to the business should not be the only deciding factor when choosing a technology stack. As the developer tools and practices evolve and become more ergonomic, it's reasonable to wish for adopting &lt;em&gt;the new&lt;/em&gt; and leave the old where it belongs.&lt;/p&gt;

&lt;p&gt;In the end people make the product so is reasonable to believe happier developers lead to better products.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Left&lt;/em&gt; in the middle. &lt;em&gt;Down&lt;/em&gt; to Earth. &lt;em&gt;Up&lt;/em&gt; to something?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;There are no two words in the English language more harmful than &lt;em&gt;good job&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;― &lt;em&gt;Whiplash&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So here I find myself today: unable to define &lt;em&gt;home&lt;/em&gt; more precisely than &lt;em&gt;planet Earth&lt;/em&gt;, neither to define a job title any better than an all encompassing &lt;a href="https://cv.l3x.in/"&gt;Full Stack Software Engineer&lt;/a&gt;, in an evolving polyamory relationship with browser dev tools, APIs, FaaS runtimes and &lt;em&gt;container&lt;/em&gt; orchestrators.&lt;/p&gt;

&lt;p&gt;In balanced tension between &lt;em&gt;back&lt;/em&gt; and &lt;em&gt;front&lt;/em&gt;, the old and the new, the &lt;em&gt;boring&lt;/em&gt; and the &lt;em&gt;bleeding edge&lt;/em&gt;, the server and the (perceived) absence of it.&lt;/p&gt;

&lt;p&gt;In time, who knows, I might finally meet &lt;em&gt;that&lt;/em&gt; redhead, the kind who keeps telling "&lt;em&gt;you know nothing!&lt;/em&gt;" and follow her in yet unexplored wild(lings) territories, leaving all my (techie) legacy behind for good.&lt;/p&gt;

&lt;p&gt;Until then I guess I'll keep telling myself the same thing I've been telling myself for the latest 20 or so years: there's never been a better time to &lt;em&gt;be in IT&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://a.l3x.in/"&gt;Alexander Fortin's tech blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;I link here some of the media content cited above. The list represents part of what I have read/watched/listened to (either very recently or long ago but still stuck in my head) that inspired me and I find worth recommending:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the world of &lt;strong&gt;A Song of Ice and Fire&lt;/strong&gt; - &lt;a href="https://awoiaf.westeros.org/index.php/A_Song_of_Ice_and_Fire"&gt;https://awoiaf.westeros.org/index.php/A_Song_of_Ice_and_Fire&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memento&lt;/strong&gt;, the movie - &lt;a href="https://www.imdb.com/title/tt0209144/"&gt;https://www.imdb.com/title/tt0209144/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Road&lt;/strong&gt;, by Cormac McCarthy - &lt;a href="https://www.goodreads.com/book/show/6288.The_Road"&gt;https://www.goodreads.com/book/show/6288.The_Road&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conscious: A Brief Guide to the Fundamental Mystery of the Mind&lt;/strong&gt;, by Anita Harris - &lt;a href="https://www.goodreads.com/book/show/41571759-conscious"&gt;https://www.goodreads.com/book/show/41571759-conscious&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;an immortal classic: &lt;strong&gt;Time Enough for Love&lt;/strong&gt;, by Robert A. Heinlein - &lt;a href="https://www.goodreads.com/book/show/353.Time_Enough_for_Love"&gt;https://www.goodreads.com/book/show/353.Time_Enough_for_Love&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Culture&lt;/strong&gt; saga, by Iain Banks - &lt;a href="https://www.goodreads.com/series/49118-culture"&gt;https://www.goodreads.com/series/49118-culture&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mr Robot&lt;/strong&gt;, the tv show - &lt;a href="https://www.imdb.com/title/tt4158110/"&gt;https://www.imdb.com/title/tt4158110/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subjective Cosmology&lt;/strong&gt; series, by Greg Egan - &lt;a href="https://www.goodreads.com/series/160715-subjective-cosmology"&gt;https://www.goodreads.com/series/160715-subjective-cosmology&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cowboy Bebop&lt;/strong&gt;, in my opinion still the best &lt;em&gt;anime&lt;/em&gt; ever made - &lt;a href="https://www.imdb.com/title/tt0213338/"&gt;https://www.imdb.com/title/tt0213338/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;the excellent &lt;strong&gt;Deep Work: Rules for Focused Success in a Distracted World&lt;/strong&gt;, by Cal Newport - &lt;a href="https://www.goodreads.com/book/show/25744928-deep-work"&gt;https://www.goodreads.com/book/show/25744928-deep-work&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;another immortal favorite of mine: &lt;strong&gt;Dune&lt;/strong&gt; saga, by Frank Herbert - &lt;a href="https://www.goodreads.com/series/45935-dune"&gt;https://www.goodreads.com/series/45935-dune&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 48 Laws of Power&lt;/strong&gt;, by Robert Greene - &lt;a href="https://www.goodreads.com/book/show/1303.The_48_Laws_of_Power"&gt;https://www.goodreads.com/book/show/1303.The_48_Laws_of_Power&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;a modern classic: &lt;strong&gt;Fight Club&lt;/strong&gt;, by Chuck Palahniuk - &lt;a href="https://www.goodreads.com/book/show/36236124-fight-club"&gt;https://www.goodreads.com/book/show/36236124-fight-club&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whiplash&lt;/strong&gt;, the movie - &lt;a href="https://www.imdb.com/title/tt2582802/"&gt;https://www.imdb.com/title/tt2582802/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>serverless</category>
      <category>jamstack</category>
      <category>devops</category>
      <category>sre</category>
    </item>
  </channel>
</rss>
